Skip to content

Commit

Permalink
Schema: add head / headOrFail (#1928)
Browse files Browse the repository at this point in the history
  • Loading branch information
gcanti authored Jan 16, 2024
1 parent 112e504 commit 2530d47
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/many-dragons-smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/schema": patch
---

add head / headOrFail
28 changes: 28 additions & 0 deletions packages/schema/src/Schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3403,6 +3403,34 @@ export const itemsCount = <A>(
})
)

/**
* Get the first element of a `ReadonlyArray`, or `None` if the array is empty.
*
* @category ReadonlyArray transformations
* @since 1.0.0
*/
export const head = <I, A>(value: Schema<I, A>): Schema<ReadonlyArray<I>, Option.Option<A>> =>
transform(
array(value),
optionFromSelf(to(value)),
ReadonlyArray.head,
Option.match({ onNone: () => [], onSome: ReadonlyArray.of })
)

/**
* Get the first element of a `ReadonlyArray` or fails if the array is empty.
*
* @category ReadonlyArray transformations
* @since 1.0.0
*/
export const headOrFail = <I, A>(value: Schema<I, A>): Schema<ReadonlyArray<I>, A> =>
transformOrFail(
array(value),
to(value),
(as, _, ast) => as.length > 0 ? ParseResult.succeed(as[0]) : ParseResult.fail(ParseResult.type(ast, as)),
(a) => ParseResult.succeed(ReadonlyArray.of(a))
)

/**
* @category type id
* @since 1.0.0
Expand Down
27 changes: 27 additions & 0 deletions packages/schema/test/ReadonlyArray/head.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as S from "@effect/schema/Schema"
import * as Util from "@effect/schema/test/util"
import * as Option from "effect/Option"
import { describe, it } from "vitest"

describe("ReadonlyArray > head", () => {
it("decoding", async () => {
const schema = S.head(S.number)
await Util.expectParseSuccess(schema, [], Option.none())
await Util.expectParseSuccess(schema, [1], Option.some(1))
await Util.expectParseFailure(
schema,
["a"],
`(ReadonlyArray<number> <-> Option<number>)
└─ From side transformation failure
└─ ReadonlyArray<number>
└─ [0]
└─ Expected a number, actual "a"`
)
})

it("encoding", async () => {
const schema = S.head(S.number)
await Util.expectEncodeSuccess(schema, Option.none(), [])
await Util.expectEncodeSuccess(schema, Option.some(1), [1])
})
})
31 changes: 31 additions & 0 deletions packages/schema/test/ReadonlyArray/headOrFail.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as S from "@effect/schema/Schema"
import * as Util from "@effect/schema/test/util"
import { describe, it } from "vitest"

describe("ReadonlyArray > headOrFail", () => {
it("decoding", async () => {
const schema = S.headOrFail(S.number)
await Util.expectParseSuccess(schema, [1], 1)
await Util.expectParseFailure(
schema,
[],
`(ReadonlyArray<number> <-> number)
└─ Transformation process failure
└─ Expected (ReadonlyArray<number> <-> number), actual []`
)
await Util.expectParseFailure(
schema,
["a"],
`(ReadonlyArray<number> <-> number)
└─ From side transformation failure
└─ ReadonlyArray<number>
└─ [0]
└─ Expected a number, actual "a"`
)
})

it("encoding", async () => {
const schema = S.headOrFail(S.number)
await Util.expectEncodeSuccess(schema, 1, [1])
})
})

0 comments on commit 2530d47

Please sign in to comment.