Packages

A result pattern for Elixir (macro version)

Current section

Files

Jump to
result_macros lib result calc.ex
Raw

lib/result/calc.ex

defmodule Result.Calc do
@moduledoc """
Result calculations
"""
@doc """
Calculate the AND of two results
r_and :: Result e1 a -> Result e2 b -> Result [e1, e2] [a, b]
## Examples
iex> Result.Calc.r_and({:ok, 1}, {:ok, 2})
{:ok, [1, 2]}
iex> Result.Calc.r_and({:ok, 1}, {:error, 2})
{:error, [2]}
iex> Result.Calc.r_and({:error, 1}, {:ok, 2})
{:error, [1]}
iex> Result.Calc.r_and({:error, 1}, {:error, 2})
{:error, [1, 2]}
"""
defmacro r_and(left, right) do
quote bind_quoted: [left: left, right: right] do
case {left, right} do
{{:ok, val1}, {:ok, val2}} -> {:ok, [val1, val2]}
{{:ok, _}, {:error, val2}} -> {:error, [val2]}
{{:error, val1}, {:ok, _}} -> {:error, [val1]}
{{:error, val1}, {:error, val2}} -> {:error, [val1, val2]}
end
end
end
@doc """
Calculate the OR of two results
r_or :: Result e1 a -> Result e2 b -> Result [e1, e2] [a, b]
## Examples
iex> Result.Calc.r_or({:ok, 1}, {:ok, 2})
{:ok, [1, 2]}
iex> Result.Calc.r_or({:ok, 1}, {:error, 2})
{:ok, [1]}
iex> Result.Calc.r_or({:error, 1}, {:ok, 2})
{:ok, [2]}
iex> Result.Calc.r_or({:error, 1}, {:error, 2})
{:error, [1, 2]}
"""
defmacro r_or(left, right) do
quote bind_quoted: [left: left, right: right] do
case {left, right} do
{{:ok, val1}, {:ok, val2}} -> {:ok, [val1, val2]}
{{:ok, val1}, {:error, _}} -> {:ok, [val1]}
{{:error, _}, {:ok, val2}} -> {:ok, [val2]}
{{:error, val1}, {:error, val2}} -> {:error, [val1, val2]}
end
end
end
@doc """
Calculate product of Results
product :: List (Result e a) -> Result (List e) (List a)
## Examples
iex> data = [{:ok, 1}, {:ok, 2}, {:ok, 3}]
iex> Result.Calc.product(data)
{:ok, [1, 2, 3]}
iex> data = [{:error, 1}, {:ok, 2}, {:error, 3}]
iex> Result.Calc.product(data)
{:error, [1, 3]}
iex> data = [{:error, 1}]
iex> Result.Calc.product(data)
{:error, [1]}
iex> data = []
iex> Result.Calc.product(data)
{:ok, []}
"""
defmacro product(list) do
quote bind_quoted: [list: list] do
list
|> Enum.reduce({:ok, []}, fn
{:ok, val}, {:ok, acc} -> {:ok, [val | acc]}
{:ok, _val}, {:error, acc} -> {:error, acc}
{:error, val}, {:ok, _acc} -> {:error, [val]}
{:error, val}, {:error, acc} -> {:error, [val | acc]}
end)
|> then(fn {ok_or_error, acc} -> {ok_or_error, Enum.reverse(acc)} end)
end
end
@doc """
Calculate sum of Results
sum :: List (Result e a) -> Result (List e) (List a)
## Examples
iex> data = [{:ok, 1}, {:ok, 2}, {:ok, 3}]
iex> Result.Calc.sum(data)
{:ok, [1, 2, 3]}
iex> data = [{:error, 1}, {:ok, 2}, {:error, 3}]
iex> Result.Calc.sum(data)
{:ok, [2]}
iex> data = [{:error, 1}, {:error, 2}, {:error, 3}]
iex> Result.Calc.sum(data)
{:error, [1, 2, 3]}
iex> data = [{:error, 1}]
iex> Result.Calc.sum(data)
{:error, [1]}
iex> data = []
iex> Result.Calc.sum(data)
{:error, []}
"""
defmacro sum(list) do
quote bind_quoted: [list: list] do
list
|> Enum.reduce({:error, []}, fn
{:ok, val}, {:ok, acc} -> {:ok, [val | acc]}
{:ok, val}, {:error, _acc} -> {:ok, [val]}
{:error, _val}, {:ok, acc} -> {:ok, acc}
{:error, val}, {:error, acc} -> {:error, [val | acc]}
end)
|> then(fn {ok_or_error, acc} -> {ok_or_error, Enum.reverse(acc)} end)
end
end
end