Commit 4c6c4a99 by Vladislav Lagunov

- Переименования в decoder/index.ts

- Рефакторинг config-reader/index.ts
parent 974e1d48
import * as t from '../decoder'; import * as t from '../decoder';
import { camelCase } from 'lodash'; import { camelCase } from 'lodash';
import { absurd } from '../types'; import { absurd } from '../types';
import { WithDefault, RecordDecoder, Decoder } from '../decoder';
import { Either } from '../either';
/** /**
...@@ -33,12 +35,12 @@ export function concat(...sources: ConfigSource[]): ConfigSource { ...@@ -33,12 +35,12 @@ export function concat(...sources: ConfigSource[]): ConfigSource {
} }
export function validate<A>(config: ConfigSource, decoder: t.RecordDecoder<A>) { export function validate<A>(config: ConfigSource, decoder: RecordDecoder<A>) {
return decoder.validate(merge(decoder, config)); return decoder.validate(merge(decoder, config));
} }
export function merge<A>(decoder: t.RecordDecoder<A>, ...srcs: ConfigSource[]): object { export function merge<A>(decoder: RecordDecoder<A>, ...srcs: ConfigSource[]): object {
const source = srcs.length === 1 ? srcs[0] : concat(...srcs); const source = srcs.length === 1 ? srcs[0] : concat(...srcs);
if (source.tag === 'Cli') { if (source.tag === 'Cli') {
const value: Record<string, any> = {}; const value: Record<string, any> = {};
...@@ -47,8 +49,8 @@ export function merge<A>(decoder: t.RecordDecoder<A>, ...srcs: ConfigSource[]): ...@@ -47,8 +49,8 @@ export function merge<A>(decoder: t.RecordDecoder<A>, ...srcs: ConfigSource[]):
const rest = source.args[i].substr(prefixLen); const rest = source.args[i].substr(prefixLen);
const [k, v] = rest.indexOf('=') === -1 ? [rest, source.args[++i] || ''] : source.args[i].split('='); const [k, v] = rest.indexOf('=') === -1 ? [rest, source.args[++i] || ''] : source.args[i].split('=');
// Проверка того что это известно // Проверка того что это известно
if (!decoder.description.hasOwnProperty(k)) continue; if (!decoder._description.hasOwnProperty(k)) continue;
value[k] = decoderFromString(decoder.description[k], v); value[k] = fromString(decoder._description[k], v);
} }
return value; return value;
} }
...@@ -57,8 +59,8 @@ export function merge<A>(decoder: t.RecordDecoder<A>, ...srcs: ConfigSource[]): ...@@ -57,8 +59,8 @@ export function merge<A>(decoder: t.RecordDecoder<A>, ...srcs: ConfigSource[]):
const substring = source.value[0] === '?' ? source.value.substr(1) : source.value; const substring = source.value[0] === '?' ? source.value.substr(1) : source.value;
const value = substring.split('&').map(x => x.split('=')).reduce((acc, [k, v]: any) => { const value = substring.split('&').map(x => x.split('=')).reduce((acc, [k, v]: any) => {
const key = source.transformKey(decodeURIComponent(k)); const key = source.transformKey(decodeURIComponent(k));
if (!decoder.description.hasOwnProperty(key)) return acc; if (!decoder._description.hasOwnProperty(key)) return acc;
acc[key] = decoderFromString(decoder.description[key], decodeURIComponent(v)); acc[key] = fromString(decoder._description[key], decodeURIComponent(v));
return acc; return acc;
}, {}); }, {});
return value; return value;
...@@ -77,16 +79,21 @@ export function merge<A>(decoder: t.RecordDecoder<A>, ...srcs: ConfigSource[]): ...@@ -77,16 +79,21 @@ export function merge<A>(decoder: t.RecordDecoder<A>, ...srcs: ConfigSource[]):
} }
export function decoderFromString(decoder: t.Decoder<any>, value: string): any { export function fromString(d: Decoder<any>, value: string): unknown {
if (decoder instanceof t.CustomDecoder) { if (d instanceof t.ArrayDecoder) {
throw new Error('[config-reader] CustomDecoder is not supported'); return value.split(', ').map(x => fromString(d._decoder, x));
} }
if (decoder instanceof t.ArrayDecoder) { // if (d instanceof t.Tuple) {
return value.split(', ').map(x => decoderFromString(decoder.decoder, x)); // return value.split(', ').map((x, idx) => fromString(d._tuple[idx], x));
} // }
// if (d instanceof t.Literals) {
// // TODO: проверять значение исходя из типа rep._literals
// return value;
// }
if (decoder instanceof t.DictionaryDecoder) { if (d instanceof t.Dict) {
try { try {
return JSON.parse(value); return JSON.parse(value);
} catch(e) { } catch(e) {
...@@ -94,61 +101,166 @@ export function decoderFromString(decoder: t.Decoder<any>, value: string): any { ...@@ -94,61 +101,166 @@ export function decoderFromString(decoder: t.Decoder<any>, value: string): any {
} }
} }
if (decoder instanceof t.RecordDecoder) { if (d instanceof t.RecordDecoder) {
try { try {
return JSON.parse(value); return JSON.parse(value);
} catch(e) { } catch(e) {
return value; return value;
} }
} }
if (d instanceof t.Primitive) {
if (d._type === 'string') return value;
if (d._type === 'boolean') return !(value === 'false' || value === '0');
if (d._type === 'undefined') return undefined;
if (d._type === 'null') return null;
if (d._type === 'any') return value;
if (d._type === 'nat') return Number(value);
if (d._type === 'int') return Number(value);
if (d._type === 'float') return Number(value);
return absurd(d._type);
}
if (d instanceof t.OneOf) {
throw new Error('[config-reader] OneOf is not supported');
}
if (decoder instanceof t.AtDecoder) { if (d instanceof t.Discriminate) {
try { try {
return JSON.parse(value); return JSON.parse(value);
} catch(e) { } catch(e) {
return value; return value;
} }
} }
if (decoder instanceof t.PrimitiveDecoder) {
if (decoder.primitive === 'string') return value;
if (decoder.primitive === 'boolean') return !(value === 'false' || value === '0');
if (decoder.primitive === 'undefined') return undefined;
if (decoder.primitive === 'null') return null;
if (decoder.primitive === 'any') return value;
if (decoder.primitive === 'nat') return Number(value);
if (decoder.primitive === 'int') return Number(value);
if (decoder.primitive === 'float') return Number(value);
return absurd(decoder.primitive);
}
if (decoder instanceof t.PureDecoder) { if (d instanceof t.Pure) {
return value; return value;
} }
if (decoder instanceof t.ChainDecoder) { if (d instanceof t.ToDecoder) {
throw new Error('[config-reader] ChainDecoder is not supported'); if (d instanceof WithDefault) {
return fromString(d._decoder, value);
}
return fromString(d.toDecoder(), value);
} }
if (decoder instanceof t.OneOfDecoder) { if (d instanceof t.Custom) {
throw new Error('[config-reader] OneOfDecoder is not supported'); throw new Error('[config-reader]: CustomDecoder is not supported');
} }
if (decoder instanceof t.DiscriminatedDecoder) { if (d instanceof t.AtDecoder) {
try { throw new Error('[config-reader]: AtDecoder is not supported');
return JSON.parse(value); }
} catch(e) {
return value; if (d instanceof t.Chain) {
} throw new Error('[config-reader]: ChainDecoder is not supported');
} }
if (decoder instanceof t.HasDecoder) { return absurd(d);
if (decoder instanceof t.WithDefaultDecoder) { }
return decoderFromString(decoder.decoder, value);
export class ConfigDescription<A> extends t.ToDecoder<A> {
constructor(
readonly _decoder: Decoder<A>,
readonly _description: string,
) { super(); }
toDecoder() {
return this._decoder;
}
}
// Информация о cli параметрe
export type InfoItem = { type: string; default?: unknown; description?: string };
export function configInfo(decoder: RecordDecoder<any>, prefix=''): Record<string, InfoItem> {
return Object.keys(decoder._description).reduce((acc, k) => (acc[k] = go(decoder._description[k]), acc), {});
function go(d: Decoder<any>): InfoItem {
if (d instanceof t.ArrayDecoder) {
const { type } = go(d._decoder);
return { type: 'Array<' + type + '>' };
}
if (d instanceof t.Dict) {
const { type } = go(d._decoder);
return { type: 'Record<string, ' + type + '>' };
}
if (d instanceof t.RecordDecoder) {
// TODO: печать полей
return { type: 'Record<string, unknown>' };
}
if (d instanceof t.Primitive) {
if (d._type === 'string') return { type: 'string' };
if (d._type === 'boolean') return { type: 'boolean' };
if (d._type === 'undefined') return { type: 'undefined' };
if (d._type === 'null') return { type: 'null' };
if (d._type === 'any') return { type: 'any' };
if (d._type === 'nat') return { type: 'number' };
if (d._type === 'int') return { type: 'number' };
if (d._type === 'float') return { type: 'number' };
return absurd(d._type);
}
if (d instanceof t.OneOf) {
return { type: d._alternatives.map(go).map(x => x.type).join('|') };
} }
return decoderFromString(decoder.toDecoder(), value); if (d instanceof t.Discriminate) {
// TODO: печать дискриминаторов
return { type: Object.keys(d._alternatives).map(k => go(d._alternatives[k]).type).join('|') };
}
if (d instanceof t.Pure) {
return { type: JSON.stringify(d._value) };
}
if (d instanceof t.ToDecoder) {
if (d instanceof WithDefault) {
const info = go(d._decoder);
info.default = d._default;
return info;
}
const info = go(d.toDecoder());
if (d instanceof ConfigDescription) {
info.description = d._description;
}
return info;
}
if (d instanceof t.Custom) {
throw new Error('[config-reader]: CustomDecoder is not supported');
}
if (d instanceof t.AtDecoder) {
throw new Error('[config-reader]: AtDecoder is not supported');
}
if (d instanceof t.Chain) {
throw new Error('[config-reader]: ChainDecoder is not supported');
}
return absurd(d);
} }
}
return absurd(decoder); declare module "../decoder" {
interface DecoderBase<A> {
withDescription(description: string): Decoder<A>;
validateConfigs(...sources: ConfigSource[]): Either<Problem, A>;
}
} }
t.DecoderBase.prototype.withDescription = function(description) {
return new ConfigDescription(this as any, description);
};
t.DecoderBase.prototype.validateConfigs = function(...sources) {
return this.validate(merge(this as any, ...sources));
};
...@@ -9,17 +9,17 @@ import { Expr, absurd } from '../types'; ...@@ -9,17 +9,17 @@ import { Expr, absurd } from '../types';
/** ADT */ /** ADT */
export type Decoder<A> = export type Decoder<A> =
| CustomDecoder<A> | Custom<A>
| ArrayDecoder<A> | ArrayDecoder<A>
| DictionaryDecoder<A> | Dict<A>
| RecordDecoder<A> | RecordDecoder<A>
| AtDecoder<A> | AtDecoder<A>
| PrimitiveDecoder<A> | Primitive<A>
| PureDecoder<A> | Pure<A>
| ChainDecoder<A> | Chain<A>
| OneOfDecoder<A> | OneOf<A>
| DiscriminatedDecoder<A> | Discriminate<A>
| HasDecoder<A> | ToDecoder<A>
/** /**
...@@ -89,193 +89,141 @@ export class DecoderBase<A> { ...@@ -89,193 +89,141 @@ export class DecoderBase<A> {
map<B>(f: (a: A) => B): Decoder<B> { map<B>(f: (a: A) => B): Decoder<B> {
const self = this as any as Decoder<A>; const self = this as any as Decoder<A>;
return new ChainDecoder(self, x => x.fold(fail, x => of(f(x)))); return new Chain(self, x => x.fold(fail, x => of(f(x))));
} }
mapTo<B>(value: B): Decoder<B> { mapTo<B>(value: B): Decoder<B> {
const self = this as any as Decoder<A>; const self = this as any as Decoder<A>;
return new ChainDecoder(self, x => x.fold(fail, () => of(value))); return new Chain(self, x => x.fold(fail, () => of(value)));
} }
chain<B>(f: (a: A) => Decoder<B>): Decoder<B> { chain<B>(f: (a: A) => Decoder<B>): Decoder<B> {
const self = this as any as Decoder<A>; const self = this as any as Decoder<A>;
return new ChainDecoder(self, x => x.fold(fail, f)); return new Chain(self, x => x.fold(fail, f));
} }
chainTo<B>(value: Decoder<B>): Decoder<B> { chainTo<B>(value: Decoder<B>): Decoder<B> {
const self = this as any as Decoder<A>; const self = this as any as Decoder<A>;
return new ChainDecoder(self, x => x.fold(fail, () => value)); return new Chain(self, x => x.fold(fail, () => value));
} }
mapProblem(f: (a: Problem) => Problem): Decoder<A> { mapProblem(f: (a: Problem) => Problem): Decoder<A> {
const self = this as any as Decoder<A>; const self = this as any as Decoder<A>;
return new ChainDecoder(self, x => x.fold(f, x => x)); return new Chain(self, x => x.fold(f, x => x));
} }
mapProblemTo(value: Problem): Decoder<A> { mapProblemTo(value: Problem): Decoder<A> {
const self = this as any as Decoder<A>; const self = this as any as Decoder<A>;
return new ChainDecoder(self, x => x.fold(() => value, x => x)); return new Chain(self, x => x.fold(() => value, x => x));
} }
withDefault(defValue: A): Decoder<A>; withDefault(defValue: A): Decoder<A>;
withDefault<B extends Expr>(defValue: B): Decoder<A|B>; withDefault<B extends Expr>(defValue: B): Decoder<A|B>;
withDefault<B extends Expr>(defValue: B): Decoder<A|B> { withDefault<B extends Expr>(defValue: B): Decoder<A|B> {
return new WithDefaultDecoder(this as any, defValue); return new WithDefault(this as any, defValue);
} }
refine(pred: (a: A) => boolean): Decoder<A> { refine(pred: (a: A) => boolean): Decoder<A> {
const self = this as any as Decoder<A>; const self = this as any as Decoder<A>;
return new ChainDecoder(self, ethr => { return new Chain(self, ethr => {
switch (ethr.tag) { switch (ethr.tag) {
case 'Left': return new PureDecoder(ethr); case 'Left': return new Pure(ethr);
case 'Right': return pred(ethr.value) ? new PureDecoder(ethr) : fail('refinement failed'); case 'Right': return pred(ethr.value) ? new Pure(ethr) : fail('refinement failed');
} }
}); });
} }
} }
export class Custom<A> extends DecoderBase<A> {
/**
* Декодер с произвольной функцией для валидации
*/
export class CustomDecoder<A> extends DecoderBase<A> {
readonly tag: 'CustomDecoder' = 'CustomDecoder';
constructor( constructor(
readonly name: string, readonly _name: string,
readonly validateCustom: (val: any) => Either<Problem|string, A>, readonly _validate: (val: any) => Either<Problem|string, A>,
) { super(); } ) { super(); }
} }
/**
* Декодер массивов
*/
export class ArrayDecoder<A> extends DecoderBase<A> { export class ArrayDecoder<A> extends DecoderBase<A> {
readonly tag: 'ArrayDecoder' = 'ArrayDecoder';
constructor( constructor(
readonly decoder: Decoder<any>, readonly _decoder: Decoder<any>,
) { super(); } ) { super(); }
} }
export class Dict<A> extends DecoderBase<A> {
/**
* Декодер словарей (или хешей, те объектов с произвольным кол-вом ключей)
*/
export class DictionaryDecoder<A> extends DecoderBase<A> {
readonly tag: 'DictionaryDecoder' = 'DictionaryDecoder';
constructor( constructor(
readonly decoder: Decoder<any>, readonly _decoder: Decoder<any>,
) { super(); } ) { super(); }
} }
/**
* Декодер записей (объекты с фиксированным количеством полей)
*/
export class RecordDecoder<A> extends DecoderBase<A> { export class RecordDecoder<A> extends DecoderBase<A> {
readonly tag: 'RecordDecoder' = 'RecordDecoder';
constructor( constructor(
readonly description: Record<string, Decoder<any>>, readonly _description: Record<string, Decoder<any>>,
) { super(); } ) { super(); }
}
extend<F extends { [K: string]: Decoder<any> }>(fields: F): RecordDecoder<A & { [K in keyof F]: F[K]['_A'] }> {
return new RecordDecoder({ ...this._description, ...fields as any });
}
}
/**
* Декодер поля с указанным индексом
*/
export class AtDecoder<A> extends DecoderBase<A> { export class AtDecoder<A> extends DecoderBase<A> {
readonly tag: 'AtDecoder' = 'AtDecoder'; readonly tag: 'AtDecoder' = 'AtDecoder';
constructor( constructor(
readonly path: Array<string|number>, readonly _path: Array<string|number>,
readonly decoder: Decoder<A>, readonly _decoder: Decoder<A>,
) { super(); } ) { super(); }
} }
export class Primitive<A> extends DecoderBase<A> {
/**
* Декодер для примитовов
*/
export class PrimitiveDecoder<A> extends DecoderBase<A> {
readonly tag: 'PrimitiveDecoder' = 'PrimitiveDecoder'; readonly tag: 'PrimitiveDecoder' = 'PrimitiveDecoder';
constructor( constructor(
readonly primitive: 'null'|'undefined'|'string'|'boolean'|'any'|'nat'|'int'|'float' readonly _type: 'null'|'undefined'|'string'|'boolean'|'any'|'nat'|'int'|'float'
) { super(); } ) { super(); }
} }
export class Pure<A> extends DecoderBase<A> {
/**
* Тривиальный декодер
*/
export class PureDecoder<A> extends DecoderBase<A> {
constructor( constructor(
readonly value: Either<Problem|string, A>, readonly _value: Either<Problem|string, A>,
) { super(); } ) { super(); }
} }
export class Chain<A> extends DecoderBase<A> {
/**
* Монадный комбинатор
*/
export class ChainDecoder<A> extends DecoderBase<A> {
constructor( constructor(
readonly decoder: Decoder<any>, readonly _decoder: Decoder<any>,
readonly andThen: (x: Validation<any>) => Decoder<A>, readonly _then: (x: Validation<any>) => Decoder<A>,
) { super(); } ) { super(); }
} }
export class OneOf<A> extends DecoderBase<A> {
/**
* `oneOf` комбинатор
*/
export class OneOfDecoder<A> extends DecoderBase<A> {
constructor( constructor(
readonly alternatives: Decoder<any>[], readonly _alternatives: Decoder<any>[],
) { super(); } ) { super(); }
} }
export class Discriminate<A> extends DecoderBase<A> {
/**
* `discriminateOn` комбинатор
*/
export class DiscriminatedDecoder<A> extends DecoderBase<A> {
constructor( constructor(
readonly discriminator: string|number, readonly _discriminator: string|number,
readonly alternatives: Record<string|number, Decoder<any>>, readonly _alternatives: Record<string|number, Decoder<any>>,
) { super(); } ) { super(); }
} }
export abstract class ToDecoder<A> extends DecoderBase<A> {
/**
* `discriminateOn` комбинатор
*/
export abstract class HasDecoder<A> extends DecoderBase<A> {
abstract toDecoder(): Decoder<A>; abstract toDecoder(): Decoder<A>;
} }
export class WithDefault<A> extends ToDecoder<A> {
/**
* `discriminateOn` комбинатор
*/
export class WithDefaultDecoder<A> extends HasDecoder<A> {
constructor( constructor(
readonly decoder: Decoder<A>, readonly _decoder: Decoder<A>,
readonly defaultValue: A, readonly _default: A,
) { super(); } ) { super(); }
toDecoder() { toDecoder() {
return new OneOfDecoder<A>([this.decoder, of(this.defaultValue)]); return new OneOf<A>([this._decoder, of(this._default)]);
} }
} }
/** /**
* Тип результата для функции-валидатора * Результат валидаии
*/ */
export type Validation<A> = Either<Problem|string, A>; export type Validation<A> = Either<Problem|string, A>;
...@@ -283,11 +231,11 @@ export type Validation<A> = Either<Problem|string, A>; ...@@ -283,11 +231,11 @@ export type Validation<A> = Either<Problem|string, A>;
/** /**
* Конструктир кастомных декодеров * Конструктир кастомных декодеров
*/ */
export function decoder<A>(validate: (value: any) => Either<Problem|string, A>): CustomDecoder<A>; export function decoder<A>(validate: (value: any) => Either<Problem|string, A>): Custom<A>;
export function decoder<A>(name: string, validate: (value: any) => Either<Problem|string, A>): CustomDecoder<A>; export function decoder<A>(name: string, validate: (value: any) => Either<Problem|string, A>): Custom<A>;
export function decoder(): any { export function decoder(): any {
if (arguments.length === 1) return new CustomDecoder('custom', arguments[0]); if (arguments.length === 1) return new Custom('custom', arguments[0]);
if (arguments.length === 2) return new CustomDecoder(arguments[0], arguments[1]); if (arguments.length === 2) return new Custom(arguments[0], arguments[1]);
throw new TypeError(`decoder: invalid number of arguments`); throw new TypeError(`decoder: invalid number of arguments`);
} }
...@@ -295,8 +243,8 @@ export function decoder(): any { ...@@ -295,8 +243,8 @@ export function decoder(): any {
/** /**
* Алиас для `x => new PureDecoder(Either.of(x))` * Алиас для `x => new PureDecoder(Either.of(x))`
*/ */
export function of<A extends Expr>(a: A): PureDecoder<A> { export function of<A extends Expr>(a: A): Pure<A> {
return new PureDecoder(Either.of(a)); return new Pure(Either.of(a));
} }
...@@ -304,7 +252,7 @@ export function of<A extends Expr>(a: A): PureDecoder<A> { ...@@ -304,7 +252,7 @@ export function of<A extends Expr>(a: A): PureDecoder<A> {
* Алиас для `x => new PureDecoder(Either.failure(x))` * Алиас для `x => new PureDecoder(Either.failure(x))`
*/ */
export function fail(x: Problem|string): Decoder<never> { export function fail(x: Problem|string): Decoder<never> {
return new PureDecoder(Either.failure(x)); return new Pure(Either.failure(x));
} }
...@@ -312,16 +260,16 @@ export function fail(x: Problem|string): Decoder<never> { ...@@ -312,16 +260,16 @@ export function fail(x: Problem|string): Decoder<never> {
* Аппликативный комбинатор * Аппликативный комбинатор
* TODO: сделать отдельным вариантом в ADT для реализации `prettyPrint` * TODO: сделать отдельным вариантом в ADT для реализации `prettyPrint`
*/ */
export function ap<A,B>(a: Decoder<A>, f:(a: A) => B): CustomDecoder<B>; export function ap<A,B>(a: Decoder<A>, f:(a: A) => B): Custom<B>;
export function ap<A,B,C>(a: Decoder<A>, b: Decoder<B>, f:(a: A, b: B) => C):CustomDecoder<C>; export function ap<A,B,C>(a: Decoder<A>, b: Decoder<B>, f:(a: A, b: B) => C):Custom<C>;
export function ap<A,B,C,D>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, f:(a: A, b: B, c: C) => D):CustomDecoder<D>; export function ap<A,B,C,D>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, f:(a: A, b: B, c: C) => D):Custom<D>;
export function ap<A,B,C,D,E>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, f:(a: A, b: B, c: C, d: D) => E):CustomDecoder<E>; export function ap<A,B,C,D,E>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, f:(a: A, b: B, c: C, d: D) => E):Custom<E>;
export function ap<A,B,C,D,E,F>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f:(a: A, b: B, c: C, d: D, e: E) => F):CustomDecoder<F>; export function ap<A,B,C,D,E,F>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f:(a: A, b: B, c: C, d: D, e: E) => F):Custom<F>;
export function ap<A,B,C,D,E,F,G>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f_:Decoder<F>, f:(a: A, b: B, c: C, d: D, e: E, f: F) => G):CustomDecoder<G>; export function ap<A,B,C,D,E,F,G>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f_:Decoder<F>, f:(a: A, b: B, c: C, d: D, e: E, f: F) => G):Custom<G>;
export function ap<A,B,C,D,E,F,G,H>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f_:Decoder<F>, g: Decoder<G>, f:(a: A, b: B, c: C, d: D, e: E, f: F, g: G) => H):CustomDecoder<H>; export function ap<A,B,C,D,E,F,G,H>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f_:Decoder<F>, g: Decoder<G>, f:(a: A, b: B, c: C, d: D, e: E, f: F, g: G) => H):Custom<H>;
export function ap<A,B,C,D,E,F,G,H,J>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f_:Decoder<F>, g: Decoder<G>, h: Decoder<H>, f:(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) => J):CustomDecoder<J>; export function ap<A,B,C,D,E,F,G,H,J>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f_:Decoder<F>, g: Decoder<G>, h: Decoder<H>, f:(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) => J):Custom<J>;
export function ap<A,B,C,D,E,F,G,H,J,K>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f_:Decoder<F>, g: Decoder<G>, h: Decoder<H>, j: Decoder<J>, f:(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, j: J) => K):CustomDecoder<K>; export function ap<A,B,C,D,E,F,G,H,J,K>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f_:Decoder<F>, g: Decoder<G>, h: Decoder<H>, j: Decoder<J>, f:(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, j: J) => K):Custom<K>;
export function ap<A,B,C,D,E,F,G,H,J,K,L>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f_:Decoder<F>, g: Decoder<G>, h: Decoder<H>, j: Decoder<J>, k: Decoder<K>, f:(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, j: J, k: K) => L):CustomDecoder<L>; export function ap<A,B,C,D,E,F,G,H,J,K,L>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f_:Decoder<F>, g: Decoder<G>, h: Decoder<H>, j: Decoder<J>, k: Decoder<K>, f:(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, j: J, k: K) => L):Custom<L>;
export function ap(...args: Array<Decoder<any> | Function>): Decoder<any> { export function ap(...args: Array<Decoder<any> | Function>): Decoder<any> {
return decoder('ap', (val) => { return decoder('ap', (val) => {
const func = args[args.length - 1] as Function; const func = args[args.length - 1] as Function;
...@@ -340,14 +288,14 @@ export function ap(...args: Array<Decoder<any> | Function>): Decoder<any> { ...@@ -340,14 +288,14 @@ export function ap(...args: Array<Decoder<any> | Function>): Decoder<any> {
// Примитивы // Примитивы
const anyDecoder = new PrimitiveDecoder<any>('any'); const anyDecoder = new Primitive<any>('any');
const stringDecoder = new PrimitiveDecoder<string>('string'); const stringDecoder = new Primitive<string>('string');
const booleanDecoder = new PrimitiveDecoder<boolean>('boolean'); const booleanDecoder = new Primitive<boolean>('boolean');
const nullDecoder = new PrimitiveDecoder<null>('null'); const nullDecoder = new Primitive<null>('null');
const undefinedDecoder = new PrimitiveDecoder<undefined>('undefined'); const undefinedDecoder = new Primitive<undefined>('undefined');
export const nat = new PrimitiveDecoder<number>('nat'); export const nat = new Primitive<number>('nat');
export const int = new PrimitiveDecoder<number>('int'); export const int = new Primitive<number>('int');
export const float = new PrimitiveDecoder<number>('float'); export const float = new Primitive<number>('float');
// Экспорт с переименованием // Экспорт с переименованием
...@@ -358,16 +306,16 @@ export { anyDecoder as any, stringDecoder as string, booleanDecoder as boolean, ...@@ -358,16 +306,16 @@ export { anyDecoder as any, stringDecoder as string, booleanDecoder as boolean,
* Сопоставление с несколькими декодерами до первого успешного * Сопоставление с несколькими декодерами до первого успешного
* сравнвния * сравнвния
*/ */
export function oneOf<A>(a: Decoder<A>): OneOfDecoder<A>; export function oneOf<A>(a: Decoder<A>): OneOf<A>;
export function oneOf<A,B>(a: Decoder<A>, b: Decoder<B>): OneOfDecoder<A|B>; export function oneOf<A,B>(a: Decoder<A>, b: Decoder<B>): OneOf<A|B>;
export function oneOf<A,B,C>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>): OneOfDecoder<A|B|C>; export function oneOf<A,B,C>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>): OneOf<A|B|C>;
export function oneOf<A,B,C,D>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>): OneOfDecoder<A|B|C|D>; export function oneOf<A,B,C,D>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>): OneOf<A|B|C|D>;
export function oneOf<A,B,C,D,E>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>): OneOfDecoder<A|B|C|D|E>; export function oneOf<A,B,C,D,E>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>): OneOf<A|B|C|D|E>;
export function oneOf<A,B,C,D,E,F>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f: Decoder<F>): OneOfDecoder<A|B|C|D|E|F>; export function oneOf<A,B,C,D,E,F>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f: Decoder<F>): OneOf<A|B|C|D|E|F>;
export function oneOf<array extends Decoder<any>[]>(array: array): Decoder<array[number]['_A']>; export function oneOf<array extends Decoder<any>[]>(array: array): Decoder<array[number]['_A']>;
export function oneOf(): Decoder<any> { export function oneOf(): Decoder<any> {
const decoders = Array.isArray(arguments[0]) ? arguments[0] : Array.prototype.slice.call(arguments); const decoders = Array.isArray(arguments[0]) ? arguments[0] : Array.prototype.slice.call(arguments);
return new OneOfDecoder(decoders); return new OneOf(decoders);
} }
...@@ -379,21 +327,8 @@ export function record<fields extends { [k: string]: Decoder<any> }>(fields: fie ...@@ -379,21 +327,8 @@ export function record<fields extends { [k: string]: Decoder<any> }>(fields: fie
return new RecordDecoder(fields); return new RecordDecoder(fields);
} }
// Вложенные записи в декодерах
export function record2<fields extends { [k: string]: Decoder<any> }>(fields: fields): RecordDecoder<{[k in keyof fields]: fields[k]['_A'] }> {
return new RecordDecoder(fields);
}
// Вложенные записи в декодерах
export function record3<fields extends { [k: string]: Decoder<any> }>(fields: fields): RecordDecoder<{[k in keyof fields]: fields[k]['_A'] }> {
return new RecordDecoder(fields);
}
export const d = array(record({ a: array(record({ b: record({ c: stringDecoder }) })) } ));
export function dict<A>(decoder: Decoder<A>): Decoder<Record<string, A>> { export function dict<A>(decoder: Decoder<A>): Decoder<Record<string, A>> {
return new DictionaryDecoder(decoder); return new Dict(decoder);
} }
export function at<A>(path: string|string[]|number|number[], decoder: Decoder<A>): Decoder<A> { export function at<A>(path: string|string[]|number|number[], decoder: Decoder<A>): Decoder<A> {
...@@ -426,9 +361,9 @@ export function required<A>(key: string|string[], dec: Decoder<A>): Decoder<A> { ...@@ -426,9 +361,9 @@ export function required<A>(key: string|string[], dec: Decoder<A>): Decoder<A> {
* // => Decoder<{ { type: 'director', ... } | { type: 'employee', ... }}> * // => Decoder<{ { type: 'director', ... } | { type: 'employee', ... }}>
* ``` * ```
*/ */
export type DiscriminateOn<TagKey extends string, Descriptor extends Record<string, Decoder<any>>> = DiscriminatedDecoder<{ [K in keyof Descriptor]: { [K2 in TagKey]: K } & Descriptor[K]['_A']}[keyof Descriptor]>; export type DiscriminateOn<TagKey extends string, Descriptor extends Record<string, Decoder<any>>> = Discriminate<{ [K in keyof Descriptor]: { [K2 in TagKey]: K } & Descriptor[K]['_A']}[keyof Descriptor]>;
export function discriminate<TagKey extends string, Descriptor extends Record<string, Decoder<any>>>(key: TagKey, record: Descriptor): DiscriminateOn<TagKey, Descriptor> { export function discriminate<TagKey extends string, Descriptor extends Record<string, Decoder<any>>>(key: TagKey, record: Descriptor): DiscriminateOn<TagKey, Descriptor> {
return new DiscriminatedDecoder(key, record); return new Discriminate(key, record);
} }
...@@ -450,7 +385,7 @@ export function literals<A extends Expr[]>(...array: A): Decoder<A[number]>; ...@@ -450,7 +385,7 @@ export function literals<A extends Expr[]>(...array: A): Decoder<A[number]>;
export function literals<A extends Expr[]>(array: A): Decoder<A[number]>; export function literals<A extends Expr[]>(array: A): Decoder<A[number]>;
export function literals(): Decoder<any> { export function literals(): Decoder<any> {
const literals: Expr[] = Array.isArray(arguments[0]) ? arguments[0] : Array.prototype.slice.call(arguments); const literals: Expr[] = Array.isArray(arguments[0]) ? arguments[0] : Array.prototype.slice.call(arguments);
return new OneOfDecoder(literals.map(x => decoder(v => v === x ? Either.of(v) : Either.failure(`expected ${x}, got ${fancyTypeOf(v)}`)))); return new OneOf(literals.map(x => decoder(v => v === x ? Either.of(v) : Either.failure(`expected ${x}, got ${fancyTypeOf(v)}`))));
} }
...@@ -462,19 +397,9 @@ export function literals(): Decoder<any> { ...@@ -462,19 +397,9 @@ export function literals(): Decoder<any> {
* const pair_2 = t.record({ '0': t.string, '1': t.number }); // тоже самое * const pair_2 = t.record({ '0': t.string, '1': t.number }); // тоже самое
* ``` * ```
*/ */
export function tuple<A>(a: Decoder<A>): Decoder<[A]>; // @ts-ignore
export function tuple<A, B>(a: Decoder<A>, b: Decoder<B>): Decoder<[A, B]>; export function tuple<A extends Decoder<any>[]>(...reps: A): Decoder<{ [K in keyof A]: A[K]['_A'] }>;
export function tuple<A, B, C>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>): Decoder<[A, B, C]>; export function tuple(...args): Decoder<any> {
export function tuple<A, B, C, D>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>): Decoder<[A, B, C, D]>;
export function tuple<A, B, C, D, E>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>): Decoder<[A, B, C, D, E]>;
export function tuple<A, B, C, D, E, F>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f: Decoder<F>): Decoder<[A, B, C, D, E, F]>;
export function tuple<A, B, C, D, E, F, G>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f: Decoder<F>, g: Decoder<G>): Decoder<[A, B, C, D, E, F, G]>;
export function tuple<A, B, C, D, E, F, G, H>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f: Decoder<F>, g: Decoder<G>, h: Decoder<H>): Decoder<[A, B, C, D, E, F, G, H]>;
export function tuple<A, B, C, D, E, F, G, H, I>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f: Decoder<F>, g: Decoder<G>, h: Decoder<H>, i: Decoder<I>): Decoder<[A, B, C, D, E, F, G, H, I]>;
export function tuple<A, B, C, D, E, F, G, H, I, J>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f: Decoder<F>, g: Decoder<G>, h: Decoder<H>, i: Decoder<I>, j: Decoder<J>): Decoder<[A, B, C, D, E, F, G, H, I, J]>;
export function tuple<A>(args: Decoder<A>[]): Decoder<A[]>;
export function tuple(): Decoder<any> {
const args: Decoder<any>[] = Array.isArray(arguments[0]) ? arguments[0] : Array.prototype.slice.call(arguments);
// @ts-ignore // @ts-ignore
return ap(...args.map((decoder, idx) => at(idx, decoder)), (...xs) => xs); return ap(...args.map((decoder, idx) => at(idx, decoder)), (...xs) => xs);
} }
...@@ -503,15 +428,15 @@ function fancyTypeOf(value: any): string { ...@@ -503,15 +428,15 @@ function fancyTypeOf(value: any): string {
* Выполнение валидации * Выполнение валидации
*/ */
export function doValidate<A>(decoder: Decoder<A>, value: unknown): Either<Problem, A> { export function doValidate<A>(decoder: Decoder<A>, value: unknown): Either<Problem, A> {
if (decoder instanceof CustomDecoder) { if (decoder instanceof Custom) {
return decoder.validateCustom(value).mapLeft(projectProblem); return decoder._validate(value).mapLeft(projectProblem);
} }
if (decoder instanceof ArrayDecoder) { if (decoder instanceof ArrayDecoder) {
const output: any[] = []; const output: any[] = [];
if (!Array.isArray(value)) return Either.failure(projectProblem('not an array')); if (!Array.isArray(value)) return Either.failure(projectProblem('not an array'));
for (let i = 0; i < value.length; i++) { for (let i = 0; i < value.length; i++) {
const ethr = doValidate(decoder.decoder, value[i]); const ethr = doValidate(decoder._decoder, value[i]);
switch(ethr.tag) { switch(ethr.tag) {
case 'Left': { return ethr; } case 'Left': { return ethr; }
case 'Right': output.push(ethr.value); break; case 'Right': output.push(ethr.value); break;
...@@ -520,13 +445,13 @@ export function doValidate<A>(decoder: Decoder<A>, value: unknown): Either<Probl ...@@ -520,13 +445,13 @@ export function doValidate<A>(decoder: Decoder<A>, value: unknown): Either<Probl
return Either.of(output as any as A); return Either.of(output as any as A);
} }
if (decoder instanceof DictionaryDecoder) { if (decoder instanceof Dict) {
if (typeof (value) !== 'object') return Either.failure(projectProblem('not an object')); if (typeof (value) !== 'object') return Either.failure(projectProblem('not an object'));
if (value === null) return Either.failure(projectProblem('found null')); if (value === null) return Either.failure(projectProblem('found null'));
const output: { [k: string]: A } = {}; const output: { [k: string]: A } = {};
for (let key in value) { for (let key in value) {
if (!value.hasOwnProperty(key)) continue; if (!value.hasOwnProperty(key)) continue;
const ethr = doValidate(decoder.decoder, value[key]); const ethr = doValidate(decoder._decoder, value[key]);
switch(ethr.tag) { switch(ethr.tag) {
case 'Left': { return ethr; } case 'Left': { return ethr; }
case 'Right': output[key] = ethr.value; break; case 'Right': output[key] = ethr.value; break;
...@@ -539,9 +464,9 @@ export function doValidate<A>(decoder: Decoder<A>, value: unknown): Either<Probl ...@@ -539,9 +464,9 @@ export function doValidate<A>(decoder: Decoder<A>, value: unknown): Either<Probl
if (typeof (value) !== 'object') return Either.failure(projectProblem('not an object')); if (typeof (value) !== 'object') return Either.failure(projectProblem('not an object'));
if (value === null) return Either.failure(projectProblem('found null')); if (value === null) return Either.failure(projectProblem('found null'));
const output: { [k: string]: any } = {}; const output: { [k: string]: any } = {};
for (let key in decoder.description) { for (let key in decoder._description) {
if (!decoder.description.hasOwnProperty(key)) continue; if (!decoder._description.hasOwnProperty(key)) continue;
const ethr = doValidate(decoder.description[key], value[key]); const ethr = doValidate(decoder._description[key], value[key]);
switch(ethr.tag) { switch(ethr.tag) {
case 'Left': { return ethr; } case 'Left': { return ethr; }
case 'Right': output[key] = ethr.value; break; case 'Right': output[key] = ethr.value; break;
...@@ -552,18 +477,18 @@ export function doValidate<A>(decoder: Decoder<A>, value: unknown): Either<Probl ...@@ -552,18 +477,18 @@ export function doValidate<A>(decoder: Decoder<A>, value: unknown): Either<Probl
if (decoder instanceof AtDecoder) { if (decoder instanceof AtDecoder) {
let iter = value as any; let iter = value as any;
for (let i in decoder.path) { for (let i in decoder._path) {
if (iter === undefined || !iter.hasOwnProperty(decoder.path[i])) { if (iter === undefined || !iter.hasOwnProperty(decoder._path[i])) {
iter = undefined; iter = undefined;
break; break;
} }
iter = iter[decoder.path[i]]; iter = iter[decoder._path[i]];
} }
return doValidate(decoder.decoder, iter); return doValidate(decoder._decoder, iter);
} }
if (decoder instanceof PrimitiveDecoder) { if (decoder instanceof Primitive) {
switch (decoder.primitive) { switch (decoder._type) {
case 'null': return value === null ? Either.of(value as A) : Either.failure(projectProblem(`expected null, got ${fancyTypeOf(value)}`)); case 'null': return value === null ? Either.of(value as A) : Either.failure(projectProblem(`expected null, got ${fancyTypeOf(value)}`));
case 'undefined': return value === undefined ? Either.of(value as A) : Either.failure(projectProblem(`expected undefined, got ${fancyTypeOf(value)}`)); case 'undefined': return value === undefined ? Either.of(value as A) : Either.failure(projectProblem(`expected undefined, got ${fancyTypeOf(value)}`));
case 'string': return typeof(value) === 'string' ? Either.of(value as any) : Either.failure(projectProblem(`expected a string, got ${fancyTypeOf(value)}`)); case 'string': return typeof(value) === 'string' ? Either.of(value as any) : Either.failure(projectProblem(`expected a string, got ${fancyTypeOf(value)}`));
...@@ -573,15 +498,15 @@ export function doValidate<A>(decoder: Decoder<A>, value: unknown): Either<Probl ...@@ -573,15 +498,15 @@ export function doValidate<A>(decoder: Decoder<A>, value: unknown): Either<Probl
case 'int': return typeof (value) !== 'number' ? Either.failure(projectProblem('not a number')) : (value|0) === value ? Either.of(value as any) : Either.failure(projectProblem('not an integer')) case 'int': return typeof (value) !== 'number' ? Either.failure(projectProblem('not a number')) : (value|0) === value ? Either.of(value as any) : Either.failure(projectProblem('not an integer'))
case 'float': return typeof (value) !== 'number' ? Either.failure(projectProblem('not a number')) : Either.of(value as any); case 'float': return typeof (value) !== 'number' ? Either.failure(projectProblem('not a number')) : Either.of(value as any);
} }
return absurd(decoder.primitive); return absurd(decoder._type);
} }
if (decoder instanceof PureDecoder) { if (decoder instanceof Pure) {
return decoder.value.mapLeft(projectProblem); return decoder._value.mapLeft(projectProblem);
} }
if (decoder instanceof OneOfDecoder) { if (decoder instanceof OneOf) {
for (const d of decoder.alternatives) { for (const d of decoder._alternatives) {
const ethr = doValidate(d, value); const ethr = doValidate(d, value);
switch(ethr.tag) { switch(ethr.tag) {
case 'Left': break; case 'Left': break;
...@@ -591,20 +516,20 @@ export function doValidate<A>(decoder: Decoder<A>, value: unknown): Either<Probl ...@@ -591,20 +516,20 @@ export function doValidate<A>(decoder: Decoder<A>, value: unknown): Either<Probl
return Either.failure(projectProblem('none of decoders succeded')); return Either.failure(projectProblem('none of decoders succeded'));
} }
if (decoder instanceof ChainDecoder) { if (decoder instanceof Chain) {
return doValidate(decoder.andThen(doValidate(decoder.decoder, value)), value); return doValidate(decoder._then(doValidate(decoder._decoder, value)), value);
} }
if (decoder instanceof DiscriminatedDecoder) { if (decoder instanceof Discriminate) {
const key = decoder.discriminator; const key = decoder._discriminator;
const record = decoder.alternatives; const record = decoder._alternatives;
if (typeof(value) !== 'object' || !value) return Either.failure(projectProblem('expected an object')); if (typeof(value) !== 'object' || !value) return Either.failure(projectProblem('expected an object'));
if (!value.hasOwnProperty(key)) return Either.failure(projectProblem(`expected input would have key '${key}'`)); if (!value.hasOwnProperty(key)) return Either.failure(projectProblem(`expected input would have key '${key}'`));
if (!(value[key] in record)) return Either.failure(projectProblem(`unknown value of discriminated key ${value[key]}`)); if (!(value[key] in record)) return Either.failure(projectProblem(`unknown value of discriminated key ${value[key]}`));
return record[value[key]].validate(value).map(x => ({ ...x, [key]: value[key] })); return record[value[key]].validate(value).map(x => ({ ...x, [key]: value[key] }));
} }
if (decoder instanceof HasDecoder) { if (decoder instanceof ToDecoder) {
return doValidate(decoder.toDecoder(), value); return doValidate(decoder.toDecoder(), value);
} }
...@@ -617,7 +542,6 @@ export function doValidate<A>(decoder: Decoder<A>, value: unknown): Either<Probl ...@@ -617,7 +542,6 @@ export function doValidate<A>(decoder: Decoder<A>, value: unknown): Either<Probl
} }
/** /**
* Печать декодера. * Печать декодера.
* ```ts * ```ts
...@@ -626,21 +550,21 @@ export function doValidate<A>(decoder: Decoder<A>, value: unknown): Either<Probl ...@@ -626,21 +550,21 @@ export function doValidate<A>(decoder: Decoder<A>, value: unknown): Either<Probl
* ``` * ```
*/ */
export function prettyPrint(decoder: Decoder<any>): string { export function prettyPrint(decoder: Decoder<any>): string {
if (decoder instanceof CustomDecoder) return `t.decoder(${JSON.stringify(decoder.name)}, <func>)`; if (decoder instanceof Custom) return `t.decoder(${JSON.stringify(decoder._name)}, <func>)`;
if (decoder instanceof ArrayDecoder) return `t.array(${decoder.decoder.prettyPrint()})`; if (decoder instanceof ArrayDecoder) return `t.array(${decoder._decoder.prettyPrint()})`;
if (decoder instanceof DictionaryDecoder) return `t.dict(${decoder.decoder.prettyPrint()})`; if (decoder instanceof Dict) return `t.dict(${decoder._decoder.prettyPrint()})`;
if (decoder instanceof RecordDecoder) return `t.record({ ${Object.keys(decoder.description).map(k => decoder.description[k].prettyPrint()).join(', ')} })`; if (decoder instanceof RecordDecoder) return `t.record({ ${Object.keys(decoder._description).map(k => decoder._description[k].prettyPrint()).join(', ')} })`;
if (decoder instanceof AtDecoder) return `t.at(${JSON.stringify(decoder.path)}, ${decoder.decoder.prettyPrint()})`; if (decoder instanceof AtDecoder) return `t.at(${JSON.stringify(decoder._path)}, ${decoder._decoder.prettyPrint()})`;
if (decoder instanceof PrimitiveDecoder) return `t.${decoder.primitive}`; if (decoder instanceof Primitive) return `t.${decoder._type}`;
if (decoder instanceof PureDecoder) return `t.of(${JSON.stringify(decoder.value)})`; if (decoder instanceof Pure) return `t.of(${JSON.stringify(decoder._value)})`;
if (decoder instanceof ChainDecoder) return `${decoder.prettyPrint()}.chain(<func>)`; if (decoder instanceof Chain) return `${decoder.prettyPrint()}.chain(<func>)`;
if (decoder instanceof OneOfDecoder) return `t.oneOf(${decoder.alternatives.map(x => x.prettyPrint()).join(', ')})`; if (decoder instanceof OneOf) return `t.oneOf(${decoder._alternatives.map(x => x.prettyPrint()).join(', ')})`;
if (decoder instanceof DiscriminatedDecoder) { if (decoder instanceof Discriminate) {
const discriminator = JSON.stringify(decoder.discriminator); const discriminator = JSON.stringify(decoder._discriminator);
const alternatives = Object.keys(decoder.alternatives).map(k => JSON.stringify(k) + ": " + decoder.alternatives[k].prettyPrint()).join(', '); const alternatives = Object.keys(decoder._alternatives).map(k => JSON.stringify(k) + ": " + decoder._alternatives[k].prettyPrint()).join(', ');
return `t.discriminateOn(${JSON.stringify(discriminator)}, ${alternatives})`; return `t.discriminateOn(${JSON.stringify(discriminator)}, ${alternatives})`;
} }
if (decoder instanceof HasDecoder) { if (decoder instanceof ToDecoder) {
return prettyPrint(decoder.toDecoder()); return prettyPrint(decoder.toDecoder());
} }
return absurd(decoder); return absurd(decoder);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment