config-util: Use zod for type-safe validation.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg
2021-07-20 18:48:28 -07:00
parent 494e716dfe
commit aaa83da0f8
3 changed files with 60 additions and 43 deletions

View File

@@ -0,0 +1,38 @@
import * as z from "zod";
export const dndSettingsSchemata = {
showNotification: z.boolean(),
silent: z.boolean(),
flashTaskbarOnMessage: z.boolean(),
};
export const configSchemata = {
...dndSettingsSchemata,
appLanguage: z.string().nullable(),
autoHideMenubar: z.boolean(),
autoUpdate: z.boolean(),
badgeOption: z.boolean(),
betaUpdate: z.boolean(),
customCSS: z.string().or(z.literal(false)).nullable(),
dnd: z.boolean(),
dndPreviousSettings: z.object(dndSettingsSchemata).partial(),
dockBouncing: z.boolean(),
downloadsPath: z.string(),
enableSpellchecker: z.boolean(),
errorReporting: z.boolean(),
lastActiveTab: z.number(),
promptDownload: z.boolean(),
proxyBypass: z.string(),
proxyPAC: z.string(),
proxyRules: z.string(),
quitOnClose: z.boolean(),
showSidebar: z.boolean(),
spellcheckerLanguages: z.string().array().nullable(),
startAtLogin: z.boolean(),
startMinimized: z.boolean(),
systemProxyRules: z.string(),
trayIcon: z.boolean(),
useManualProxy: z.boolean(),
useProxy: z.boolean(),
useSystemProxy: z.boolean(),
};

View File

@@ -3,40 +3,16 @@ import fs from "fs";
import path from "path";
import {JsonDB} from "node-json-db";
import {DataError} from "node-json-db/dist/lib/Errors";
import type * as z from "zod";
import type {DNDSettings} from "./dnd-util";
import {configSchemata} from "./config-schemata";
import * as EnterpriseUtil from "./enterprise-util";
import Logger from "./logger-util";
export interface Config extends DNDSettings {
appLanguage: string | null;
autoHideMenubar: boolean;
autoUpdate: boolean;
badgeOption: boolean;
betaUpdate: boolean;
customCSS: string | false | null;
dnd: boolean;
dndPreviousSettings: Partial<DNDSettings>;
dockBouncing: boolean;
downloadsPath: string;
enableSpellchecker: boolean;
errorReporting: boolean;
lastActiveTab: number;
promptDownload: boolean;
proxyBypass: string;
proxyPAC: string;
proxyRules: string;
quitOnClose: boolean;
showSidebar: boolean;
spellcheckerLanguages: string[] | null;
startAtLogin: boolean;
startMinimized: boolean;
systemProxyRules: string;
trayIcon: boolean;
useManualProxy: boolean;
useProxy: boolean;
useSystemProxy: boolean;
}
export type Config = {
[Key in keyof typeof configSchemata]: z.output<typeof configSchemata[Key]>;
};
/* To make the util runnable in both main and renderer process */
const {app, dialog} = process.type === "renderer" ? electron.remote : electron;
@@ -52,7 +28,7 @@ reloadDB();
export function getConfigItem<Key extends keyof Config>(
key: Key,
defaultValue: Config[Key],
): Config[Key] {
): z.output<typeof configSchemata[Key]> {
try {
db.reload();
} catch (error: unknown) {
@@ -60,13 +36,13 @@ export function getConfigItem<Key extends keyof Config>(
logger.error(error);
}
const value = db.getData("/")[key];
if (value === undefined) {
try {
return configSchemata[key].parse(db.getObject<unknown>(`/${key}`));
} catch (error: unknown) {
if (!(error instanceof DataError)) throw error;
setConfigItem(key, defaultValue);
return defaultValue;
}
return value;
}
// This function returns whether a key exists in the configuration file (settings.json)
@@ -78,8 +54,7 @@ export function isConfigItemExists(key: string): boolean {
logger.error(error);
}
const value = db.getData("/")[key];
return value !== undefined;
return db.exists(`/${key}`);
}
export function setConfigItem<Key extends keyof Config>(
@@ -92,6 +67,7 @@ export function setConfigItem<Key extends keyof Config>(
return;
}
configSchemata[key].parse(value);
db.push(`/${key}`, value, true);
db.save();
}

View File

@@ -1,12 +1,15 @@
import type * as z from "zod";
import type {dndSettingsSchemata} from "./config-schemata";
import * as ConfigUtil from "./config-util";
type SettingName = keyof DNDSettings;
export type DNDSettings = {
[Key in keyof typeof dndSettingsSchemata]: z.output<
typeof dndSettingsSchemata[Key]
>;
};
export interface DNDSettings {
showNotification: boolean;
silent: boolean;
flashTaskbarOnMessage: boolean;
}
type SettingName = keyof DNDSettings;
interface Toggle {
dnd: boolean;