diff --git a/src/main/accounts/store.ts b/src/main/accounts/store.ts index 94ba221..696d405 100644 --- a/src/main/accounts/store.ts +++ b/src/main/accounts/store.ts @@ -1,7 +1,7 @@ import { app } from "electron"; import { join } from "node:path"; -import { copyFileSync, existsSync, readFileSync, rmSync } from "node:fs"; -import { writeFileAtomicSync } from "../lib/atomicFile"; +import { copyFileSync, existsSync, rmSync } from "node:fs"; +import { jsonFile } from "../lib/jsonFile"; import type { Account, AccountsState } from "../../shared/types/auth"; const dir = () => join(app.getPath("userData"), "sessions"); @@ -15,17 +15,9 @@ interface Registry { accounts: Account[]; } -function read(): Registry { - try { - return JSON.parse(readFileSync(registryPath(), "utf8")) as Registry; - } catch { - return { active: null, accounts: [] }; - } -} - -function write(reg: Registry): void { - writeFileAtomicSync(registryPath(), JSON.stringify(reg, null, 2)); -} +const registry = jsonFile(registryPath, () => ({ active: null, accounts: [] })); +const read = registry.read; +const write = registry.write; export function listAccounts(): AccountsState { const reg = read(); diff --git a/src/main/config/appConfig.ts b/src/main/config/appConfig.ts index 882215c..560d02e 100644 --- a/src/main/config/appConfig.ts +++ b/src/main/config/appConfig.ts @@ -1,7 +1,7 @@ import { app } from "electron"; import { join } from "node:path"; -import { existsSync, readFileSync } from "node:fs"; -import { writeFileAtomicSync } from "../lib/atomicFile"; +import { existsSync } from "node:fs"; +import { jsonFile } from "../lib/jsonFile"; import type { AppConfig, PreferredRegion } from "../../shared/types/appConfig"; import { detectedGamePath } from "../game/steam"; @@ -10,12 +10,15 @@ const path = () => join(app.getPath("userData"), "app-config.json"); type StoredConfig = Omit; const DEFAULTS: StoredConfig = { gamePath: null, preferredRegion: "auto" }; -function readStored(): StoredConfig { - try { - return { ...DEFAULTS, ...(JSON.parse(readFileSync(path(), "utf8")) as Partial) }; - } catch { - return { ...DEFAULTS }; - } +const configFile = jsonFile( + path, + () => ({ ...DEFAULTS }), + (raw) => ({ ...DEFAULTS, ...(raw as Partial) }), +); +const readStored = configFile.read; + +function writeStored(next: StoredConfig): void { + configFile.write(next); } function withDerived(stored: StoredConfig): AppConfig { @@ -32,7 +35,7 @@ export function preferredRegion(): PreferredRegion { export function setPreferredRegion(region: PreferredRegion): AppConfig { const next: StoredConfig = { ...readStored(), preferredRegion: region }; - writeFileAtomicSync(path(), JSON.stringify(next, null, 2)); + writeStored(next); return withDerived(next); } @@ -46,6 +49,6 @@ export function setGamePath(gamePath: string | null): AppConfig { throw new Error("That path doesn't exist on disk."); } const next: StoredConfig = { ...readStored(), gamePath: trimmed }; - writeFileAtomicSync(path(), JSON.stringify(next, null, 2)); + writeStored(next); return withDerived(next); } diff --git a/src/main/enhancements/service.ts b/src/main/enhancements/service.ts index d6e772f..ef6ef09 100644 --- a/src/main/enhancements/service.ts +++ b/src/main/enhancements/service.ts @@ -1,7 +1,6 @@ import { app } from "electron"; import { join } from "node:path"; -import { readFileSync } from "node:fs"; -import { writeFileAtomicSync } from "../lib/atomicFile"; +import { jsonFile } from "../lib/jsonFile"; import { logger } from "../debug/logger"; import type { EnhancementId, @@ -17,17 +16,9 @@ const storePath = () => join(app.getPath("userData"), "enhancements.json"); type Prefs = Partial>; -function readPrefs(): Prefs { - try { - return JSON.parse(readFileSync(storePath(), "utf8")) as Prefs; - } catch { - return {}; - } -} - -function writePrefs(prefs: Prefs): void { - writeFileAtomicSync(storePath(), JSON.stringify(prefs, null, 2)); -} +const prefsFile = jsonFile(storePath, () => ({})); +const readPrefs = prefsFile.read; +const writePrefs = prefsFile.write; function screenshotState(): EnhancementState { const s = screenshot.status(); diff --git a/src/main/lib/jsonFile.ts b/src/main/lib/jsonFile.ts new file mode 100644 index 0000000..9bd00fa --- /dev/null +++ b/src/main/lib/jsonFile.ts @@ -0,0 +1,21 @@ +import { readFileSync } from "node:fs"; +import { writeFileAtomicSync } from "./atomicFile"; + +export function jsonFile( + path: () => string, + fallback: () => T, + parse: (raw: unknown) => T = (raw) => raw as T, +) { + return { + read(): T { + try { + return parse(JSON.parse(readFileSync(path(), "utf8"))); + } catch { + return fallback(); + } + }, + write(value: T): void { + writeFileAtomicSync(path(), JSON.stringify(value, null, 2)); + }, + }; +} diff --git a/src/main/windowState.ts b/src/main/windowState.ts index d71ccb5..7221753 100644 --- a/src/main/windowState.ts +++ b/src/main/windowState.ts @@ -1,19 +1,13 @@ import { app, screen, type BrowserWindow, type Rectangle } from "electron"; import { join } from "node:path"; -import { readFileSync } from "node:fs"; -import { writeFileAtomicSync } from "./lib/atomicFile"; +import { jsonFile } from "./lib/jsonFile"; type Bounds = Rectangle & { maximized: boolean }; const path = () => join(app.getPath("userData"), "window-state.json"); -function read(): Partial | null { - try { - return JSON.parse(readFileSync(path(), "utf8")) as Partial; - } catch { - return null; - } -} +const stateFile = jsonFile | null>(path, () => null); +const read = stateFile.read; function onScreen(b: Rectangle): boolean { return screen.getAllDisplays().some((d) => { @@ -47,10 +41,7 @@ export function trackBounds(win: BrowserWindow): void { const save = (): void => { if (win.isDestroyed()) return; - writeFileAtomicSync( - path(), - JSON.stringify({ ...normalBounds, maximized: win.isMaximized() }, null, 2), - ); + stateFile.write({ ...normalBounds, maximized: win.isMaximized() }); }; const remember = (): void => {