Skip to content

Commit

Permalink
Better union validation
Browse files Browse the repository at this point in the history
It's better to evaluate right side only if left side primitive check
failed, because only this type of failure means that maybe we have an
input that matches right-side specificiation.
  • Loading branch information
solnic committed Jan 29, 2024
1 parent 3f97627 commit 46ecc11
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 17 deletions.
20 changes: 20 additions & 0 deletions lib/drops/types/union.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,26 @@ defmodule Drops.Types.Union do
"""

defmodule Validator do
def validate(%{left: %{primitive: _} = left, right: %{primitive: _} = right}, input) do
case Drops.Type.Validator.validate(left, input) do
{:ok, value} ->
{:ok, value}

{:error, meta} = left_error ->
if is_list(meta) and meta[:predicate] != :type? do
left_error
else
case Drops.Type.Validator.validate(right, input) do
{:ok, value} ->
{:ok, value}

{:error, _} = right_error ->
{:error, {:or, {left_error, right_error}}}
end
end
end
end

def validate(%{left: left, right: right}, input) do
case Drops.Type.Validator.validate(left, input) do
{:ok, value} ->
Expand Down
5 changes: 1 addition & 4 deletions test/contract/type_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,7 @@ defmodule Drops.Contract.TypeTest do
end

test "returns error from left predicates", %{contract: contract} do
assert_errors(
["test must be filled or test must be a map"],
contract.conform(%{test: []})
)
assert_errors(["test must be filled"], contract.conform(%{test: []}))
end

test "returns errors from right predicates", %{contract: contract} do
Expand Down
5 changes: 1 addition & 4 deletions test/contract/types/custom_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,7 @@ defmodule Drops.Contract.Types.CustomTest do
contract.conform(%{test: "Hello World"})
)

assert_errors(
["test must be greater than 0 or test must be a float"],
contract.conform(%{test: -1})
)
assert_errors(["test must be greater than 0"], contract.conform(%{test: -1}))
end
end

Expand Down
12 changes: 3 additions & 9 deletions test/contract/types/union_test.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule Drops.Contract.Types.StringTest do
defmodule Drops.Contract.Types.UnionTest do
use Drops.ContractCase

describe "a union of two primitive types" do
Expand Down Expand Up @@ -40,10 +40,7 @@ defmodule Drops.Contract.Types.StringTest do
end

test "returns error when left side is a failure", %{contract: contract} do
assert_errors(
["test size must be 5 or test must be an integer"],
contract.conform(%{test: "Hello World"})
)
assert_errors(["test size must be 5"], contract.conform(%{test: "Hello World"}))
end

test "returns error when left side and right are failures", %{contract: contract} do
Expand Down Expand Up @@ -100,10 +97,7 @@ defmodule Drops.Contract.Types.StringTest do
end

test "returns error when left side is a failure", %{contract: contract} do
assert_errors(
["test size must be 5 or test must be an integer"],
contract.conform(%{test: "Hello World"})
)
assert_errors(["test size must be 5"], contract.conform(%{test: "Hello World"}))
end

test "returns success when right side is a failure", %{contract: contract} do
Expand Down

0 comments on commit 46ecc11

Please sign in to comment.