Current section
Files
Jump to
Current section
Files
lib/blunt/message/options/parser.ex
defmodule Blunt.Message.Options.Parser do
alias Blunt.Message.{Changeset, Metadata}
def parse_message_opts(message_module, opts) do
message_opts = Metadata.get(message_module, :options, [])
%{parsed: parsed, unparsed: unparsed} =
Enum.reduce(
message_opts,
%{parsed: [], unparsed: opts},
fn current_option, acc ->
{name, value} = parse_option(current_option, acc.unparsed)
%{acc | parsed: [{name, value} | acc.parsed], unparsed: Keyword.delete(acc.unparsed, name)}
end
)
case validate_options(parsed, message_opts) do
{:ok, parsed} ->
{:ok, Keyword.merge(unparsed, parsed)}
{:error, errors} ->
{:error, errors}
end
end
defp parse_option({name, _type, config}, provided_opts) do
default = Keyword.fetch!(config, :default)
{name, Keyword.get(provided_opts, name, default)}
end
defp validate_options(parsed_opts, supported_opts) do
required =
supported_opts
|> Enum.filter(fn {_, _type, config} -> Keyword.fetch!(config, :required) == true end)
|> Enum.map(&elem(&1, 0))
|> Enum.uniq()
data = %{}
types =
Enum.into(supported_opts, %{}, fn {name, type, config} ->
{name, ecto_type(type, config)}
end)
params = Enum.into(parsed_opts, %{})
changeset =
{data, types}
|> Ecto.Changeset.cast(params, Map.keys(types))
|> Ecto.Changeset.validate_required(required)
case changeset do
%{valid?: false} ->
{:error, Changeset.format_errors(changeset)}
%{valid?: true} = changset ->
validated =
changset
|> Ecto.Changeset.apply_changes()
|> Map.to_list()
{:ok, validated}
end
end
[
integer: :integer,
float: :float,
boolean: :boolean,
string: :string,
map: :map,
binary: :binary,
decimal: :decimal,
id: :id,
binary_id: Ecto.UUID,
utc_datetime: :utc_datetime,
naive_datetime: :naive_datetime,
date: :date,
time: :time,
any: :any,
utc_datetime_usec: :utc_datetime_usec,
naive_datetime_usec: :naive_datetime_usec,
time_usec: :time_usec
]
|> Enum.map(fn {type_hint, ecto_type} ->
def ecto_type(unquote(type_hint), _config), do: unquote(ecto_type)
def ecto_type({:array, unquote(type_hint)}, _config), do: {:array, unquote(ecto_type)}
def ecto_type({:map, unquote(type_hint)}, _config), do: {:map, unquote(ecto_type)}
end)
def ecto_type(:pid, _config), do: Blunt.Message.Type.Pid
def ecto_type(:enum, config) do
{:parameterized, Ecto.Enum, Ecto.Enum.init(values: Keyword.get(config, :values))}
end
def ecto_type({:array, :enum}, config) do
{:parameterized, {:array, Ecto.Enum}, Ecto.Enum.init(values: Keyword.get(config, :values))}
end
end