Packages
croma
0.1.4
0.13.0
0.12.0
0.11.3
0.11.2
0.11.1
0.11.0
0.10.2
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.3
0.7.2
0.7.1
0.7.0
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.1
0.5.0
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
Elixir macro utilities to make type-based programming easier
Current section
Files
Jump to
Current section
Files
lib/croma/struct.ex
import Croma.Defun
alias Croma.Result, as: R
defmodule Croma.Struct do
def field_default_pairs(fields) do
Enum.map(fields, fn {key, mod} ->
default = try do
mod.default
rescue
_ -> nil
end
{key, default}
end)
end
def field_type_pairs(fields) do
Enum.map(fields, fn {key, mod} ->
{key, quote do: unquote(mod).t}
end)
end
def dict_get2(dict, key) do
kv = Enum.find(dict, :error, fn {k, _} ->
k == key || k == Atom.to_string(key)
end)
case kv do
{_, v} -> {:ok, v}
:error -> :error
end
end
defmacro __using__(fields) do
%Macro.Env{module: module} = __CALLER__
quote context: Croma do
@fields unquote(fields)
defstruct Croma.Struct.field_default_pairs(@fields)
@type t :: %unquote(module){unquote_splicing(Croma.Struct.field_type_pairs(fields))}
defun new(dict: Dict.t) :: t do
Enum.map(@fields, fn {field, mod} ->
case Croma.Struct.dict_get2(dict, field) do
{:ok, v} -> mod.validate(v)
:error -> {:ok, mod.default}
end
|> R.map(&{field, &1})
end)
|> R.sequence
|> R.get!
|> (fn kvs -> struct(__MODULE__, kvs) end).()
end
defun validate(dict: Dict.t) :: R.t(t) do
dict when is_list(dict) or is_map(dict) ->
Enum.map(@fields, fn {field, mod} ->
case Croma.Struct.dict_get2(dict, field) do
{:ok, v} -> v
:error -> nil
end
|> mod.validate
|> R.map(&{field, &1})
end)
|> R.sequence
|> R.map(fn kvs -> struct(__MODULE__, kvs) end)
x -> {:error, "validation error for #{__MODULE__}: #{inspect x}"}
end
defun update(s: t, dict: Dict.t) :: R.t(t) do
(%{__struct__: __MODULE__} = s, dict) when is_list(dict) or is_map(dict) ->
Enum.map(@fields, fn {field, mod} ->
case Croma.Struct.dict_get2(dict, field) do
{:ok, v} -> mod.validate(v) |> R.map(&{field, &1})
:error -> nil
end
end)
|> Enum.reject(&is_nil/1)
|> R.sequence
|> R.map(fn kvs -> struct(s, kvs) end)
end
end
end
end