1import { assert, describe, it } from "@effect/vitest"
2import { Schema, SchemaGetter } from "effect"
3
4function makeValueCodec<Type extends string, Value>(type: Type, value: Schema.Codec<Value>) {
5 return value.pipe(
6 Schema.encodeTo(Schema.Struct({ type: Schema.tag(type), value }), {
7 decode: SchemaGetter.transform((encoded: { readonly type: Type; readonly value: Value }) => encoded.value),
8 encode: SchemaGetter.transform((value: Value) => ({ type, value }))
9 })
10 )
11}
12
13const LiteralValueSchema = Schema.Union([
14 makeValueCodec("string", Schema.String),
15 makeValueCodec("number", Schema.Finite),
16 makeValueCodec("bigint", Schema.BigInt),
17 makeValueCodec("boolean", Schema.Boolean)
18])
19
20const EnumValueSchema = Schema.Union([
21 makeValueCodec("string", Schema.String),
22 makeValueCodec("number", Schema.Number)
23])
24
25const PropertyNameSchema = Schema.Union([
26 makeValueCodec("string", Schema.String),
27 makeValueCodec("number", Schema.Number),
28 makeValueCodec("symbol", Schema.Symbol)
29])
30
31function assertRoundtrips<A>(
32 schema: Schema.Codec<A, unknown>,
33 cases: ReadonlyArray<readonly [value: A, encoded: Schema.StringTree]>
34): void {
35 const codec = Schema.toCodecStringTree(schema)
36 const encode = Schema.encodeSync(codec)
37 const decode = Schema.decodeSync(codec)
38 for (const [value, encoded] of cases) {
39 assert.deepStrictEqual(encode(value), encoded)
40 assert.deepStrictEqual(decode(encoded), value)
41 }
42}
43
44describe("SchemaRepresentation encoded values through StringTree", () => {
45 it("preserves literal value types", () => {
46 assertRoundtrips(LiteralValueSchema, [
47 ["1", { type: "string", value: "1" }],
48 [1, { type: "number", value: "1" }],
49 [1n, { type: "bigint", value: "1" }],
50 ["true", { type: "string", value: "true" }],
51 [true, { type: "boolean", value: "true" }]
52 ])
53 })
54
55 it("preserves enum value types", () => {
56 assertRoundtrips(EnumValueSchema, [
57 ["NaN", { type: "string", value: "NaN" }],
58 [Number.NaN, { type: "number", value: "NaN" }],
59 ["Infinity", { type: "string", value: "Infinity" }],
60 [Number.POSITIVE_INFINITY, { type: "number", value: "Infinity" }]
61 ])
62 })
63
64 it("preserves property name types", () => {
65 assertRoundtrips(PropertyNameSchema, [
66 ["1", { type: "string", value: "1" }],
67 [1, { type: "number", value: "1" }],
68 ["Symbol(key)", { type: "string", value: "Symbol(key)" }],
69 [Symbol.for("key"), { type: "symbol", value: "Symbol(key)" }]
70 ])
71 })
72})