Skip to content

Commit 6020981

Browse files
authored
feat!: comprehensive errors for list schematics (#26)
1 parent 1441651 commit 6020981

3 files changed

Lines changed: 34 additions & 20 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ jobs:
5656

5757
doctests:
5858
runs-on: ubuntu-latest
59-
name: Doctests (1.14..x/25.x)
59+
name: Doctests (1.14.x/25.x)
6060

6161
steps:
6262
- uses: actions/checkout@v2
@@ -81,7 +81,7 @@ jobs:
8181

8282
formatter:
8383
runs-on: ubuntu-latest
84-
name: Formatter (1.14.x/25.x)
84+
name: Formatter (1.15.x/25.x)
8585

8686
steps:
8787
- uses: actions/checkout@v2

lib/schematic.ex

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ defmodule Schematic do
4545
}
4646
end
4747

48+
defmodule Error do
49+
@moduledoc false
50+
defstruct []
51+
end
52+
4853
@doc """
4954
Specifies that the data can be **any**thing.
5055
@@ -276,12 +281,14 @@ defmodule Schematic do
276281
@doc """
277282
Specifies that the data is a list whose items unify to the given schematic.
278283
284+
Lists whose elements do not unify return a list of `:ok` and `:error` tuples.
285+
279286
## Usage
280287
281288
```elixir
282289
iex> schematic = list(oneof([str(), int()]))
283290
iex> {:ok, ["one", 2, "three"]} = unify(schematic, ["one", 2, "three"])
284-
iex> {:error, "expected a list of either a string or an integer"} = unify(schematic, ["one", 2, :three])
291+
iex> {:error, [ok: "one", ok: 2, error: "expected either a string or an integer"]} = unify(schematic, ["one", 2, :three])
285292
```
286293
"""
287294
@spec list(t() | lazy_schematic() | literal()) :: t()
@@ -304,21 +311,18 @@ defmodule Schematic do
304311
unify:
305312
telemetry_wrap(:list, %{}, fn input, dir ->
306313
if is_list(input) do
307-
Enum.reduce_while(input, {:ok, []}, fn el, {:ok, acc} ->
314+
Enum.map_reduce(input, [], fn el, acc ->
308315
case schematic.().unify.(el, dir) do
309-
{:ok, output} ->
310-
{:cont, {:ok, [output | acc]}}
311-
312-
{:error, _error} ->
313-
{:halt, {:error, ~s|expected #{message.()}|}}
316+
{:ok, output} -> {output, [{:ok, output} | acc]}
317+
{:error, error} -> {%Error{}, [{:error, error} | acc]}
314318
end
315319
end)
316-
|> then(fn
317-
{:ok, result} ->
318-
{:ok, Enum.reverse(result)}
319-
320-
error ->
321-
error
320+
|> then(fn {result, errors} ->
321+
if %Error{} in result do
322+
{:error, Enum.reverse(errors)}
323+
else
324+
{:ok, result}
325+
end
322326
end)
323327
else
324328
{:error, ~s|expected a list|}

test/schematic_test.exs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,16 @@ defmodule SchematicTest do
8989
end
9090

9191
test "list/1" do
92-
schematic = list(int())
93-
input = [1, 2, 3]
94-
assert {:ok, input} == unify(schematic, input)
92+
schematic = list(map(%{name: str()}))
93+
94+
assert {:ok, [%{name: "mitch"}, %{name: "scott"}]} ==
95+
unify(schematic, [%{name: "mitch"}, %{name: "scott"}])
96+
97+
assert {:error, "expected a list"} == unify(schematic, "foo")
98+
99+
assert {:error,
100+
[{:ok, _}, {:error, %{name: "expected a string"}}, {:error, "expected a map"}]} =
101+
unify(schematic, [%{name: "mitch"}, %{name: 1}, 2])
95102
end
96103

97104
test "tuple/2" do
@@ -462,7 +469,10 @@ defmodule SchematicTest do
462469
"bob" => "expected 99",
463470
"dave" => %{
464471
"first" => "expected an integer",
465-
"second" => "expected a list of either a list or a map",
472+
"second" => [
473+
error: "expected either a list or a map",
474+
error: "expected either a list or a map"
475+
],
466476
"teacher" => %{
467477
"grade" => "expected an integer",
468478
"name" => "expected a string",
@@ -721,7 +731,7 @@ defmodule SchematicTest do
721731
assert {:error,
722732
%{
723733
recursive: %{recursive: "expected a map"},
724-
recursive_list: "expected a list of a map"
734+
recursive_list: [error: "expected a map"]
725735
}} ==
726736
unify(schematic, %{
727737
foo: "hi",

0 commit comments

Comments
 (0)