Commit 444c887c by Vladislav Lagunov

Merge remote-tracking branch 'origin/develop'

parents 02a5ab2d 6edd1d06
import * as t from '../decoder';
import { camelCase } from 'lodash';
import { absurd } from '../types';
import { WithDefault, RecordDecoder, Decoder } from '../decoder';
import { Either } from '../either';
/**
......@@ -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));
}
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);
if (source.tag === 'Cli') {
const value: Record<string, any> = {};
......@@ -47,8 +49,8 @@ export function merge<A>(decoder: t.RecordDecoder<A>, ...srcs: ConfigSource[]):
const rest = source.args[i].substr(prefixLen);
const [k, v] = rest.indexOf('=') === -1 ? [rest, source.args[++i] || ''] : source.args[i].split('=');
// Проверка того что это известно
if (!decoder.description.hasOwnProperty(k)) continue;
value[k] = decoderFromString(decoder.description[k], v);
if (!decoder._description.hasOwnProperty(k)) continue;
value[k] = fromString(decoder._description[k], v);
}
return value;
}
......@@ -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 value = substring.split('&').map(x => x.split('=')).reduce((acc, [k, v]: any) => {
const key = source.transformKey(decodeURIComponent(k));
if (!decoder.description.hasOwnProperty(key)) return acc;
acc[key] = decoderFromString(decoder.description[key], decodeURIComponent(v));
if (!decoder._description.hasOwnProperty(key)) return acc;
acc[key] = fromString(decoder._description[key], decodeURIComponent(v));
return acc;
}, {});
return value;
......@@ -77,16 +79,12 @@ export function merge<A>(decoder: t.RecordDecoder<A>, ...srcs: ConfigSource[]):
}
export function decoderFromString(decoder: t.Decoder<any>, value: string): any {
if (decoder instanceof t.CustomDecoder) {
throw new Error('[config-reader] CustomDecoder is not supported');
export function fromString(d: Decoder<any>, value: string): unknown {
if (d instanceof t.ArrayDecoder) {
return value.split(', ').map(x => fromString(d._decoder, x));
}
if (decoder instanceof t.ArrayDecoder) {
return value.split(', ').map(x => decoderFromString(decoder.decoder, x));
}
if (decoder instanceof t.DictionaryDecoder) {
if (d instanceof t.Dict) {
try {
return JSON.parse(value);
} catch(e) {
......@@ -94,7 +92,7 @@ export function decoderFromString(decoder: t.Decoder<any>, value: string): any {
}
}
if (decoder instanceof t.RecordDecoder) {
if (d instanceof t.RecordDecoder) {
try {
return JSON.parse(value);
} catch(e) {
......@@ -102,7 +100,23 @@ export function decoderFromString(decoder: t.Decoder<any>, value: string): any {
}
}
if (decoder instanceof t.AtDecoder) {
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 (d instanceof t.Discriminate) {
try {
return JSON.parse(value);
} catch(e) {
......@@ -110,45 +124,141 @@ export function decoderFromString(decoder: t.Decoder<any>, value: string): any {
}
}
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 (d instanceof t.Pure) {
return value;
}
if (decoder instanceof t.PureDecoder) {
if (d instanceof t.ToDecoder) {
if (d instanceof WithDefault) {
return fromString(d._decoder, value);
}
if (d instanceof t.Variants) {
return value;
}
return fromString(d.toDecoder(), value);
}
if (decoder instanceof t.ChainDecoder) {
throw new Error('[config-reader] ChainDecoder is not supported');
if (d instanceof t.Custom) {
throw new Error('[config-reader]: CustomDecoder is not supported');
}
if (decoder instanceof t.OneOfDecoder) {
throw new Error('[config-reader] OneOfDecoder is not supported');
if (d instanceof t.AtDecoder) {
throw new Error('[config-reader]: AtDecoder is not supported');
}
if (decoder instanceof t.DiscriminatedDecoder) {
try {
return JSON.parse(value);
} catch(e) {
return value;
if (d instanceof t.Chain) {
throw new Error('[config-reader]: ChainDecoder is not supported');
}
return absurd(d);
}
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('|') };
}
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;
}
if (d instanceof t.Variants) {
return { type: d._variants.map(x => typeof(x) === 'string' ? x : JSON.stringify(x)).join('|') };
}
const info = go(d.toDecoder());
if (d instanceof ConfigDescription) {
info.description = d._description;
}
return info;
}
if (decoder instanceof t.HasDecoder) {
if (decoder instanceof t.WithDefaultDecoder) {
return decoderFromString(decoder.decoder, value);
if (d instanceof t.Custom) {
throw new Error('[config-reader]: CustomDecoder is not supported');
}
return decoderFromString(decoder.toDecoder(), value);
if (d instanceof t.AtDecoder) {
throw new Error('[config-reader]: AtDecoder is not supported');
}
return absurd(decoder);
if (d instanceof t.Chain) {
throw new Error('[config-reader]: ChainDecoder is not supported');
}
return absurd(d);
}
}
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));
};
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