Current section
Files
Jump to
Current section
Files
lib/error.ex
defmodule Error do
@moduledoc """
Model domain and infrastructure errors as regular data.
"""
defstruct [:kind, :reason, :details]
@type kind :: :domain | :infra
@type reason :: atom()
@opaque t(a) :: %__MODULE__{
kind: kind,
reason: reason,
details: a
}
@opaque t :: t(map())
@doc """
Create a `domain` error, with a reason and optional details.
"""
@spec domain(atom(), a) :: t(a) when a: map
def domain(reason, details \\ %{}) when is_atom(reason) and is_map(details) do
%__MODULE__{kind: :domain, reason: reason, details: details}
end
@doc """
Create an `infra` error, with a reason and optional details.
"""
@spec infra(atom(), a) :: t(a) when a: map
def infra(reason, details \\ %{}) when is_atom(reason) and is_map(details) do
%__MODULE__{kind: :infra, reason: reason, details: details}
end
@doc """
Determine whether a given `Error` is a `domain` or `infra` error.
"""
@spec kind(t) :: kind
def kind(%__MODULE__{kind: kind}), do: kind
@doc """
Return the reason the `Error` was created with.
"""
@spec reason(t) :: reason
def reason(%__MODULE__{reason: reason}), do: reason
@doc """
Return the map of detailed information supplied at `Error` creation.
"""
@spec details(t(a)) :: a when a: map
def details(%__MODULE__{details: details}), do: details
@doc """
Map a function on the `details` map in an `Error`.
Useful for adding extra details, modifying exisint ones, or removing them.
"""
@spec map_details(t(a), (a -> b)) :: t(b) when a: map, b: map
def map_details(%__MODULE__{details: details} = error, f) do
%__MODULE__{error | details: f.(details)}
end
@doc """
Convert an `Error` to an Elixir map.
"""
@spec to_map(t) :: map
def to_map(%__MODULE__{} = error), do: Map.from_struct(error)
end