From d430befdc0efb10bfd5a42761b8c02c3e35727d4 Mon Sep 17 00:00:00 2001 From: Peter Solnica Date: Thu, 18 Jan 2024 16:54:42 +0100 Subject: [PATCH] wip - port Sum --- lib/drops/contract.ex | 19 +++++++++++++++ lib/drops/types/map/key.ex | 4 ++++ lib/drops/types/sum.ex | 4 ++-- lib/drops/validator/messages/backend.ex | 32 ++++++++++++++++++++----- test/contract/types/custom_test.exs | 4 ++-- 5 files changed, 53 insertions(+), 10 deletions(-) diff --git a/lib/drops/contract.ex b/lib/drops/contract.ex index 7b0e79c..5357ae6 100644 --- a/lib/drops/contract.ex +++ b/lib/drops/contract.ex @@ -73,6 +73,25 @@ defmodule Drops.Contract do end end + def conform(data, %Types.Sum{} = type, path: path) do + case conform(data, type.left, path: path) do + {:ok, output} = success -> + success + + {:error, left_error} -> + case conform(data, type.right, path: path) do + {:ok, output} = success -> + success + + {:error, right_error} -> + {:error, + @message_backend.errors( + {:error, {path, {:or, {left_error, right_error}}}} + )} + end + end + end + defp apply_rules(output) do Enum.map(rules(), fn name -> apply(__MODULE__, :rule, [name, output]) end) |> Enum.filter(fn diff --git a/lib/drops/types/map/key.ex b/lib/drops/types/map/key.ex index 764e87c..134232f 100644 --- a/lib/drops/types/map/key.ex +++ b/lib/drops/types/map/key.ex @@ -42,6 +42,10 @@ defmodule Drops.Types.Map.Key do Map.has_key?(map, key) and present?(map[key], tail) end + defp nest_result({:error, {:or, {left, right}}}, root) do + {:error, {:or, {nest_result(left, root), nest_result(right, root)}}} + end + defp nest_result({:error, {:list, results}}, root) when is_list(results) do {:error, {root, {:list, Enum.with_index(results, &nest_result(&1, root ++ [&2]))}}} end diff --git a/lib/drops/types/sum.ex b/lib/drops/types/sum.ex index 7ced1c8..13e4d5a 100644 --- a/lib/drops/types/sum.ex +++ b/lib/drops/types/sum.ex @@ -32,12 +32,12 @@ defmodule Drops.Types.Sum do {:ok, value} -> {:ok, value} - {:error, left_error} -> + {:error, _} = left_error -> case Drops.Type.Validator.validate(right, input) do {:ok, value} -> {:ok, value} - {:error, right_error} -> + {:error, _} = right_error -> {:error, {:or, {left_error, right_error}}} end end diff --git a/lib/drops/validator/messages/backend.ex b/lib/drops/validator/messages/backend.ex index d9e643b..b481e51 100644 --- a/lib/drops/validator/messages/backend.ex +++ b/lib/drops/validator/messages/backend.ex @@ -93,7 +93,7 @@ defmodule Drops.Validator.Messages.Backend do end defp error({:error, {path, {:map, errors}}}) do - Error.Conversions.nest( + nest( %Error.Set{ errors: Enum.reject(Enum.map(errors, &error/1), &is_nil/1) }, @@ -101,11 +101,32 @@ defmodule Drops.Validator.Messages.Backend do ) end + defp error({:error, {:map, results}}) when is_list(results) do + %Error.Set{ + errors: Enum.reject(Enum.map(results, &error/1), &is_nil/1) + } + end + + defp error({:error, {value, [predicate: predicate, args: _] = meta}}) do + %Error.Type{text: text(predicate, value), meta: meta} + end + defp error({:error, {path, {:list, results}}}) when is_list(results) do errors = Enum.map(results, &error/1) |> Enum.reject(&is_nil/1) if Enum.empty?(errors), do: nil, else: %Error.Set{errors: errors} end + defp error({:error, {:list, results}}) when is_list(results) do + errors = + Enum.with_index(results, fn + {:error, _} = result, index -> nest(error(result), [index]) + {:ok, _}, _ -> nil + end) + |> Enum.reject(&is_nil/1) + + if Enum.empty?(errors), do: nil, else: %Error.Set{errors: errors} + end + defp error(results) when is_list(results) do errors = Enum.map(results, &error/1) |> Enum.reject(&is_nil/1) if Enum.empty?(errors), do: nil, else: %Error.Set{errors: errors} @@ -123,11 +144,8 @@ defmodule Drops.Validator.Messages.Backend do %Error.Rule{path: path, text: text} end - defp error({:error, {path, {:or, {left, right}}}}) do - %Error.Sum{ - left: error({:error, {path, left}}), - right: error({:error, {path, right}}) - } + defp error({:error, {:or, {left, right}}}) do + %Error.Sum{left: error(left), right: error(right)} end defp error({:error, {path, {:cast, error}}}) do @@ -136,6 +154,8 @@ defmodule Drops.Validator.Messages.Backend do defp error(:ok), do: nil defp error({:ok, _}), do: nil + + defp nest(error, path), do: Error.Conversions.nest(error, path) end end end diff --git a/test/contract/types/custom_test.exs b/test/contract/types/custom_test.exs index 7963a20..521bb55 100644 --- a/test/contract/types/custom_test.exs +++ b/test/contract/types/custom_test.exs @@ -17,8 +17,8 @@ defmodule Drops.Contract.Types.CustomTest do end test "returns errors with invalid input", %{contract: contract} do - assert_errors ["test must be a string"], contract.conform(%{test: 1}) - assert_errors ["test must be filled"], contract.conform(%{test: ""}) + assert_errors(["test must be a string"], contract.conform(%{test: 1})) + assert_errors(["test must be filled"], contract.conform(%{test: ""})) end end end