From 0408065886b150e3814b0c96e6601e2c541ed5ea Mon Sep 17 00:00:00 2001 From: Diego Magdaleno Date: Sat, 22 May 2021 10:47:11 -0500 Subject: Config: Refactor config to be be in separate classes --- src/util/Config.ts | 150 +++++++++++++++++++++++++++++------------------------ 1 file changed, 83 insertions(+), 67 deletions(-) (limited to 'src') diff --git a/src/util/Config.ts b/src/util/Config.ts index 9b2b9600..1e29508b 100644 --- a/src/util/Config.ts +++ b/src/util/Config.ts @@ -368,55 +368,53 @@ const createPlainObject = (): T => { type Serialize = (value: T) => string; type Deserialize = (text: string) => T; - -class Config = Record> implements Iterable<[keyof T, T[keyof T]]> { - readonly path: string; - readonly #validator?: ValidateFunction; - readonly #defaultOptions: Partial = {}; - - constructor() { - - const ajv = new Ajv(); - - ajvFormats(ajv); - - this.#validator = ajv.compile(schema); - - const base = envPaths('fosscord', {suffix: ""}).config; - - this.path = path.resolve(base, 'api.json'); +function getConfigPath(): string { + const configEnvPath = envPaths('fosscord', {suffix: ""}).config; + const configPath = path.resolve(configEnvPath, 'api.json'); + return configPath +} - const fileStore = this.store; - const store = Object.assign(createPlainObject(), fileStore); - this._validate(store); - - try { - assert.deepStrictEqual(fileStore, store); - } catch { - this.store = store; +class Store = Record> implements Iterable<[keyof T, T[keyof T]]>{ + readonly path: string; + readonly validator: ValidateFunction; + constructor(path: string, validator: ValidateFunction) { + this.validator = validator; + if (fs.existsSync(path)) { + this.path = path + } else { + this._ensureDirectory() } } - private _has(key: Key | string): boolean { - return dotProp.has(this.store, key as string); + private _ensureDirectory(): void { + fs.mkdirSync(path.dirname(this.path), {recursive: true}) } - private _validate(data: T | unknown): void { - if (!this.#validator) { - return; - } - - const valid = this.#validator(data); - if (valid || !this.#validator.errors) { + protected _validate(data: T | unknown): void { + const valid = this.validator(data); + if (valid || !this.validator.errors) { return; } - const errors = this.#validator.errors.map(({ instancePath, message = '' }) => `\`${instancePath.slice(1)}\` ${message}`); + const errors = this.validator.errors.map(({ instancePath, message = '' }) => `\`${instancePath.slice(1)}\` ${message}`); throw new Error('The config schema was violated!: ' + errors.join('; ')); } - get store(): T { + private _write(value: T): void { + let data: string | Buffer = this._serialize(value); + + try { + atomically.writeFileSync(this.path, data); + } catch (error) { + throw error; + } + } + + private readonly _serialize: Serialize = value => JSON.stringify(value, undefined, '\t'); + private readonly _deserialize: Deserialize = value => JSON.parse(value); + + public get store(): T { try { const data = fs.readFileSync(this.path).toString(); const deserializedData = this._deserialize(data); @@ -425,41 +423,67 @@ class Config = Record> implements } catch (error) { if (error.code == 'ENOENT') { this._ensureDirectory(); - return createPlainObject(); - + return Object.create(null); } throw error; } } - private _ensureDirectory(): void { - fs.mkdirSync(path.dirname(this.path), { recursive: true }) - } - - set store(value: T) { + public set store(value: T) { this._validate(value); this._write(value); } - private readonly _deserialize: Deserialize = value => JSON.parse(value); - private readonly _serialize: Serialize = value => JSON.stringify(value, undefined, '\t') + *[Symbol.iterator](): IterableIterator<[keyof T, T[keyof T]]>{ + for (const [key, value] of Object.entries(this.store)) { + yield [key, value] + } + } +} + +interface Options { + path: string; + schemaValidator: ValidateFunction; +} + +class Config = Record> extends Store implements Iterable<[keyof T, T[keyof T]]> { + readonly path: string; + + constructor(options: Readonly> = {}) { + super(options.path!, options.schemaValidator!); + + + const fileStore = this.store; + const store = Object.assign(createPlainObject(), fileStore); + this._validate(store); + + try { + assert.deepStrictEqual(fileStore, store); + } catch { + this.store = store; + } + } - get(key: Key): T[Key]; - get(key: Key, defaultValue: Required[Key]): Required[Key]; - get(key: Exclude, defaultValue?: Value): Value; - get(key: string, defaultValue?: unknown): unknown { + public get(key: Key): T[Key]; + public get(key: Key, defaultValue: Required[Key]): Required[Key]; + public get(key: Exclude, defaultValue?: Value): Value; + public get(key: string, defaultValue?: unknown): unknown { return this._get(key, defaultValue); } + private _has(key: Key | string): boolean { + return dotProp.has(this.store, key as string); + } + public getAll(): DefaultOptions { return this.store as unknown as DefaultOptions } - private _get(key: Key): T[Key] | undefined; - private _get(key: Key, defaultValue: Default): T[Key] | Default; - private _get(key: Key | string, defaultValue?: Default): Default | undefined { + _get(key: Key): T[Key] | undefined; + _get(key: Key, defaultValue: Default): T[Key] | Default; + _get(key: Key | string, defaultValue?: Default): Default | undefined { if (!this._has(key)) { throw new Error("Tried to acess a non existant property in the config"); } @@ -467,26 +491,18 @@ class Config = Record> implements return dotProp.get(this.store, key as string, defaultValue as T[Key]); } - *[Symbol.iterator](): IterableIterator<[keyof T, T[keyof T]]> { + * [Symbol.iterator](): IterableIterator<[keyof T, T[keyof T]]> { for (const [key, value] of Object.entries(this.store)) { yield [key, value]; } } - private _write(value: T): void { - let data: string | Buffer = this._serialize(value); +} - try { - atomically.writeFileSync(this.path, data); - } catch (error) { - if (error.code == 'EXDEV') { - fs.writeFileSync(this.path, data) - return - } +const ajv = new Ajv(); +const validator = ajv.compile(schema); - throw error; - } - } -} +const configPath = getConfigPath() +console.log(configPath) -export const apiConfig = new Config(); \ No newline at end of file +export const apiConfig = new Config({path: configPath, schemaValidator: validator}); \ No newline at end of file -- cgit 1.5.1