Skip to content
Snippets Groups Projects
Commit 70645473 authored by Loïc Correnson's avatar Loïc Correnson Committed by Michele Alberti
Browse files

[dome] JSON encoders

parent bfa340f2
No related branches found
No related tags found
No related merge requests found
......@@ -40,7 +40,7 @@ export function stringify(js: any) {
}
/**
Export JSON (or any data) as a string with indented content.
Export JSON (or any data) as a string with indentation.
*/
export function pretty(js: any) {
return JSON.stringify(js, undefined, 2);
......@@ -189,6 +189,38 @@ export function jTry<A>(fn: Loose<A>, defaultValue?: A): Loose<A> {
};
}
/**
Converts maps to dictionnaries.
*/
export function jMap<A>(fn: Loose<A>): Safe<Map<string, A>> {
return (js: json) => {
const m = new Map<string, A>();
if (js !== null && typeof js === 'object' && !Array.isArray(js)) {
for (let k of Object.keys(js)) {
const v = fn(js[k]);
if (v !== undefined) m.set(k, v);
}
}
return m;
};
}
/**
Converts dictionnaries to maps.
*/
export function eMap<A>(fn: Encoder<A>): Encoder<Map<string, undefined | A>> {
return m => {
const js: json = {};
m.forEach((v, k) => {
if (v !== undefined) {
const u = fn(v);
if (u !== undefined) js[k] = u;
}
});
return js;
};
}
/**
Apply the decoder on each item of a JSON array, or return `[]` otherwize.
Can be also applied on a _loose_ decoder, but you will get
......@@ -215,6 +247,22 @@ export function jList<A>(fn: Loose<A>): Safe<A[]> {
};
}
/**
Exports all non-undefined elements.
*/
export function eList<A>(fn: Encoder<A>): Encoder<(A | undefined)[]> {
return m => {
const js: json[] = [];
m.forEach(v => {
if (v !== undefined) {
const u = fn(v);
if (u !== undefined) js.push(u);
}
});
return js;
};
}
/** Apply a pair of decoders to JSON pairs, or return `undefined`. */
export function jPair<A, B>(
fa: Safe<A>,
......@@ -289,8 +337,13 @@ export function jObject<A>(fp: Props<A>): Loose<A> {
const buffer = {} as A;
for (var k of Object.keys(fp)) {
const fn = fp[k as keyof A];
const fv = fn(js[k]);
buffer[k as keyof A] = fv;
if (fn !== undefined) {
const fj = js[k];
if (fj !== undefined) {
const fv = fn(fj);
if (fv !== undefined) buffer[k as keyof A] = fv;
}
}
}
return buffer;
}
......@@ -298,6 +351,35 @@ export function jObject<A>(fp: Props<A>): Loose<A> {
};
}
/**
Encoders for each property of object type `A`.
*/
export type EProps<A> = {
[P in keyof A]?: Encoder<A[P]>;
}
/**
Encode an object given the provided encoders by fields.
The exported JSON object has only original
fields with some specified encoder.
*/
export function eObject<A>(fp: EProps<A>): Encoder<A> {
return (m: A) => {
const js: json = {};
for (var k of Object.keys(fp)) {
const fn = fp[k as keyof A];
if (fn !== undefined) {
const fv = m[k as keyof A];
if (fv !== undefined) {
const r = fn(fv);
if (r !== undefined) js[k] = r;
}
}
}
return js;
}
}
/** Type of dictionaries. */
export type dict<A> = { [key: string]: A };
......@@ -310,12 +392,33 @@ export function jDictionary<A>(fn: Loose<A>): Safe<dict<A>> {
const buffer: dict<A> = {};
if (js !== null && typeof js === 'object' && !Array.isArray(js)) {
for (var k of Object.keys(js)) {
const fv = fn(js[k]);
if (fv) buffer[k] = fv;
const fd = js[k];
if (fd !== undefined) {
const fv = fn(fd);
if (fv !== undefined) buffer[k] = fv;
}
}
}
return buffer;
};
}
/**
Encode a dictionary into JSON, dicarding all inconsistent entries.
If the dictionary contains no valid entry, still returns `{}`.
*/
export function eDictionary<A>(fn: Encoder<A>): Encoder<dict<A>> {
return (d: dict<A>) => {
const js: json = {};
for (var k of Object.keys(d)) {
const fv = d[k];
if (fv !== undefined) {
const fv = fn(d[k]);
if (fv !== undefined) js[k] = fv;
}
}
return js;
};
}
// --------------------------------------------------------------------------
......@@ -119,10 +119,12 @@ abstract class Settings<A> {
private readonly role: symbol;
protected readonly decoder: JSON.Safe<A>;
protected readonly encoder: JSON.Encoder<A>;
constructor(role: string, decoder: JSON.Safe<A>) {
constructor(role: string, decoder: JSON.Safe<A>, encoder: JSON.Encoder<A>) {
this.role = Symbol(role);
this.decoder = decoder;
this.encoder = encoder;
}
validateKey(k?: string): string | undefined {
......@@ -150,9 +152,13 @@ abstract class Settings<A> {
return this.decoder(key ? this.loadData(key) : undefined)
}
saveValue(key: string, value: A) {
this.saveData(key, this.encoder(value));
}
}
export function useSettings<A extends JSON.json>(
export function useSettings<A>(
S: Settings<A>,
dataKey?: string,
): [A, (update: A) => void] {
......@@ -172,7 +178,7 @@ export function useSettings<A extends JSON.json>(
const updateValue = React.useCallback((update: A) => {
if (!isEqual(value, update)) {
setValue(update);
if (theKey) S.saveData(theKey, update);
if (theKey) S.saveValue(theKey, update);
}
}, [S, theKey]);
......@@ -180,10 +186,10 @@ export function useSettings<A extends JSON.json>(
}
export class WindowSettings<A> extends Settings<A> {
export class WindowSettingsData<A> extends Settings<A> {
constructor(role: string, decoder: JSON.Safe<A>) {
super(role, decoder);
constructor(role: string, decoder: JSON.Safe<A>, encoder: JSON.Encoder<A>) {
super(role, decoder, encoder);
}
event = Symbol('dome.settings');
......@@ -192,10 +198,10 @@ export class WindowSettings<A> extends Settings<A> {
}
export class GlobalSettings<A> extends Settings<A> {
export class GlobalSettingsData<A> extends Settings<A> {
constructor(role: string, decoder: JSON.Safe<A>) {
super(role, decoder);
constructor(role: string, decoder: JSON.Safe<A>, encoder: JSON.Encoder<A>) {
super(role, decoder, encoder);
}
event = Symbol('dome.globals');
......@@ -204,4 +210,20 @@ export class GlobalSettings<A> extends Settings<A> {
}
export class WindowSettings<A extends JSON.json> extends WindowSettingsData<A> {
constructor(role: string, decoder: JSON.Safe<A>) {
super(role, decoder, JSON.identity);
}
}
export class GlobalSettings<A extends JSON.json> extends GlobalSettingsData<A> {
constructor(role: string, decoder: JSON.Safe<A>) {
super(role, decoder, JSON.identity);
}
}
// --------------------------------------------------------------------------
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment