import type { Cache } from '../Cache' import type { DataSource } from '../DataSource' import type { DataSourceAspect } from '../DataSourceAspect' import type { Described } from '../Described' import type { Query } from '../Query' import type { Request } from '../Request' import type { QueryContext } from './QueryContext' import type { FIO } from '@principia/base/IO' import type * as Ca from '@principia/base/IO/Cause' import type { URef } from '@principia/base/Ref' import * as E from '@principia/base/Either' import { pipe } from '@principia/base/function' import * as I from '@principia/base/IO' import * as M from '@principia/base/Maybe' import { matchTag_ } from '@principia/base/util/match' import * as Q from '../Query' export class Effect { readonly _tag = 'Effect' constructor(readonly query: Query) {} } export function effect(query: Query): Continue { return new Effect(query) } export class Get { readonly _tag = 'Get' constructor(readonly io: FIO) {} } export function get(io: FIO): Continue { return new Get(io) } export type Continue = Effect | Get export function matchCauseQuery_( cont: Continue, onFailure: (cause: Ca.Cause) => Query, onSuccess: (a: A) => Query ): Continue { return matchTag_(cont, { Effect: ({ query }) => new Effect(Q.matchCauseQuery_(query, onFailure, onSuccess)), Get: ({ io }) => pipe(Q.fromIO(io), (q) => Q.matchCauseQuery_(q, onFailure, onSuccess), effect) }) } export function matchCauseQuery( onFailure: (cause: Ca.Cause) => Query, onSuccess: (a: A) => Query ): (ma: Continue) => Continue { return (ma) => matchCauseQuery_(ma, onFailure, onSuccess) } export function mapError_(fa: Continue, f: (e: E) => E1): Continue { return matchTag_(fa, { Effect: ({ query }) => effect(Q.mapError_(query, f)), Get: ({ io }) => pipe(io, I.mapError(f), get) }) } export function mapError(f: (e: E) => E1): (ma: Continue) => Continue { return (ma) => mapError_(ma, f) } export function crossWith_( fa: Continue, fb: Continue, f: (a: A, b: B) => C ): Continue { return fa._tag === 'Effect' ? fb._tag === 'Effect' ? effect(Q.crossWith_(fa.query, fb.query, f)) : effect(Q.crossWith_(fa.query, Q.fromIO(fb.io), f)) : fb._tag === 'Effect' ? effect(pipe(Q.fromIO(fa.io), Q.crossWith(fb.query, f))) : get(I.crossWith_(fa.io, fb.io, f)) } export function crossWith( fb: Continue, f: (a: A, b: B) => C ): (fa: Continue) => Continue { return (fa) => crossWith_(fa, fb, f) } export function map_(fa: Continue, f: (a: A) => B): Continue { return matchTag_(fa, { Effect: ({ query }) => effect(Q.map_(query, f)), Get: ({ io }) => get(I.map_(io, f)) }) } export function map(f: (a: A) => B): (fa: Continue) => Continue { return (fa) => map_(fa, f) } export function crossWithC_( fa: Continue, fb: Continue, f: (a: A, b: B) => C ): Continue { return fa._tag === 'Effect' ? fb._tag === 'Effect' ? effect(Q.crossWithC_(fa.query, fb.query, f)) : effect(Q.crossWith_(fa.query, Q.fromIO(fb.io), f)) : fb._tag === 'Effect' ? effect(pipe(Q.fromIO(fa.io), Q.crossWith(fb.query, f))) : get(I.crossWith_(fa.io, fb.io, f)) } export function crossWithC( fb: Continue, f: (a: A, b: B) => C ): (fa: Continue) => Continue { return (fa) => crossWithC_(fa, fb, f) } export function crossWithBatched_( fa: Continue, fb: Continue, f: (a: A, b: B) => C ): Continue { return fa._tag === 'Effect' ? fb._tag === 'Effect' ? effect(Q.crossWithBatched_(fa.query, fb.query, f)) : effect(Q.crossWith_(fa.query, Q.fromIO(fb.io), f)) : fb._tag === 'Effect' ? effect(pipe(Q.fromIO(fa.io), Q.crossWith(fb.query, f))) : get(I.crossWith_(fa.io, fb.io, f)) } export function crossWithBatched( fb: Continue, f: (a: A, b: B) => C ): (fa: Continue) => Continue { return (fa) => crossWithBatched_(fa, fb, f) } export function mapDataSources_(fa: Continue, f: DataSourceAspect): Continue { return matchTag_(fa, { Effect: ({ query }) => effect(Q.mapDataSources_(query, f)), Get: ({ io }) => get(io) }) } export function mapDataSources( f: DataSourceAspect ): (fa: Continue) => Continue { return (fa) => mapDataSources_(fa, f) } export function make, B>( request: A, dataSource: DataSource, ref: URef>> ): Continue { return get(pipe(ref.get, I.chain(M.match(() => I.haltMessage('TODO: Query Failure'), I.fromEither)))) } export function mapQuery_( fa: Continue, f: (a: A) => Query ): Continue { return matchTag_(fa, { Effect: ({ query }) => effect(Q.chain_(query, f)), Get: ({ io }) => effect(pipe(Q.fromIO(io), Q.chain(f))) }) } export function mapQuery( f: (a: A) => Query ): (fa: Continue) => Continue { return (fa) => mapQuery_(fa, f) } export function gives_(ra: Continue, f: Described<(r0: R0) => R>): Continue { return matchTag_(ra, { Effect: ({ query }) => effect(Q.gives_(query, f)), Get: ({ io }) => get(io) }) } export function gives(f: Described<(r0: R0) => R>): (ra: Continue) => Continue { return (ra) => gives_(ra, f) } export function runContext_(ma: Continue, queryContext: QueryContext): I.IO { return matchTag_(ma, { Effect: ({ query }) => Q.runContext_(query, queryContext), Get: ({ io }) => io }) } export function runCache_(ma: Continue, cache: Cache): I.IO { return matchTag_(ma, { Effect: ({ query }) => Q.runCache_(query, cache), Get: ({ io }) => io }) } export function runCache(cache: Cache): (ma: Continue) => I.IO { return (ma) => runCache_(ma, cache) }