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 }