Skip to content
This repository has been archived by the owner on Jul 16, 2024. It is now read-only.

Commit

Permalink
Refactor ParseResult module
Browse files Browse the repository at this point in the history
  • Loading branch information
gcanti committed Dec 27, 2023
1 parent 0889af0 commit 718182b
Show file tree
Hide file tree
Showing 32 changed files with 188 additions and 112 deletions.
8 changes: 8 additions & 0 deletions .changeset/gentle-readers-call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@effect/schema": minor
---

Refactor `ParseResult` module:

- rename `UnionMember` to `Member`
- ast `ast` field to `Member`
2 changes: 1 addition & 1 deletion src/ArrayFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const format = (self: ParseIssue, path: ReadonlyArray<PropertyKey> = []): Array<
return ReadonlyArray.flatMap(self.errors, (e) => format(e, [...path, self.key]))
case "Index":
return ReadonlyArray.flatMap(self.errors, (e) => format(e, [...path, self.index]))
case "UnionMember":
case "Member":
return ReadonlyArray.flatMap(self.errors, (e) => format(e, path))
case "Missing":
return [{ _tag, path, message: "Missing key or index" }]
Expand Down
36 changes: 20 additions & 16 deletions src/ParseResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,14 @@ export const parseError = (
* @since 1.0.0
*/
export type ParseIssue =
| Type
// context
| Index
| Key
| Member
// primitives
| Type
| Missing
| Unexpected
| UnionMember
| Forbidden

/**
Expand All @@ -80,16 +82,6 @@ export interface Type {
readonly message: Option.Option<string>
}

/**
* The `Forbidden` variant of the `ParseIssue` type represents an error that occurs when an Effect is encounter but disallowed from execution.
*
* @category model
* @since 1.0.0
*/
export interface Forbidden {
readonly _tag: "Forbidden"
}

/**
* @category constructors
* @since 1.0.0
Expand All @@ -101,6 +93,16 @@ export const type = (expected: AST.AST, actual: unknown, message?: string): Type
message: Option.fromNullable(message)
})

/**
* The `Forbidden` variant of the `ParseIssue` type represents an error that occurs when an Effect is encounter but disallowed from execution.
*
* @category model
* @since 1.0.0
*/
export interface Forbidden {
readonly _tag: "Forbidden"
}

/**
* @category constructors
* @since 1.0.0
Expand Down Expand Up @@ -199,18 +201,20 @@ export const unexpected = (
* @category model
* @since 1.0.0
*/
export interface UnionMember {
readonly _tag: "UnionMember"
export interface Member {
readonly _tag: "Member"
readonly ast: AST.AST
readonly errors: ReadonlyArray.NonEmptyReadonlyArray<ParseIssue>
}

/**
* @category constructors
* @since 1.0.0
*/
export const unionMember = (
export const member = (
ast: AST.AST,
errors: ReadonlyArray.NonEmptyReadonlyArray<ParseIssue>
): UnionMember => ({ _tag: "UnionMember", errors })
): Member => ({ _tag: "Member", ast, errors })

/**
* @category constructors
Expand Down
7 changes: 4 additions & 3 deletions src/Parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -843,7 +843,8 @@ const go = (ast: AST.AST, isDecoding: boolean): Parser<any, any> => {
}

for (let i = 0; i < candidates.length; i++) {
const pr = map.get(candidates[i])!(input, options)
const candidate = candidates[i]
const pr = map.get(candidate)!(input, options)
// the members of a union are ordered based on which one should be decoded first,
// therefore if one member has added a task, all subsequent members must
// also add a task to the queue even if they are synchronous
Expand All @@ -852,7 +853,7 @@ const go = (ast: AST.AST, isDecoding: boolean): Parser<any, any> => {
if (Either.isRight(eu)) {
return ParseResult.succeed(eu.right)
} else {
es.push([stepKey++, ParseResult.unionMember(eu.left.errors)])
es.push([stepKey++, ParseResult.member(candidate, eu.left.errors)])
}
} else {
const nk = stepKey++
Expand All @@ -869,7 +870,7 @@ const go = (ast: AST.AST, isDecoding: boolean): Parser<any, any> => {
if (Either.isRight(t)) {
state.finalResult = ParseResult.succeed(t.right)
} else {
state.es.push([nk, ParseResult.unionMember(t.left.errors)])
state.es.push([nk, ParseResult.member(candidate, t.left.errors)])
}
return Effect.unit
})
Expand Down
15 changes: 9 additions & 6 deletions src/TreeFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,11 @@ export const formatExpected = (ast: AST.AST): string => {
case "Tuple":
return Option.getOrElse(getExpected(ast), () => "<anonymous tuple or array schema>")
case "TypeLiteral":
return Option.getOrElse(getExpected(ast), () => "<anonymous type literal schema>")
return Option.getOrElse(getExpected(ast), () => "<anonymous type literal or record schema>")
case "Enums":
return Option.getOrElse(
getExpected(ast),
() => ast.enums.map((_, value) => JSON.stringify(value)).join(" | ")
() => `<anonymous enum ${ast.enums.map((_, value) => JSON.stringify(value)).join(" | ")}>`
)
case "Suspend":
return Option.getOrElse(getExpected(ast), () => "<anonymous suspended schema>")
Expand All @@ -130,13 +130,13 @@ export const formatExpected = (ast: AST.AST): string => {
case "Transform":
return Option.getOrElse(
getExpected(ast),
() => `${formatExpected(ast.from)} <-> ${formatExpected(ast.to)}`
() => `<anonymous transformation ${formatExpected(ast.from)} <-> ${formatExpected(ast.to)}>`
)
}
}

const isCollapsible = (es: Forest<string>, errors: NonEmptyReadonlyArray<ParseIssue>): boolean =>
es.length === 1 && es[0].forest.length !== 0 && errors[0]._tag !== "UnionMember"
es.length === 1 && es[0].forest.length !== 0 && errors[0]._tag !== "Member"

/** @internal */
export const getMessage = (e: Type) =>
Expand Down Expand Up @@ -174,7 +174,10 @@ const go = (e: ParseIssue): Tree<string> => {
}
case "Missing":
return make("is missing")
case "UnionMember":
return make("union member", e.errors.map(go))
case "Member":
return make(
`union member: ${formatExpected(e.ast)}`,
e.errors.map(go)
)
}
}
2 changes: 1 addition & 1 deletion test/ArrayFormatter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ describe("ArrayFormatter", () => {
}])
})

it("UnionMember", () => {
it("Member", () => {
const schema = S.union(S.string, S.number)
expectIssues(schema, null, [{
_tag: "Type",
Expand Down
2 changes: 1 addition & 1 deletion test/BigDecimal/BigDecimal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe("BigDecimal/BigDecimal", () => {
await Util.expectParseFailure(
schema,
"abc",
`Expected string <-> BigDecimal, actual "abc"`
`Expected <anonymous transformation string <-> BigDecimal>, actual "abc"`
)
})

Expand Down
2 changes: 1 addition & 1 deletion test/Cause/cause.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ describe("Cause/cause", () => {
await Util.expectParseFailure(
schema,
null,
`Expected <anonymous type literal schema>, actual null`
`Expected <anonymous type literal or record schema>, actual null`
)
await Util.expectParseFailure(
schema,
Expand Down
4 changes: 2 additions & 2 deletions test/Cause/causeFromSelf.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ describe("Cause/causeFromSelf", () => {
await Util.expectParseFailure(
schema,
Cause.fail("a"),
`union member: /error Expected string <-> number, actual "a"`
`union member: /error Expected <anonymous transformation string <-> number>, actual "a"`
)
await Util.expectParseFailure(
schema,
Cause.parallel(Cause.die("error"), Cause.fail("a")),
`union member: /right union member: /error Expected string <-> number, actual "a"`
`union member: /right union member: /error Expected <anonymous transformation string <-> number>, actual "a"`
)
})

Expand Down
2 changes: 1 addition & 1 deletion test/Chunk/chunkFromSelf.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe("Chunk/chunkFromSelf", () => {
await Util.expectParseFailure(
schema,
C.fromIterable(["1", "a", "3"]),
`/1 Expected string <-> number, actual "a"`
`/1 Expected <anonymous transformation string <-> number>, actual "a"`
)
})

Expand Down
12 changes: 8 additions & 4 deletions test/Parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe("Parser", () => {
expect(P.parseSync(schema)("1")).toEqual(1)
expect(() => P.parseSync(schema)("a")).toThrow(
new Error(`error(s) found
└─ Expected string <-> number, actual "a"`)
└─ Expected <anonymous transformation string <-> number>, actual "a"`)
)
})

Expand All @@ -36,7 +36,9 @@ describe("Parser", () => {
it("parseEither", () => {
const schema = S.NumberFromString
expect(P.parseEither(schema)("1")).toEqual(E.right(1))
expect(P.parseEither(schema)("a")).toEqual(E.left(PR.parseError([PR.type(schema.ast, "a")])))
expect(P.parseEither(schema)("a")).toEqual(
E.left(PR.parseError([PR.type(schema.ast, "a")]))
)
})

it("parsePromise", async () => {
Expand All @@ -60,7 +62,7 @@ describe("Parser", () => {
expect(P.decodeSync(schema)("1")).toEqual(1)
expect(() => P.decodeSync(schema)("a")).toThrow(
new Error(`error(s) found
└─ Expected string <-> number, actual "a"`)
└─ Expected <anonymous transformation string <-> number>, actual "a"`)
)
})

Expand All @@ -73,7 +75,9 @@ describe("Parser", () => {
it("decodeEither", () => {
const schema = S.NumberFromString
expect(P.decodeEither(schema)("1")).toEqual(E.right(1))
expect(P.decodeEither(schema)("a")).toEqual(E.left(PR.parseError([PR.type(schema.ast, "a")])))
expect(P.decodeEither(schema)("a")).toEqual(
E.left(PR.parseError([PR.type(schema.ast, "a")]))
)
})

it("decodePromise", async () => {
Expand Down
2 changes: 1 addition & 1 deletion test/ReadonlyMap/readonlyMapFromSelf.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe("ReadonlyMap/readonlyMapFromSelf", () => {
await Util.expectParseFailure(
schema,
new Map([["1", "a"], ["a", "b"]]),
`/1 /0 Expected string <-> number, actual "a"`
`/1 /0 Expected <anonymous transformation string <-> number>, actual "a"`
)
})

Expand Down
2 changes: 1 addition & 1 deletion test/ReadonlySet/readonlySetFromSelf.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describe("ReadonlySet/readonlySetFromSelf", () => {
await Util.expectParseFailure(
schema,
new Set(["1", "a", "3"]),
`/1 Expected string <-> number, actual "a"`
`/1 Expected <anonymous transformation string <-> number>, actual "a"`
)
})

Expand Down
6 changes: 3 additions & 3 deletions test/Schema/PropertySignatureTransformations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe("Schema/PropertySignatureTransformations", () => {
await Util.expectParseFailure(
transform,
{ a: "a" },
`/a Expected string <-> number, actual "a"`
`/a Expected <anonymous transformation string <-> number>, actual "a"`
)

await Util.expectEncodeSuccess(transform, { a: 1 }, { a: "1" })
Expand Down Expand Up @@ -61,7 +61,7 @@ describe("Schema/PropertySignatureTransformations", () => {
await Util.expectParseFailure(
transform,
{ a: "a" },
`/a Expected string <-> number, actual "a"`
`/a Expected <anonymous transformation string <-> number>, actual "a"`
)

await Util.expectEncodeSuccess(transform, { a: 1 }, { a: "1" })
Expand Down Expand Up @@ -93,7 +93,7 @@ describe("Schema/PropertySignatureTransformations", () => {
await Util.expectParseFailure(
transform,
{ a: "a" },
`/a Expected string <-> number, actual "a"`
`/a Expected <anonymous transformation string <-> number>, actual "a"`
)

await Util.expectEncodeSuccess(transform, { a: O.some(1) }, { a: "1" })
Expand Down
12 changes: 10 additions & 2 deletions test/Schema/compose.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,21 @@ describe("Schema/compose", async () => {
it("force decoding: (A U B) compose (B -> C)", async () => {
const schema1 = S.compose(S.union(S.null, S.string), S.NumberFromString)
await Util.expectParseSuccess(schema1, "1", 1)
await Util.expectParseFailure(schema1, "a", `Expected string <-> number, actual "a"`)
await Util.expectParseFailure(
schema1,
"a",
`Expected <anonymous transformation string <-> number>, actual "a"`
)
await Util.expectParseFailure(schema1, null, "Expected string, actual null")
const schema2 = S.union(S.null, S.string).pipe(
S.compose(S.NumberFromString)
)
await Util.expectParseSuccess(schema2, "1", 1)
await Util.expectParseFailure(schema2, "a", `Expected string <-> number, actual "a"`)
await Util.expectParseFailure(
schema2,
"a",
`Expected <anonymous transformation string <-> number>, actual "a"`
)
await Util.expectParseFailure(schema2, null, "Expected string, actual null")
})

Expand Down
6 changes: 3 additions & 3 deletions test/Schema/enums.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe("Schema/enums", () => {
await Util.expectParseFailure(
schema,
3,
`Expected 0 | 1, actual 3`
`Expected <anonymous enum 0 | 1>, actual 3`
)
})

Expand Down Expand Up @@ -47,7 +47,7 @@ describe("Schema/enums", () => {
await Util.expectParseFailure(
schema,
"Cantaloupe",
`Expected 0 | 1 | 2, actual "Cantaloupe"`
`Expected <anonymous enum 0 | 1 | 2>, actual "Cantaloupe"`
)
})

Expand All @@ -74,7 +74,7 @@ describe("Schema/enums", () => {
await Util.expectParseFailure(
schema,
"Cantaloupe",
`Expected 0 | 1 | 2, actual "Cantaloupe"`
`Expected <anonymous enum 0 | 1 | 2>, actual "Cantaloupe"`
)
})

Expand Down
6 changes: 5 additions & 1 deletion test/Schema/filter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ describe("Schema/filter", () => {
: Option.some(
ParseResult.parseError([
ParseResult.key("b", [
ParseResult.type(S.literal(o.a).ast, o.b, `should be equal to a's value ("${o.a}")`)
ParseResult.type(
S.literal(o.a).ast,
o.b,
`should be equal to a's value ("${o.a}")`
)
])
])
)
Expand Down
4 changes: 2 additions & 2 deletions test/Schema/literal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ describe("Schema/literal", () => {
schema,
null,
`error(s) found
├─ union member
├─ union member: 1
│ └─ Expected 1, actual null
└─ union member
└─ union member: "a"
└─ Expected "a", actual null`
)
})
Expand Down
4 changes: 2 additions & 2 deletions test/Schema/omit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe("Schema/omit", () => {
await Util.expectParseFailure(
schema,
null,
"Expected <anonymous type literal schema>, actual null"
"Expected <anonymous type literal or record schema>, actual null"
)
await Util.expectParseFailure(schema, { [a]: "a" }, `/b is missing`)
await Util.expectParseFailure(
Expand All @@ -38,7 +38,7 @@ describe("Schema/omit", () => {
await Util.expectParseFailure(
schema,
null,
"Expected <anonymous type literal schema>, actual null"
"Expected <anonymous type literal or record schema>, actual null"
)
await Util.expectParseFailure(schema, { a: "a" }, `/b is missing`)
})
Expand Down
Loading

0 comments on commit 718182b

Please sign in to comment.