Skip to content
This repository has been archived by the owner on Jan 6, 2025. It is now read-only.

Commit

Permalink
fix encode discriminated union with transformation (#508)
Browse files Browse the repository at this point in the history
  • Loading branch information
matheuspuel authored Oct 23, 2023
1 parent c04fbfe commit 618b1b5
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 18 deletions.
5 changes: 5 additions & 0 deletions .changeset/plenty-sheep-clean.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/schema": patch
---

fix encode discriminated union with transformation
17 changes: 10 additions & 7 deletions src/Parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,7 @@ const go = (ast: AST.AST, isDecoding: boolean): Parser<any, any> => {
}
}
case "Union": {
const searchTree = _getSearchTree(ast.types)
const searchTree = _getSearchTree(ast.types, isDecoding)
const ownKeys = Internal.ownKeys(searchTree.keys)
const len = ownKeys.length
const map = new Map<any, Parser<any, any>>()
Expand Down Expand Up @@ -942,25 +942,27 @@ const fromRefinement =

/** @internal */
export const _getLiterals = (
ast: AST.AST
ast: AST.AST,
isDecoding: boolean
): ReadonlyArray<[PropertyKey, AST.Literal]> => {
switch (ast._tag) {
case "Declaration":
return _getLiterals(ast.type)
return _getLiterals(ast.type, isDecoding)
case "TypeLiteral": {
const out: Array<[PropertyKey, AST.Literal]> = []
for (let i = 0; i < ast.propertySignatures.length; i++) {
const propertySignature = ast.propertySignatures[i]
const type = AST.from(propertySignature.type)
const type = isDecoding ? AST.from(propertySignature.type) : AST.to(propertySignature.type)
if (AST.isLiteral(type) && !propertySignature.isOptional) {
out.push([propertySignature.name, type])
}
}
return out
}
case "Refinement":
return _getLiterals(ast.from, isDecoding)
case "Transform":
return _getLiterals(ast.from)
return _getLiterals(isDecoding ? ast.from : ast.to, isDecoding)
}
return []
}
Expand All @@ -980,7 +982,8 @@ export const _getLiterals = (
* @internal
*/
export const _getSearchTree = (
members: ReadonlyArray<AST.AST>
members: ReadonlyArray<AST.AST>,
isDecoding: boolean
): {
keys: {
readonly [key: PropertyKey]: {
Expand All @@ -999,7 +1002,7 @@ export const _getSearchTree = (
const otherwise: Array<AST.AST> = []
for (let i = 0; i < members.length; i++) {
const member = members[i]
const tags = _getLiterals(member)
const tags = _getLiterals(member, isDecoding)
if (tags.length > 0) {
for (let j = 0; j < tags.length; j++) {
const [key, literal] = tags[j]
Expand Down
75 changes: 64 additions & 11 deletions test/Parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,34 @@ describe("Parser", () => {
it("encodeEither", () => {
const schema = S.NumberFromString
expect(P.encodeEither(schema)(1)).toEqual(E.right("1"))
expect(
P.encodeEither(
S.union(
S.transform(
S.struct({ _tag: S.literal("a") }),
S.struct({ _tag: S.literal("b") }),
() => ({ _tag: "b" as const }),
() => ({ _tag: "a" as const })
),
S.struct({ _tag: S.literal("c") })
)
)({ _tag: "b" })
).toEqual(E.right({ _tag: "a" }))
expect(
P.encodeEither(
S.union(
S.struct({
_tag: S.transform(
S.literal("a"),
S.literal("b"),
() => "b" as const,
() => "a" as const
)
}),
S.struct({ _tag: S.literal("c") })
)
)({ _tag: "b" })
).toEqual(E.right({ _tag: "a" }))
})

it("encodePromise", async () => {
Expand All @@ -138,16 +166,17 @@ describe("Parser", () => {
})

it("_getLiterals", () => {
expect(P._getLiterals(S.string.ast)).toEqual([])
expect(P._getLiterals(S.string.ast, true)).toEqual([])
// TypeLiteral
expect(P._getLiterals(S.struct({ _tag: S.literal("a") }).ast))
expect(P._getLiterals(S.struct({ _tag: S.literal("a") }).ast, true))
.toEqual([["_tag", AST.createLiteral("a")]])
// Refinement
expect(
P._getLiterals(
S.struct({ _tag: S.literal("a") }).pipe(
S.filter(() => true)
).ast
).ast,
true
)
).toEqual([["_tag", AST.createLiteral("a")]])
// declare
Expand All @@ -157,25 +186,49 @@ describe("Parser", () => {
[],
S.struct({ _tag: S.literal("a") }),
() => P.parse(S.struct({ _tag: S.literal("a") }))
).ast
).ast,
true
)
).toEqual([["_tag", AST.createLiteral("a")]])

// Transform
expect(
P._getLiterals(
S.struct({ radius: S.number }).pipe(S.attachPropertySignature("kind", "circle")).ast
S.struct({ radius: S.number }).pipe(S.attachPropertySignature("kind", "circle")).ast,
true
)
).toEqual([])
// Transform encode
expect(
P._getLiterals(
S.struct({ radius: S.number }).pipe(S.attachPropertySignature("kind", "circle")).ast,
false
)
).toEqual([["kind", AST.createLiteral("circle")]])
// property Transform encode
expect(
P._getLiterals(
S.struct({
_tag: S.transform(
S.literal("a"),
S.literal("b"),
() => "b" as const,
() => "a" as const
)
})
.ast,
false
)
).toEqual([["_tag", AST.createLiteral("b")]])
})

it("_getSearchTree", () => {
expect(P._getSearchTree([S.string.ast, S.number.ast])).toEqual({
expect(P._getSearchTree([S.string.ast, S.number.ast], true)).toEqual({
keys: {},
otherwise: [S.string.ast, S.number.ast]
})

expect(P._getSearchTree([S.struct({ _tag: S.literal("a") }).ast, S.number.ast])).toEqual(
expect(P._getSearchTree([S.struct({ _tag: S.literal("a") }).ast, S.number.ast], true)).toEqual(
{
keys: {
_tag: {
Expand All @@ -193,7 +246,7 @@ describe("Parser", () => {
P._getSearchTree([
S.struct({ _tag: S.literal("a") }).ast,
S.struct({ _tag: S.literal("b") }).ast
])
], true)
).toEqual({
keys: {
_tag: {
Expand All @@ -211,7 +264,7 @@ describe("Parser", () => {
P._getSearchTree([
S.struct({ a: S.literal("A"), c: S.string }).ast,
S.struct({ b: S.literal("B"), d: S.number }).ast
])
], true)
).toEqual({
keys: {
a: {
Expand All @@ -236,7 +289,7 @@ describe("Parser", () => {
S.struct({ category: S.literal("catA"), tag: S.literal("a") }).ast,
S.struct({ category: S.literal("catA"), tag: S.literal("b") }).ast,
S.struct({ category: S.literal("catA"), tag: S.literal("c") }).ast
])
], true)
).toEqual({
keys: {
category: {
Expand Down Expand Up @@ -271,7 +324,7 @@ describe("Parser", () => {
)
const types = (schema.ast as AST.Union).types
expect(
P._getSearchTree(types)
P._getSearchTree(types, true)
).toEqual({
keys: {
type: {
Expand Down

0 comments on commit 618b1b5

Please sign in to comment.