Skip to content
Snippets Groups Projects
Commit cdbc0dcd authored by Loïc Correnson's avatar Loïc Correnson
Browse files

[ivette] fix in decoders

parent b766201b
No related branches found
No related tags found
No related merge requests found
...@@ -168,7 +168,7 @@ let rec makeDecoder ~safe ?self ~names fmt js = ...@@ -168,7 +168,7 @@ let rec makeDecoder ~safe ?self ~names fmt js =
| Jself -> jcall names fmt (Pkg.Derived.decode ~safe (getSelf self)) | Jself -> jcall names fmt (Pkg.Derived.decode ~safe (getSelf self))
| Joption js -> makeLoose fmt js | Joption js -> makeLoose fmt js
| Jdict(kd,js) -> | Jdict(kd,js) ->
Format.fprintf fmt "@[<hov 2>Json.jDict('#%s',@,%a)@]" kd makeLoose js Format.fprintf fmt "@[<hov 2>Json.jDictionary('#%s',@,%a)@]" kd makeLoose js
| Jlist js -> | Jlist js ->
Format.fprintf fmt "@[<hov 2>Json.jList(%a)@]" makeLoose js Format.fprintf fmt "@[<hov 2>Json.jList(%a)@]" makeLoose js
| Jarray js -> | Jarray js ->
......
...@@ -71,6 +71,12 @@ export function identity<A>(v: A): A { return v; }; ...@@ -71,6 +71,12 @@ export function identity<A>(v: A): A { return v; };
// --- Primitives // --- Primitives
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
/** Always returns `undefined` on any input. */
export const jNull: Safe<undefined> = (_: json) => undefined;
/** Identity. */
export const jAny: Safe<json> = (js: json) => js;
/** Primitive JSON number or `undefined`. */ /** Primitive JSON number or `undefined`. */
export const jNumber: Loose<number> = (js: json) => ( export const jNumber: Loose<number> = (js: json) => (
typeof js === 'number' && !Number.isNaN(js) ? js : undefined typeof js === 'number' && !Number.isNaN(js) ? js : undefined
...@@ -106,13 +112,21 @@ export const jString: Loose<string> = (js: json) => ( ...@@ -106,13 +112,21 @@ export const jString: Loose<string> = (js: json) => (
typeof js === 'string' ? js : undefined typeof js === 'string' ? js : undefined
); );
/**
Lookup tags in a dictionary.
Can be used directly for enum types, eg. `jEnum(myEnumType)`.
*/
export function jEnum<A>(d: { [tag: string]: A }): Loose<A> {
return (v: json) => typeof v === 'string' ? d[v] : undefined;
}
/** /**
One of the enumerated _constants_ or `undefined`. One of the enumerated _constants_ or `undefined`.
The typechecker will prevent you from listing values that are not in The typechecker will prevent you from listing values that are not in
type `A`. However, it will not protected you type `A`. However, it will not protected you
from missings constants in `A`. from missings constants in `A`.
*/ */
export function jEnum<A>(...values: ((string | number) & A)[]): Loose<A> { export function jTags<A>(...values: ((string | number) & A)[]): Loose<A> {
var m = new Map<string | number, A>(); var m = new Map<string | number, A>();
values.forEach(v => m.set(v, v)); values.forEach(v => m.set(v, v));
return (v: json) => (typeof v === 'string' ? m.get(v) : undefined); return (v: json) => (typeof v === 'string' ? m.get(v) : undefined);
...@@ -131,18 +145,10 @@ export function jDefault<A>( ...@@ -131,18 +145,10 @@ export function jDefault<A>(
} }
/** /**
Force returning `undefined` or a default value for `undefined` JSON input. Force returning `undefined` or a default value for `undefined` _or_ `null` JSON input.
Typically usefull to leverage an existing `Safe<A>` decoder. Typically usefull to leverage an existing `Safe<A>` decoder.
*/ */
export function jOption<A>(fn: Safe<A>, defaultValue?: A): Loose<A> { export function jOption<A>(fn: Safe<A>, defaultValue?: A): Loose<A> {
return (js: json) => (js === undefined ? defaultValue : fn(js));
}
/**
Force returning `undefined` or a default value for `undefined` _or_ `null`
JSON input. Typically usefull to leverage an existing `Safe<A>` decoder.
*/
export function jNull<A>(fn: Safe<A>, defaultValue?: A): Loose<A> {
return (js: json) => (js === undefined || js === null ? defaultValue : fn(js)); return (js: json) => (js === undefined || js === null ? defaultValue : fn(js));
} }
...@@ -349,6 +355,19 @@ export function jObject<A>(fp: Props<A>): Loose<A> { ...@@ -349,6 +355,19 @@ export function jObject<A>(fp: Props<A>): Loose<A> {
}; };
} }
/**
Returns the first decoder result that is not undefined.
*/
export function jUnion<A>(...cases: Loose<A>[]): Loose<A> {
return (js: json) => {
for (var fn of cases) {
const fv = fn(js);
if (fv !== undefined) return fv;
}
return undefined;
};
}
/** /**
Encoders for each property of object type `A`. Encoders for each property of object type `A`.
*/ */
...@@ -378,22 +397,64 @@ export function eObject<A>(fp: EProps<A>): Encoder<A> { ...@@ -378,22 +397,64 @@ export function eObject<A>(fp: EProps<A>): Encoder<A> {
} }
} }
/** Type of dictionaries. */ // Intentionnaly internal and only declared
export type dict<A> = { [key: string]: A }; declare const tag: unique symbol;
/** Phantom type. */
export type phantom<K, A> = A & { tag: K };
export function forge<K, A>(_tag: K, data: A): phantom<K, A> {
return data as any;
}
/** String key with kind. Can be used as a `string` but shall be created with [forge]. */
export type key<K> = phantom<K, string>;
/** Number index with kind. Can be used as a `number` but shall be created with [forge]. */
export type index<K> = phantom<K, number>;
/** Decoder for `key<K>` strings. */
export function jKey<K>(kd: K): Loose<key<K>> {
return (js: json) => typeof js === 'string' ? forge(kd, js) : undefined;
}
/** Decoder for `index<K>` numbers. */
export function jIndex<K>(kd: K): Loose<index<K>> {
return (js: json) => typeof js === 'number' ? forge(kd, js) : undefined;
}
/** Dictionaries with « typed » keys. */
export type dict<K, A> = phantom<K, { [key: string]: A }>
/** Lookup into dictionary.
Better than a direct access to `d[k]` for undefined values. */
export function lookup<K, A>(d: dict<K, A>, k: key<K>): A | undefined {
return d[k];
}
/** Empty dictionary. */
export function empty<K, A>(kd: K): dict<K, A> {
return forge(kd, {} as any);
}
/** Dictionary extension. */
export function index<K, A>(d: dict<K, A>, key: key<K>, value: A) {
d[key] = value;
}
/** /**
Decode a JSON dictionary, dicarding all inconsistent entries. Decode a JSON dictionary, dicarding all inconsistent entries.
If the JSON contains no valid entry, still returns `{}`. If the JSON contains no valid entry, still returns `{}`.
*/ */
export function jDictionary<A>(fn: Loose<A>): Safe<dict<A>> { export function jDictionary<K, A>(kd: K, fn: Loose<A>): Safe<dict<K, A>> {
return (js: json) => { return (js: json) => {
const buffer: dict<A> = {}; const buffer: dict<K, A> = empty(kd);
if (js !== null && typeof js === 'object' && !Array.isArray(js)) { if (js !== null && typeof js === 'object' && !Array.isArray(js)) {
for (var k of Object.keys(js)) { for (var key of Object.keys(js)) {
const fd = js[k]; const fd = js[key];
if (fd !== undefined) { if (fd !== undefined) {
const fv = fn(fd); const fv = fn(fd);
if (fv !== undefined) buffer[k] = fv; if (fv !== undefined) index(buffer, forge(kd, key), fv);
} }
} }
} }
...@@ -405,14 +466,14 @@ export function jDictionary<A>(fn: Loose<A>): Safe<dict<A>> { ...@@ -405,14 +466,14 @@ export function jDictionary<A>(fn: Loose<A>): Safe<dict<A>> {
Encode a dictionary into JSON, dicarding all inconsistent entries. Encode a dictionary into JSON, dicarding all inconsistent entries.
If the dictionary contains no valid entry, still returns `{}`. If the dictionary contains no valid entry, still returns `{}`.
*/ */
export function eDictionary<A>(fn: Encoder<A>): Encoder<dict<A>> { export function eDictionary<K, A>(fn: Encoder<A>): Encoder<dict<K, A>> {
return (d: dict<A>) => { return (d: dict<K, A>) => {
const js: json = {}; const js: json = {};
for (var k of Object.keys(d)) { for (var k of Object.keys(d)) {
const fv = d[k]; const fv = d[k];
if (fv !== undefined) { if (fv !== undefined) {
const fv = fn(d[k]); const fr = fn(fv);
if (fv !== undefined) js[k] = fv; if (fr !== undefined) js[k] = fr;
} }
} }
return js; return js;
......
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