@thisisagile/easy
Version:
Straightforward library for building domain-driven microservice architectures
98 lines (82 loc) • 3.6 kB
text/typescript
import { traverse } from './Traverse';
import { ifDefined } from './If';
import { Primitive } from '../types/Primitive';
import { Constructor, isConstructor, use } from '../types/Constructor';
import { asJson, json } from '../types/Json';
import { choose } from '../types/Case';
import { isArray, isBoolean, isFunction, isString } from '../types/Is';
import { meta } from '../types/Meta';
import { isPageList, PageList, toPageList } from '../types/PageList';
import { List } from '../types/List';
import { isEqual } from '../types/IsEqual';
import { DontInfer } from '../types/Types';
type Func<T = unknown> = (a: any, key?: string) => T;
type Viewer = { key: string; f: Func };
type ViewType<V = any> = Primitive | Constructor | Func | View<V> | undefined;
type ViewRecord<V = any> = Partial<Record<keyof V, ViewType>>;
const ignore = Symbol('view.ignore');
const keep = Symbol('view.keep');
export const toViewer = (key: string, value: ViewType): Viewer =>
choose(value)
.is.not.defined(v => v, { key, f: () => undefined } as Viewer)
.type(isBoolean, b => ({ key, f: () => b }))
.equals(ignore, { key, f: () => undefined })
.equals(keep, { key, f: (a: any) => traverse(a, key) })
.type(isString, s => ({ key, f: (a: any) => traverse(a, s) }))
.type(isConstructor, c => ({
key,
f: (a, key) => use(traverse(a, key), v => (isArray(v) ? v.map(i => optional(c, i)) : optional(c, v))),
}))
.type(isSimpleView, f => ({ key, f: (a: any) => f.from(traverse(a, key)) }))
.type(isFunction, f => ({ key, f }))
.else(v => ({ key, f: () => v }));
const optional = (c: Constructor, v: any) =>
ifDefined(
v,
i => new c(i),
() => v
);
const toViewers = (views: ViewRecord): Viewer[] =>
meta(views)
.entries<ViewType>()
.map(([k, v]) => toViewer(k, v));
export class View<V = any> {
constructor(
private views = {} as ViewRecord<V>,
readonly startsFrom: 'scratch' | 'source' = 'scratch',
readonly viewers: Viewer[] = toViewers(views)
) {}
get fromSource(): View<V> {
return new View(this.views, 'source', this.viewers);
}
from<T = unknown>(source: PageList<T>): PageList<V>;
from<T = unknown>(source: List<T>): List<V>;
from<T = unknown>(source: T[]): V[];
from<T = unknown>(source: T): V;
from<T = unknown>(source: PageList<T> | List<T> | T[] | T): PageList<V> | List<V> | V[] | V {
if (isPageList(source))
return toPageList(
source.map(s => this.reduce(s)),
source.options
);
if (isArray(source)) return source.map(s => this.reduce(s));
return this.reduce(source);
}
same = (one?: unknown, another?: unknown): boolean => isEqual(this.from(one), this.from(another));
private reduce = (source: any): any =>
use(asJson(source), src => this.viewers.reduce((acc, v) => json.set(acc, v.key, v.f(src, v.key)), this.startsFrom === 'scratch' ? {} : src));
}
export const isSimpleView = (a: unknown): a is View => a instanceof View;
export const view = <V = any>(views: ViewRecord<DontInfer<V>>): View<V> => new View<V>(views);
export const views = {
ignore,
keep,
skip: ignore,
value: (value: unknown) => () => value,
or: {
key: (altKey: string) => (a: unknown, key?: string) => traverse(a, key) ?? traverse(a, altKey),
value: (altValue: unknown) => (a: unknown, key?: string) => traverse(a, key) ?? altValue,
func: (altFunc: Func) => (a: unknown, key?: string) => traverse(a, key) ?? altFunc(a, key),
},
};
// spread: (a: any, key: string) => ({...a, ...(use(traverse(a, key), v => isObject(v) ? v : ({[key]: v})))}),