import * as Appli from 'fp-ts/Applicative' import * as _Apply from 'fp-ts/Apply' import * as C from 'fp-ts/Chain' import * as FIO from 'fp-ts/FromIO' import { constNull, constUndefined, constVoid, flip, pipe, } from 'fp-ts/function' import * as F from 'fp-ts/Functor' import * as IO from 'fp-ts/IO' import * as M from 'fp-ts/Monad' import * as MIO from 'fp-ts/MonadIO' import * as N from 'fp-ts/number' import * as Op from 'fp-ts/Option' import * as Or from 'fp-ts/Ord' import * as P from 'fp-ts/Pointed' import * as Pr from 'fp-ts/Predicate' import * as R from 'fp-ts/Random' import * as RA from 'fp-ts/ReadonlyArray' import * as RNEA from 'fp-ts/ReadonlyNonEmptyArray' import * as RR from 'fp-ts/ReadonlyRecord' import * as Se from 'fp-ts/Semigroup' import * as t from 'io-ts' import { PartialDeep } from '.' import { curry, recurse, run } from './function' import * as $St from './struct' import * as $t from './Type' import * as $Y from './Yield' export const URI = 'Mock' export type URI = typeof URI declare module 'fp-ts/HKT' { interface URItoKind { readonly [URI]: Mock } } type EnforceNonEmptyRecord = keyof R extends never ? never : R export interface Mock { (...as: ReadonlyArray>): IO.IO } export const Functor: F.Functor1 = { URI, map: (fa, f) => pipe(fa(), IO.map(f), FromIO.fromIO), } export const map = curry(flip(Functor.map)) export const flap = F.flap(Functor) export const bindTo = F.bindTo(Functor) export const Pointed: P.Pointed1 = { URI, of: (a: A): Mock => (...as) => pipe( as, RNEA.fromReadonlyArray, Op.traverse(IO.Applicative)(R.randomElem), IO.map( Op.match( () => a, (_a) => $t.struct.is(a) && $t.struct.is(_a) ? (pipe(_a, $St.filterDeep(Pr.not(t.undefined.is)), (_a) => $St.patch>( _a as PartialDeep, )(a), ) as A) : (_a as A), ), ), ), } export const of = Pointed.of export const Do = Pointed.of({}) export const Apply: _Apply.Apply1 = { ...Functor, ap: (fab, fa) => Chain.chain(fab, curry(Functor.map)(fa)), } export const ap = curry(flip(Apply.ap)) export const apFirst = _Apply.apFirst(Apply) export const apSecond = _Apply.apSecond(Apply) export const apS = _Apply.apS(Apply) export const Applicative: Appli.Applicative1 = { ...Pointed, ...Apply } export const Chain: C.Chain1 = { ...Apply, chain: (fa, f) => Functor.map(Functor.map(fa, f), (fb) => fb()()), } export const chain = curry(flip(Chain.chain)) export const chainFirst = C.chainFirst(Chain) export const bind = C.bind(Chain) export const Monad: M.Monad1 = { ...Applicative, ...Chain } export const FromIO: FIO.FromIO1 = { URI, fromIO: (fa) => (...as) => pipe( fa, IO.chain((a) => Pointed.of(a)(...as)), ), } export const fromIO = FromIO.fromIO export const fromIOK = FIO.fromIOK(FromIO) export const chainIOK = FIO.chainIOK(FromIO, Chain) export const chainFirstIOK = FIO.chainFirstIOK(FromIO, Chain) export const MonadIO: MIO.MonadIO1 = { ...Monad, ...FromIO } const _void: Mock = () => constVoid const _undefined: Mock = () => constUndefined const _null: Mock = () => constNull export const boolean: Mock = fromIO(R.randomBool) export const float = ( min = Number.MIN_SAFE_INTEGER * 1e-6, max = Number.MAX_SAFE_INTEGER * 1e-6, ): Mock => (...as) => pipe( fromIO(R.randomRange(min, Math.max(min + Number.EPSILON, max)))(...as), IO.map( Or.clamp(N.Ord)( min, Math.max(min + Number.EPSILON, max - Number.EPSILON), ), ), ) export const integer = ( min = Number.MIN_SAFE_INTEGER * 1e-6, max = Number.MAX_SAFE_INTEGER * 1e-6, ): Mock => (...as) => pipe(float(min, max)(...as), IO.map(Math.floor)) export const number = ( min = Number.MIN_SAFE_INTEGER * 1e-6, max = Number.MAX_SAFE_INTEGER * 1e-6, ): Mock => (...as) => pipe( union(float(min, max), integer(min, max))(...as), IO.map( Or.clamp(N.Ord)( min, Math.max(min + Number.EPSILON, max - Number.EPSILON), ), ), ) export const string: Mock = fromIO( () => Math.random().toString(36).split('.')[1], ) export const literal = (a: A): Mock => of(a) export const unknown = (depth = 10): Mock => recurse( (_unknown, _depth) => (_depth < depth ? union( _undefined, _null, boolean, number(), string, readonlyArray(_unknown), readonlyRecord(string, _unknown), ) : union(_undefined, _null, boolean, number(), string)) as Mock, ) export const nullable = (M: Mock): Mock => union(M, _undefined) export const tuple = _Apply.sequenceT(Apply) export const struct = _Apply.sequenceS(Apply) export const partial = ( Ms: EnforceNonEmptyRecord<{ readonly [K in keyof A]: Mock }>, ): Mock>> => pipe( Ms as RR.ReadonlyRecord>, RR.map(nullable), struct, ) as any export function union( a: Mock, b: Mock, c: Mock, d: Mock, e: Mock, f: Mock, g: Mock, h: Mock, i: Mock, j: Mock, ): Mock export function union( a: Mock, b: Mock, c: Mock, d: Mock, e: Mock, f: Mock, g: Mock, h: Mock, i: Mock, ): Mock export function union( a: Mock, b: Mock, c: Mock, d: Mock, e: Mock, f: Mock, g: Mock, h: Mock, ): Mock export function union( a: Mock, b: Mock, c: Mock, d: Mock, e: Mock, f: Mock, g: Mock, ): Mock export function union( a: Mock, b: Mock, c: Mock, d: Mock, e: Mock, f: Mock, ): Mock export function union( a: Mock, b: Mock, c: Mock, d: Mock, e: Mock, ): Mock export function union( a: Mock, b: Mock, c: Mock, d: Mock, ): Mock export function union( a: Mock, b: Mock, c: Mock, ): Mock export function union(a: Mock, b: Mock): Mock export function union(...Ms: RNEA.ReadonlyNonEmptyArray>) { return pipe(Ms, R.randomElem, IO.chain(run), fromIO) } export const readonlyArray = ( M: Mock, min = 0, max = 10, ): Mock> => pipe( R.randomInt(Math.max(0, min), Math.max(0, min, max)), IO.map((n) => pipe($Y.fromIO(M()), $Y.takeLeft(n), $Y.toReadonlyArray)), fromIO, ) export const readonlyNonEmptyArray = ( M: Mock, min = 1, max = 10, ): Mock> => readonlyArray(M, Math.max(1, min), Math.max(1, min, max)) as unknown as Mock< RNEA.ReadonlyNonEmptyArray > export const readonlyRecord = ( KM: Mock, TM: Mock, min = 0, max = 10, ): Mock> => pipe( readonlyArray(tuple(KM, TM), Math.max(0, min), Math.max(0, min, max))(), IO.map(RR.fromFoldable(Se.last(), RA.Foldable)), fromIO, ) export { _void as void, _undefined as undefined, _null as null }