395 lines
12 KiB
TypeScript
395 lines
12 KiB
TypeScript
import {
|
|
Cause,
|
|
Data,
|
|
Exit,
|
|
HashMap,
|
|
Option,
|
|
Predicate,
|
|
Record,
|
|
Result,
|
|
Schema,
|
|
SchemaTransformation,
|
|
SchemaUtils
|
|
} from "effect"
|
|
import { describe, it } from "vitest"
|
|
import { assertNone, assertSome, deepStrictEqual, strictEqual, throws } from "../utils/assert.ts"
|
|
|
|
class Value extends Schema.Class<Value, { readonly brand: unique symbol }>("Value")({
|
|
a: Schema.DateValid
|
|
}) {}
|
|
|
|
function addOne(date: Date): Date {
|
|
const time = date.getTime()
|
|
if (time === -1) {
|
|
return new Date("")
|
|
}
|
|
return new Date(time + 1)
|
|
}
|
|
|
|
function addTwo(date: Date): Date {
|
|
const time = date.getTime()
|
|
return new Date(time + 2)
|
|
}
|
|
|
|
describe("Optic generation", () => {
|
|
it("overrideToCodecIso", () => {
|
|
const schema = Schema.URL.pipe(Schema.overrideToCodecIso(Schema.String, SchemaTransformation.urlFromString))
|
|
const optic = Schema.toIso(schema)
|
|
const modify = optic.modify((s) => s + "test")
|
|
deepStrictEqual(modify(new URL("https://example.com")), new URL("https://example.com/test"))
|
|
})
|
|
|
|
describe("toIso", () => {
|
|
describe("Class", () => {
|
|
it("Class", () => {
|
|
class A extends Schema.Class<A>("A")({ value: Value }) {}
|
|
class B extends Schema.Class<B>("B")({ a: A }) {}
|
|
|
|
const schema = B
|
|
const optic = Schema.toIso(schema).key("a").key("value").key("a")
|
|
const modify = optic.modify(addOne)
|
|
|
|
deepStrictEqual(
|
|
modify(B.make({ a: A.make({ value: Value.make({ a: new Date(0) }) }) })),
|
|
B.make({ a: A.make({ value: Value.make({ a: new Date(1) }) }) })
|
|
)
|
|
})
|
|
})
|
|
|
|
it("toType(Class)", () => {
|
|
const schema = Schema.toType(Value)
|
|
const optic = Schema.toIso(schema).key("a")
|
|
const modify = optic.modify(addOne)
|
|
|
|
deepStrictEqual(modify(Value.make({ a: new Date(0) })), Value.make({ a: new Date(1) }))
|
|
})
|
|
|
|
it("toEncoded(Class)", () => {
|
|
const schema = Schema.toEncoded(Value)
|
|
const optic = Schema.toIso(schema).key("a")
|
|
const modify = optic.modify(addOne)
|
|
|
|
deepStrictEqual(modify({ a: new Date(0) }), { a: new Date(1) })
|
|
})
|
|
|
|
describe("brand", () => {
|
|
it("Number & isPositive", () => {
|
|
const schema = Schema.Number.check(Schema.isGreaterThan(0)).pipe(Schema.brand("isPositive"))
|
|
const optic = Schema.toIso(schema)
|
|
const modify = optic.modify((n) => schema.make(n - 1))
|
|
|
|
strictEqual(modify(schema.make(2)), 1)
|
|
throws(() => modify(schema.make(1)), "Expected a value greater than 0, got 0")
|
|
})
|
|
})
|
|
|
|
it("Tuple", () => {
|
|
const schema = Schema.Tuple([Value, Schema.optionalKey(Value)])
|
|
const optic = Schema.toIso(schema).key("0").key("a")
|
|
const modify = optic.modify(addOne)
|
|
|
|
deepStrictEqual(
|
|
modify([Value.make({ a: new Date(0) })]),
|
|
[Value.make({ a: new Date(1) })]
|
|
)
|
|
})
|
|
|
|
it("Array", () => {
|
|
const schema = Schema.Array(Value)
|
|
const optic = Schema.toIso(schema)
|
|
const item = Schema.toIsoFocus(Value).key("a")
|
|
const modify = optic.modify((as) => as.map(item.modify(addOne)))
|
|
|
|
deepStrictEqual(modify([Value.make({ a: new Date(0) })]), [Value.make({ a: new Date(1) })])
|
|
})
|
|
|
|
it("NonEmptyArray", () => {
|
|
const schema = Schema.NonEmptyArray(Value)
|
|
const optic = Schema.toIso(schema)
|
|
const item = Schema.toIsoFocus(Value).key("a")
|
|
const modify = optic.modify(([a, ...rest]) => [item.modify(addOne)(a), ...rest.map(item.modify(addTwo))])
|
|
|
|
deepStrictEqual(
|
|
modify([
|
|
Value.make({ a: new Date(0) }),
|
|
Value.make({ a: new Date(1) }),
|
|
Value.make({ a: new Date(2) })
|
|
]),
|
|
[
|
|
Value.make({ a: new Date(1) }),
|
|
Value.make({ a: new Date(3) }),
|
|
Value.make({ a: new Date(4) })
|
|
]
|
|
)
|
|
})
|
|
|
|
it("TupleWithRest", () => {
|
|
const schema = Schema.TupleWithRest(Schema.Tuple([Value]), [Value])
|
|
const optic = Schema.toIso(schema)
|
|
const item = Schema.toIsoFocus(Value).key("a")
|
|
const modify = optic.modify((
|
|
[value, ...rest]
|
|
) => [item.modify(addOne)(value), ...rest.map((r) => item.modify(addTwo)(r))])
|
|
|
|
deepStrictEqual(
|
|
modify([
|
|
Value.make({ a: new Date(0) }),
|
|
Value.make({ a: new Date(1) }),
|
|
Value.make({ a: new Date(2) })
|
|
]),
|
|
[
|
|
Value.make({ a: new Date(1) }),
|
|
Value.make({ a: new Date(3) }),
|
|
Value.make({ a: new Date(4) })
|
|
]
|
|
)
|
|
})
|
|
|
|
it("Struct", () => {
|
|
const schema = Schema.Struct({
|
|
value: Value,
|
|
optionalValue: Schema.optionalKey(Value)
|
|
})
|
|
const optic = Schema.toIso(schema).key("value").key("a")
|
|
const modify = optic.modify(addOne)
|
|
|
|
deepStrictEqual(
|
|
modify({
|
|
value: Value.make({ a: new Date(0) })
|
|
}),
|
|
{
|
|
value: Value.make({ a: new Date(1) })
|
|
}
|
|
)
|
|
deepStrictEqual(
|
|
modify({
|
|
value: Value.make({ a: new Date(0) }),
|
|
optionalValue: Value.make({ a: new Date(2) })
|
|
}),
|
|
{
|
|
value: Value.make({ a: new Date(1) }),
|
|
optionalValue: Value.make({ a: new Date(2) })
|
|
}
|
|
)
|
|
})
|
|
|
|
it("Record", () => {
|
|
const schema = Schema.Record(Schema.String, Value)
|
|
const optic = Schema.toIso(schema)
|
|
const item = Schema.toIsoFocus(Value).key("a")
|
|
const modify = optic.modify((rec) => Record.map(rec, item.modify(addOne)))
|
|
|
|
deepStrictEqual(
|
|
modify({
|
|
a: Value.make({ a: new Date(0) }),
|
|
b: Value.make({ a: new Date(1) })
|
|
}),
|
|
{
|
|
a: Value.make({ a: new Date(1) }),
|
|
b: Value.make({ a: new Date(2) })
|
|
}
|
|
)
|
|
})
|
|
|
|
it("StructWithRest", () => {
|
|
const schema = Schema.StructWithRest(
|
|
Schema.Struct({ a: Value }),
|
|
[Schema.Record(Schema.String, Value)]
|
|
)
|
|
const optic = Schema.toIso(schema)
|
|
const item = Schema.toIsoFocus(Value).key("a")
|
|
const modify = optic.modify(({ a, ...rest }) => ({
|
|
a: item.modify(addOne)(a),
|
|
...Record.map(rest, item.modify(addTwo))
|
|
}))
|
|
|
|
deepStrictEqual(
|
|
modify({ a: Value.make({ a: new Date(0) }), b: Value.make({ a: new Date(1) }) }),
|
|
{ a: Value.make({ a: new Date(1) }), b: Value.make({ a: new Date(3) }) }
|
|
)
|
|
})
|
|
|
|
it("Union", () => {
|
|
const schema = Schema.Union([Schema.String, Value])
|
|
const optic = Schema.toIso(schema)
|
|
const item = Schema.toIsoFocus(Value).key("a")
|
|
const modify = optic.modify((x) => Predicate.isString(x) ? x : item.modify(addOne)(x))
|
|
|
|
deepStrictEqual(modify("a"), "a")
|
|
deepStrictEqual(modify(Value.make({ a: new Date(0) })), Value.make({ a: new Date(1) }))
|
|
})
|
|
|
|
it("suspend", () => {
|
|
interface A {
|
|
readonly a: Value
|
|
readonly as: ReadonlyArray<A>
|
|
}
|
|
interface AIso {
|
|
readonly a: typeof Value["Iso"]
|
|
readonly as: ReadonlyArray<AIso>
|
|
}
|
|
const schema = Schema.Struct({
|
|
a: Value,
|
|
as: Schema.Array(Schema.suspend((): Schema.Optic<A, AIso> => schema))
|
|
})
|
|
const optic = Schema.toIso(schema)
|
|
const item = Schema.toIsoFocus(Value).key("a")
|
|
const f = ({ a, as }: AIso): AIso => ({
|
|
a: item.modify(addOne)(a),
|
|
as: as.map(f)
|
|
})
|
|
const modify = optic.modify(f)
|
|
|
|
deepStrictEqual(
|
|
modify({ a: Value.make({ a: new Date(0) }), as: [{ a: Value.make({ a: new Date(1) }), as: [] }] }),
|
|
{
|
|
a: Value.make({ a: new Date(1) }),
|
|
as: [{ a: Value.make({ a: new Date(2) }), as: [] }]
|
|
}
|
|
)
|
|
})
|
|
|
|
it("flip(schema)", () => {
|
|
const schema = Schema.flip(Value)
|
|
const optic = Schema.toIso(schema).key("a")
|
|
const modify = optic.modify(addOne)
|
|
|
|
deepStrictEqual(modify(Value.make({ a: new Date(0) })), { a: new Date(1) })
|
|
})
|
|
|
|
it("flip(flip(schema))", () => {
|
|
const schema = Schema.flip(Schema.flip(Value))
|
|
const optic = Schema.toIso(schema).key("a")
|
|
const modify = optic.modify(addOne)
|
|
|
|
deepStrictEqual(modify(Value.make({ a: new Date(0) })), Value.make({ a: new Date(1) }))
|
|
})
|
|
|
|
it("Opaque", () => {
|
|
class S extends Schema.Opaque<S>()(Schema.Struct({ a: Schema.Date })) {}
|
|
const schema = S
|
|
const optic = Schema.toIso(schema).key("a")
|
|
const modify = optic.modify(addOne)
|
|
|
|
deepStrictEqual(modify({ a: new Date(0) }), { a: new Date(1) })
|
|
})
|
|
|
|
it("Option", () => {
|
|
const schema = Schema.Option(Value)
|
|
const optic = Schema.toIso(schema).tag("Some").key("value").key("a")
|
|
const modify = optic.modify(addOne)
|
|
|
|
assertSome(
|
|
modify(Option.some(Value.make({ a: new Date(0) }))),
|
|
Value.make({ a: new Date(1) })
|
|
)
|
|
assertNone(modify(Option.none()))
|
|
})
|
|
|
|
it("Result", () => {
|
|
const schema = Schema.Result(Value, Value)
|
|
const optic = Schema.toIso(schema).tag("Success").key("success").key("a")
|
|
const modify = optic.modify(addOne)
|
|
|
|
deepStrictEqual(
|
|
modify(Result.succeed(Value.make({ a: new Date(0) }))),
|
|
Result.succeed(Value.make({ a: new Date(1) }))
|
|
)
|
|
})
|
|
|
|
it("CauseReason", () => {
|
|
const schema = Schema.CauseReason(Value, Schema.Defect())
|
|
const optic = Schema.toIso(schema).tag("Fail").key("error").key("a")
|
|
const modify = optic.modify(addOne)
|
|
|
|
deepStrictEqual(
|
|
modify(Cause.makeFailReason(Value.make({ a: new Date(0) }))),
|
|
Cause.makeFailReason(Value.make({ a: new Date(1) }))
|
|
)
|
|
})
|
|
|
|
it("Cause", () => {
|
|
const schema = Schema.Cause(Value, Value)
|
|
const optic = Schema.toIso(schema)
|
|
const failure = Schema.toIsoFocus(Schema.CauseReason(Value, Value)).tag("Fail").key("error").key("a")
|
|
const modify = optic.modify((failures) => failures.map(failure.modify(addOne)))
|
|
|
|
deepStrictEqual(
|
|
modify(Cause.fail(Value.make({ a: new Date(0) }))),
|
|
Cause.fail(Value.make({ a: new Date(1) }))
|
|
)
|
|
})
|
|
|
|
it("Error", () => {
|
|
const schema = Schema.Error()
|
|
const optic = Schema.toIso(schema)
|
|
const modify = optic.modify((e) => new Error(e.message + "!"))
|
|
|
|
deepStrictEqual(modify(new Error("a")), new Error("a!"))
|
|
})
|
|
|
|
it("Exit", () => {
|
|
const schema = Schema.Exit(Value, Schema.Error(), Schema.Defect())
|
|
const optic = Schema.toIso(schema).tag("Success").key("value").key("a")
|
|
const modify = optic.modify(addOne)
|
|
|
|
deepStrictEqual(
|
|
modify(Exit.succeed(Value.make({ a: new Date(0) }))),
|
|
Exit.succeed(Value.make({ a: new Date(1) }))
|
|
)
|
|
})
|
|
|
|
it("ReadonlySet", () => {
|
|
const schema = Schema.ReadonlySet(Value)
|
|
const optic = Schema.toIso(schema)
|
|
const item = Schema.toIsoFocus(Value).key("a")
|
|
const modify = optic.modify((as) => as.map(item.modify(addOne)))
|
|
|
|
deepStrictEqual(
|
|
modify(new Set([Value.make({ a: new Date(0) })])),
|
|
new Set([Value.make({ a: new Date(1) })])
|
|
)
|
|
})
|
|
|
|
it("ReadonlyMap", () => {
|
|
const schema = Schema.ReadonlyMap(Schema.String, Value)
|
|
const optic = Schema.toIso(schema)
|
|
const entry = Schema.toIsoFocus(Schema.Tuple([Schema.String, Value])).key("1").key("a")
|
|
const modify = optic.modify((entries) => entries.map(([key, value]) => entry.modify(addOne)([key, value])))
|
|
|
|
deepStrictEqual(
|
|
modify(new Map([["a", Value.make({ a: new Date(0) })]])),
|
|
new Map([["a", Value.make({ a: new Date(1) })]])
|
|
)
|
|
})
|
|
|
|
it("HashMap", () => {
|
|
const schema = Schema.HashMap(Schema.String, Value)
|
|
const optic = Schema.toIso(schema)
|
|
const entry = Schema.toIsoFocus(Schema.Tuple([Schema.String, Value])).key("1").key("a")
|
|
const modify = optic.modify((entries) => entries.map(([key, value]) => entry.modify(addOne)([key, value])))
|
|
|
|
deepStrictEqual(
|
|
HashMap.toEntries(modify(HashMap.make(["a", Value.make({ a: new Date(0) })]))),
|
|
HashMap.toEntries(HashMap.make(["a", Value.make({ a: new Date(1) })]))
|
|
)
|
|
})
|
|
|
|
it("getNativeClassSchema", () => {
|
|
const Props = Schema.Struct({
|
|
message: Schema.String
|
|
})
|
|
class Err extends Data.Error<typeof Props.Type> {
|
|
constructor(props: typeof Props.Type) {
|
|
super(Props.make(props))
|
|
}
|
|
}
|
|
const schema = SchemaUtils.getNativeClassSchema(Err, { encoding: Props })
|
|
const optic = Schema.toIso(schema)
|
|
const modify = optic.modify((e) => new Err({ message: e.message + "!" }))
|
|
|
|
deepStrictEqual(modify(new Err({ message: "a" })), new Err({ message: "a!" }))
|
|
})
|
|
})
|
|
})
|