Skip to content

Commit

Permalink
Schema: add eitherFromUnion (#1794)
Browse files Browse the repository at this point in the history
  • Loading branch information
matheuspuel authored Jan 9, 2024
1 parent 87f7ef2 commit 210d27e
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/silly-coins-train.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/schema": patch
---

add eitherFromUnion
30 changes: 30 additions & 0 deletions packages/schema/src/Schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3718,6 +3718,36 @@ export const either = <IE, E, IA, A>(
})
)

/**
* @example
* import * as Schema from "@effect/schema/Schema"
*
* // Schema<"A" | "E", Either<"E", "A">>
* Schema.eitherFromUnion(Schema.literal("A"), Schema.literal("E"))
*
* @category Either transformations
* @since 1.0.0
*/
export const eitherFromUnion = <EI, EA, AI, AA>(
left: Schema<EI, EA>,
right: Schema<AI, AA>
): Schema<EI | AI, Either.Either<EA, AA>> => {
return transformOrFail(
union(from(right), from(left)),
eitherFromSelf(to(left), to(right)),
(value, options) =>
ParseResult.orElse(
ParseResult.map(Parser.parse(right)(value, options), Either.right),
() => ParseResult.map(Parser.parse(left)(value, options), Either.left)
),
(value, options) =>
Either.match(value, {
onLeft: (_) => Parser.encode(left)(_, options),
onRight: (_) => Parser.encode(right)(_, options)
})
)
}

const isMap = (u: unknown): u is Map<unknown, unknown> => u instanceof Map

const readonlyMapArbitrary = <K, V>(
Expand Down
103 changes: 103 additions & 0 deletions packages/schema/test/Either/eitherFromUnion.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import * as S from "@effect/schema/Schema"
import * as Util from "@effect/schema/test/util"
import * as E from "effect/Either"
import { describe, expect, it } from "vitest"

describe("Either/eitherFromUnion", () => {
it("property tests", () => {
Util.roundtrip(S.eitherFromUnion(S.string, S.number))
})

it("decoding success", async () => {
const schema = S.eitherFromUnion(S.DateFromString, S.NumberFromString)
await Util.expectParseSuccess(schema, "1970-01-01T00:00:00.000Z", E.left(new Date(0)))
await Util.expectParseSuccess(schema, "1", E.right(1))

expect(E.isEither(S.decodeSync(schema)("1970-01-01T00:00:00.000Z"))).toEqual(true)
expect(E.isEither(S.decodeSync(schema)("1"))).toEqual(true)
})

it("decoding error", async () => {
const schema = S.eitherFromUnion(S.number, S.string)
await Util.expectParseFailure(
schema,
undefined,
`(string | number <-> Either<number, string>)
└─ From side transformation failure
└─ string | number
├─ Union member
│ └─ Expected a string, actual undefined
└─ Union member
└─ Expected a number, actual undefined`
)
})

it("decoding prefer right", async () => {
const schema = S.eitherFromUnion(S.NumberFromString, S.NumberFromString)
await Util.expectParseSuccess(schema, "1", E.right(1))
})

it("encoding success", async () => {
const schema = S.eitherFromUnion(S.DateFromString, S.NumberFromString)
await Util.expectEncodeSuccess(schema, E.left(new Date(0)), "1970-01-01T00:00:00.000Z")
await Util.expectEncodeSuccess(schema, E.right(1), "1")
})

it("encoding error", async () => {
const schema = S.eitherFromUnion(
S.compose(S.DateFromString, S.unknown),
S.compose(S.NumberFromString, S.unknown)
)
await Util.expectEncodeFailure(
schema,
E.left(undefined),
`(string <-> Either<unknown, unknown>)
└─ Transformation process failure
└─ (DateFromString <-> unknown)
└─ From side transformation failure
└─ DateFromString
└─ To side transformation failure
└─ Expected DateFromSelf, actual undefined`
)
await Util.expectEncodeFailure(
schema,
E.right(undefined),
`(string <-> Either<unknown, unknown>)
└─ Transformation process failure
└─ (NumberFromString <-> unknown)
└─ From side transformation failure
└─ NumberFromString
└─ To side transformation failure
└─ Expected a number, actual undefined`
)
})

it("encoding don't overlap", async () => {
const schema = S.eitherFromUnion(
S.compose(S.DateFromString, S.unknown),
S.compose(S.NumberFromString, S.unknown)
)
await Util.expectEncodeFailure(
schema,
E.left(1),
`(string <-> Either<unknown, unknown>)
└─ Transformation process failure
└─ (DateFromString <-> unknown)
└─ From side transformation failure
└─ DateFromString
└─ To side transformation failure
└─ Expected DateFromSelf, actual 1`
)
await Util.expectEncodeFailure(
schema,
E.right(new Date(0)),
`(string <-> Either<unknown, unknown>)
└─ Transformation process failure
└─ (NumberFromString <-> unknown)
└─ From side transformation failure
└─ NumberFromString
└─ To side transformation failure
└─ Expected a number, actual ${new Date(0).toString()}`
)
})
})

0 comments on commit 210d27e

Please sign in to comment.