Skip to content

Commit

Permalink
Merge pull request #3 from newclarityex/main
Browse files Browse the repository at this point in the history
Added dot notation along with appropriate types
  • Loading branch information
harshkhandeparkar authored Nov 15, 2022
2 parents a4e9c08 + af1e3d9 commit 4b1a795
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 43 deletions.
50 changes: 21 additions & 29 deletions src/settings-manager/settings-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { ConfigOptions } from '../config/config';
import { STATUS } from '../fs/ensure-settings-file';

import { getSettings, saveSettings } from '../fs/load-save';
import { has, get, set } from '../settings/getter-setter';
import { get, set } from '../settings/getter-setter';
import { getDotNotation, setDotNotation } from '../utils/dot-notation';
import type { Path, PathValue } from '../types/dot-notation';

export class SettingsManager<SettingsSchema extends {} = any> {
/**
Expand Down Expand Up @@ -54,20 +56,19 @@ export class SettingsManager<SettingsSchema extends {} = any> {
* Checks whether a key exists in the settings cache.
* @param key The key for the setting
*/
hasCache(key: string | number | symbol): boolean {
return key in this.settings;
hasCache<K extends Path<SettingsSchema>>(key: K): boolean {
return getDotNotation(this.settings, key) !== undefined;
}

/**
* Sets the value of a setting from the cache.
* @param key The key for the setting
* @returns The value of the setting
*/
getCache<K extends keyof SettingsSchema = keyof SettingsSchema>(key: K): SettingsSchema[K] {
if (this.hasCache(key)) {
return this.settings[key];
}
else throw 'Error: key does not exist';
getCache<K extends Path<SettingsSchema>>(key: K): PathValue<SettingsSchema, K> {
if (!this.hasCache(key)) throw 'Error: key does not exist';

return getDotNotation<SettingsSchema, K>(this.settings, key);
}

/**
Expand All @@ -76,35 +77,26 @@ export class SettingsManager<SettingsSchema extends {} = any> {
* @param value The new value for the setting
* @returns The entire settings object
*/
setCache<K extends keyof SettingsSchema = keyof SettingsSchema>(key: K, value: SettingsSchema[K]): SettingsSchema[K] {
this.settings[key] = value;
setCache<K extends Path<SettingsSchema>, V extends PathValue<SettingsSchema, K>>(key: K, value: V): V {
if (!this.hasCache(key)) throw 'Error: key does not exist';

return value;
}
setDotNotation<SettingsSchema, K>(this.settings, key, value);

/**
* Checks whether a key exists in the settings directly from the storage.
* @param key The key for the setting
*/
async has(key: string | number | symbol): Promise<boolean> {
return await has<SettingsSchema>(key, this.options);
return value;
}

/**
* Gets the value of a setting directly from the storage. Also updates cache.
* @param key The key for the setting
* @returns The value of the setting
*/
async get<K extends keyof SettingsSchema = keyof SettingsSchema>(key: K): Promise<SettingsSchema[K]> {
if (await this.has(key)) {
const value = await get<SettingsSchema, K>(key, this.options);
async get<K extends Path<SettingsSchema>>(key: K): Promise<PathValue<SettingsSchema, K>> {
const value = await get<SettingsSchema, K>(key, this.options);

// to also update cache
this.setCache(key, value);
// to also update cache
this.setCache(key, value);

return value;
}
else throw 'Error: key does not exist';
return value;
}

/**
Expand All @@ -113,11 +105,11 @@ export class SettingsManager<SettingsSchema extends {} = any> {
* @param value The new value for the setting
* @returns The entire settings object
*/
async set<K extends keyof SettingsSchema = keyof SettingsSchema>(key: K, value: SettingsSchema[K]): Promise<SettingsSchema> {
async set<K extends Path<SettingsSchema>, V extends PathValue<SettingsSchema, K>>(key: K, value: V): Promise<SettingsSchema> {
// to also update cache
this.setCache(key, value);

return await set<SettingsSchema, K>(key, value, this.options);
return await set<SettingsSchema, K, V>(key, value, this.options);
}

/**
Expand All @@ -129,4 +121,4 @@ export class SettingsManager<SettingsSchema extends {} = any> {

return this.settings;
}
}
}
32 changes: 18 additions & 14 deletions src/settings/getter-setter.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
import { ConfigOptions } from '../config/config';
import { getSettings, saveSettings } from '../fs/load-save';
import { getDotNotation, setDotNotation } from '../utils/dot-notation';
import type { Path, PathValue } from '../types/dot-notation';

/**
* Checks whether a key exists in the settings.
* @param key The key for the setting
*/
export async function has
<SettingsSchema extends {} = any>
(key: string | number | symbol, options: ConfigOptions = {}): Promise<boolean>
{
return (key in (await getSettings<SettingsSchema>(options)).settings);
<SettingsSchema, K extends Path<SettingsSchema>>
(key: K, options: ConfigOptions = {}): Promise<boolean> {
const settings = (await getSettings<SettingsSchema>(options)).settings;
const value = getDotNotation(settings, key);
return value !== undefined;
}

/**
* Get the value of a particular setting.
* @param key The key for the setting
* @returns The value
* @returns The value of the setting
*/
export async function get
<SettingsSchema extends {} = any, K extends keyof SettingsSchema = keyof SettingsSchema>
(key: K, options: ConfigOptions = {}): Promise<SettingsSchema[K]>
{
if (await has<SettingsSchema>(key)) return (await getSettings<SettingsSchema>(options)).settings[key];
else throw 'Error: key does not exist.'
<SettingsSchema, K extends Path<SettingsSchema>>
(key: K, options: ConfigOptions = {}): Promise<PathValue<SettingsSchema, K>> {
if (!await has<SettingsSchema, K>(key)) throw 'Error: key does not exist';
const settings = (await getSettings<SettingsSchema>(options)).settings;
return getDotNotation<SettingsSchema, K>(settings, key);
}

/**
Expand All @@ -32,12 +35,13 @@ export async function get
* @returns The entire settings object
*/
export async function set
<SettingsSchema extends {} = any, K extends keyof SettingsSchema = keyof SettingsSchema>
(key: K, value: SettingsSchema[K], options: ConfigOptions = {}): Promise<SettingsSchema>
{
<SettingsSchema, K extends Path<SettingsSchema>, V extends PathValue<SettingsSchema, K>>
(key: K, value: V, options: ConfigOptions = {}): Promise<SettingsSchema> {
if (!await has<SettingsSchema, K>(key)) throw 'Error: key does not exist';

const settings = await getSettings<SettingsSchema>(options);
setDotNotation<SettingsSchema, K>(settings.settings, key, value);

settings.settings[key] = value;
await saveSettings<SettingsSchema>(settings.settings, settings.path, options);

return settings.settings;
Expand Down
21 changes: 21 additions & 0 deletions src/types/dot-notation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
type PathImpl<T, K extends keyof T> =
K extends string
? T[K] extends Record<string, any>
? T[K] extends ArrayLike<any>
? K | `${K}.${PathImpl<T[K], Exclude<keyof T[K], keyof any[]>>}`
: K | `${K}.${PathImpl<T[K], keyof T[K]>}`
: K
: never;

export type Path<T> = PathImpl<T, keyof T> | keyof T;

export type PathValue<T, P extends Path<T>> =
P extends `${infer K}.${infer Rest}`
? K extends keyof T
? Rest extends Path<T[K]>
? PathValue<T[K], Rest>
: never
: never
: P extends keyof T
? T[P]
: never;
48 changes: 48 additions & 0 deletions src/utils/dot-notation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type { Path, PathValue } from '../types/dot-notation';

/**
* @internal
*/
export function getDotNotation<SettingsSchema, K extends Path<SettingsSchema>>(obj: SettingsSchema, path: K): PathValue<SettingsSchema, K> | undefined {
if (typeof path !== 'string') throw 'Error: path must be a string';

const keys = path.split('.');

let current: any = obj;
for (let i = 0; i < keys.length; i++) {
const key = keys[i];

if (current[key] === undefined) {
return undefined;
} else {
current = current[key];
}
}

return current;
}

/**
* @internal
*/
export function setDotNotation<SettingsSchema, K extends Path<SettingsSchema>>(obj: SettingsSchema, path: K, value: PathValue<SettingsSchema, K>): PathValue<SettingsSchema, K> | undefined {
if (typeof path !== 'string') throw 'Error: path must be a string';

const keys = path.split('.');

let current = obj;
for (let i = 0; i < keys.length - 1; i++) {
const key = keys[i];

if (current[key] === undefined) {
return undefined;
}

current = current[key];
}

if (current[keys[keys.length - 1]] === undefined) return undefined;

current[keys[keys.length - 1]] = value;
return value;
}

0 comments on commit 4b1a795

Please sign in to comment.