// ets_tracing: off
import * as L from "../../Collections/Immutable/List/index.js"
import * as T from "../../Effect/index.js"
import type { Lazy } from "../../Function/index.js"
import { pipe } from "../../Function/index.js"
import * as O from "../../Option/index.js"
import * as AMD from "../AssertionMData/index.js"
import type * as AR from "../AssertionResult/index.js"
import type * as ARM from "../AssertionResultM/index.js"
import * as makeAssertionValue from "../AssertionValue/makeAssertionValue.js"
import * as BA from "../BoolAlgebra/index.js"
import * as BAM from "../BoolAlgebraM/index.js"
import * as R from "../Render/index.js"
import { apply } from "./apply.js"
import { AssertionM } from "./AssertionM.js"
/**
* Returns a new assertion that succeeds only if both assertions succeed.
*/
export function and(self: AssertionM, that: Lazy>): AssertionM {
return new (class extends AssertionM {})(
() => R.infix(R.param(self), "&&", R.param(that)),
(actual) => BAM.and_(self.runM(actual), that().runM(actual))
)
}
/**
* Returns a new assertion that succeeds if either assertion succeeds.
*/
export function or(self: AssertionM, that: Lazy>): AssertionM {
return new (class extends AssertionM {})(
() => R.infix(R.param(self), "||", R.param(that)),
(actual) => BAM.or_(self.runM(actual), that().runM(actual))
)
}
/**
* Labels this assertion with the specified string.
*/
export function label_(self: AssertionM, str: string) {
return apply(() => R.infix(R.param(self), "??", R.param(R.quoted(str))), self.runM)
}
/**
* Labels this assertion with the specified string.
*/
export function label(str: string) {
return (self: AssertionM) => label_(self, str)
}
/**
* Makes a new `AssertionM` from a pretty-printing and a function.
*/
export function makeAssertionDirect(name: string, ...params: R.RenderParam[]) {
return (run: (a: Lazy) => ARM.AssertResultM): AssertionM => {
return apply(() => R.function_(name, L.of(L.from(params))), run)
}
}
/**
* Makes a new `AssertionM` from a pretty-printing and a function.
*/
export function makeAssertionM(name: string, ...params: R.RenderParam[]) {
return (run: (a: Lazy) => T.UIO) => {
const assertion = makeAssertionDirect(
name,
...params
)((actual) => {
const actualValue = actual()
return pipe(
run(() => actualValue),
BAM.fromEffect,
BAM.chain((p) => {
const result = (): AR.AssertResult =>
p
? BA.success(
makeAssertionValue.makeAssertionValue(
assertion,
() => actualValue,
result
)
)
: BA.failure(
makeAssertionValue.makeAssertionValue(
assertion,
() => actualValue,
result
)
)
return new BAM.BoolAlgebraM(T.succeed(result()))
})
)
})
return assertion
}
}
/**
* Makes a new `AssertionM` from a pretty-printing and a function.
*/
export function makeAssertionRecM(name: string, ...params: R.RenderParam[]) {
return (assertion: AssertionM) =>
(
get: (a: Lazy) => T.Effect>,
orElse: (amd: AMD.AssertionMData) => ARM.AssertResultM = AMD.asFailureM
): AssertionM => {
const resultAssertion = () =>
makeAssertionDirect(
name,
...params
)((a) => {
const actualValue = a()
return pipe(
get(() => actualValue),
BAM.fromEffect,
BAM.chain((p): ARM.AssertResultM => {
return O.fold_(
p,
() =>
orElse(AMD.makeAssertionMData(resultAssertion(), () => actualValue)),
(b) => {
return new BAM.BoolAlgebraM(
pipe(
assertion.runM(() => b).run,
T.map((p) => {
const result = (): AR.AssertResult =>
BA.isSuccess(p)
? BA.success(
makeAssertionValue.makeAssertionValue(
assertion,
() => actualValue as unknown as B,
result
)
)
: BA.failure(
makeAssertionValue.makeAssertionValue(
assertion,
() => b,
() => p
)
)
return result()
})
)
)
}
)
})
)
})
return resultAssertion()
}
}
/**
* Makes a new assertion that negates the specified assertion.
*/
export function not(assertion: AssertionM) {
return makeAssertionDirect(
"not",
R.param(assertion)
)((_) => BAM.not(assertion.runM(_)))
}