@thisisagile/easy
Version:
Straightforward library for building domain-driven microservice architectures
129 lines (111 loc) • 5.93 kB
text/typescript
import { Get, ofGet, Predicate } from './Get';
import { TypeGuard } from './TypeGuard';
import { Func } from './Func';
import { isDefined, isEmpty } from './Is';
import { validate } from '../validation/Validate';
import { tryTo } from './Try';
class CaseBuilder<V> {
is = {
true: <T>(pred: Predicate<V>, out: Get<T, V>): Case<T, V> => this.case(pred, out),
false: <T>(pred: Predicate<V>, out: Get<T, V>): Case<T, V> => this.case(!ofGet(pred, this.v), out),
truthy: <T>(prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(!!prop(this.v), out),
falsy: <T>(prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(!prop(this.v), out),
defined: <T>(prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(isDefined(prop(this.v)), out),
empty: <T>(prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(isEmpty(prop(this.v)), out),
valid: <T>(prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(validate(prop(this.v)).isValid, out),
in: <T>(prop: Get<Array<V>, V>, out: Get<T, V>): Case<T, V> => this.case(ofGet(prop, this.v).includes(this.v), out),
type: <T, U = unknown>(guard: TypeGuard<U>, out: Func<T, U>): Case<T, V> => this.type<T, U>(guard, out),
not: {
true: <T>(pred: Predicate<V>, out: Get<T, V>): Case<T, V> => this.case(!ofGet(pred, this.v), out),
false: <T>(pred: Predicate<V>, out: Get<T, V>): Case<T, V> => this.case(pred, out),
truthy: <T>(prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(!prop(this.v), out),
falsy: <T>(prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(!!prop(this.v), out),
defined: <T>(prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(!isDefined(prop(this.v)), out),
empty: <T>(prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(!isEmpty(prop(this.v)), out),
valid: <T>(prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(!validate(prop(this.v)).isValid, out),
in: <T>(prop: Get<Array<V>, V>, out: Get<T, V>): Case<T, V> => this.case(!ofGet(prop, this.v).includes(this.v), out),
type: <T, U = unknown>(guard: TypeGuard<U>, out: Get<T, V>): Case<T, V> => this.case(v => !guard(v as unknown), out),
},
};
if = this.is;
constructor(readonly v: V) {}
case<T>(pred: Predicate<V>, out: Get<T, V>): Case<T, V> {
return new Case<T, V>(this.v).case(pred, out);
}
type<T, U = unknown>(guard: TypeGuard<U>, out: Func<T, U>): Case<T, V> {
return new Case<T, V>(this.v).type<U>(guard, out);
}
equals<T>(value: V, out: Get<T, V>): Case<T, V> {
return new Case<T, V>(this.v).equals(value, out);
}
}
class Case<T, V = unknown> {
is = {
true: (pred: Predicate<V>, out: Get<T, V>): Case<T, V> => this.case(pred, out),
false: (pred: Predicate<V>, out: Get<T, V>): Case<T, V> => this.case(!ofGet(pred, this.value), out),
truthy: (prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(!!prop(this.value), out),
falsy: (prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(!prop(this.value), out),
defined: (prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(isDefined(prop(this.value)), out),
empty: (prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(isEmpty(prop(this.value)), out),
valid: (prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(validate(prop(this.value)).isValid, out),
in: (prop: Get<Array<V>, V>, out: Get<T, V>): Case<T, V> => this.case(ofGet(prop, this.value).includes(this.value), out),
type: <U>(guard: TypeGuard<U>, out: Func<T, U>): Case<T, V> => this.type<U>(guard, out),
not: {
true: (pred: Predicate<V>, out: Get<T, V>): Case<T, V> => this.case(!ofGet(pred, this.value), out),
false: (pred: Predicate<V>, out: Get<T, V>): Case<T, V> => this.case(pred, out),
truthy: (prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(!prop(this.value), out),
falsy: (prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(!!prop(this.value), out),
defined: (prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(!isDefined(prop(this.value)), out),
empty: (prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(!isEmpty(prop(this.value)), out),
valid: (prop: Func<unknown, V>, out: Get<T, V>): Case<T, V> => this.case(!validate(prop(this.value)).isValid, out),
in: (prop: Get<Array<V>, V>, out: Get<T, V>): Case<T, V> => this.case(!ofGet(prop, this.value).includes(this.value), out),
type: <U>(guard: TypeGuard<U>, out: Get<T, V>): Case<T, V> => this.case(v => !guard(v as unknown), out),
},
};
if = this.is;
constructor(
protected value: V,
protected outcome?: T
) {}
case(pred: Predicate<V>, out: Get<T, V>): Case<T, V> {
return tryTo(pred, this.value)
.is.true()
.map(() => ofGet(out, this.value))
.map(res => new Found(this.value, res) as Case<T, V>)
.or(this);
}
type<U>(guard: TypeGuard<U>, out: Func<T, U>): Case<T, V> {
return tryTo(guard, this.value)
.is.true()
.map(() => out(this.value as unknown as U))
.map(res => new Found(this.value, res) as Case<T, V>)
.or(this);
}
equals(value: V, out: Get<T, V>): Case<T, V> {
return this.case(this.value === value, out);
}
else(alt: Get<T, V>): T {
return ofGet<T, V>(alt, this.value);
}
}
class Found<T, V> extends Case<T, V> {
constructor(
protected value: V,
protected outcome: T
) {
super(value, outcome);
}
case(pred: Predicate<V>, out: Get<T, V>): this {
return this;
}
type<U>(guard: TypeGuard<U>, out: Func<T, U>): Case<T, V> {
return this;
}
equals(value: V, out: Get<T, V>): Case<T, V> {
return this;
}
else(alt: Get<T, V>): T {
return this.outcome;
}
}
export const choose = <V>(value: V) => new CaseBuilder<V>(value);