Current section
Files
Jump to
Current section
Files
lib/ex_aequo_kwds.ex
defmodule ExAequoKwds do
use ExAequoKwds.Types
@moduledoc """
Tools to handle access and constraints to Keyword Lists
### `check_kwds`
All required keys are present
iex(1)> check_kwds([a: 1, b: 2], [:b, :a])
{:ok, %{a: 1, b: 2}}
Defaults can be used
iex(2)> check_kwds([a: 1], [:a, b: 2])
{:ok, %{a: 1, b: 2}}
But might not
iex(3)> check_kwds([a: 1, b: 1], [:a, b: 2])
{:ok, %{a: 1, b: 1}}
We must not have spurious keys
iex(4)> check_kwds([a: 1, b: 1], [:a])
{:error, "spurious [b: 1]"}
Nor missing ones
iex(5)> check_kwds([b: 1], [:a, :b])
{:error, "missing key a"}
But we can ignore_errors
iex(6)> check_kwds([a: 1, b: 1], [:a], ignore_errors: true)
{:ok, %{a: 1}}
iex(7)> check_kwds([b: 1], [:a, :b], ignore_errors: true)
{:ok, %{a: nil, b: 1}}
### `check_kwds!`
All fine or ignoring errors
iex(8)> check_kwds!([a: 1, b: 2], [:b, :a])
%{a: 1, b: 2}
iex(9)> check_kwds!([a: 1], [:a, b: 2])
%{a: 1, b: 2}
iex(10)> check_kwds!([a: 1, b: 1], [:a], ignore_errors: true)
%{a: 1}
iex(11)> check_kwds!([b: 1], [:a, :b], ignore_errors: true)
%{a: nil, b: 1}
Otherwise `ArgumentError` will be raised
iex(12)> assert_raise(ArgumentError, fn -> check_kwds!([a: 1, b: 1], [:a]) end)
Nor missing ones
iex(13)> assert_raise(ArgumentError, fn -> check_kwds!([b: 1], [:a, :b]) end)
"""
@spec check_kwds(Keyword.t, spec_t(), Keyword.t) :: result_t()
def check_kwds(kwds, keys, options \\ []) do
if Keyword.get(options, :ignore_errors) do
_check_kwds_easy(keys, kwds)
else
_check_kwds_strict(keys, kwds)
end
end
@spec check_kwds!(Keyword.t, spec_t(), Keyword.t) :: map()
def check_kwds!(kwds, keys, options \\ []) do
case check_kwds(kwds, keys, options) do
{:ok, result} -> result
{:error, message} -> raise ArgumentError, message
end
end
@spec _check_kwds_easy(spec_t(), Keyword.t) :: result_t()
defp _check_kwds_easy(keys, kwds) do
result = Enum.reduce(keys, %{}, fn key, result ->
Map.put(result, key, Keyword.get(kwds, key))
end)
{:ok, result}
end
@spec _check_kwds_strict(spec_t(), Keyword.t, map()) :: result_t()
defp _check_kwds_strict(keys, rest, result \\ %{})
defp _check_kwds_strict([], [], result) do
{:ok, result}
end
defp _check_kwds_strict([], rest, _result) do
{:error, "spurious #{inspect(rest)}"}
end
defp _check_kwds_strict([key|others], rest, result) do
case key do
{k, d} when is_atom(k) -> {rest1, value} = _get_value(k, rest, d)
_check_kwds_strict(others, rest1, Map.put(result, k, value))
_ -> _get_kwd_strict(others, key, rest, result)
end
end
@spec _get_kwd_strict(spec_t(), atom(), Keyword.t, map()) :: result_t()
defp _get_kwd_strict(others, k, rest, result) do
case Keyword.fetch(rest, k) do
{:ok, value} -> _check_kwds_strict(others, Keyword.delete(rest, k), Map.put(result, k, value))
:error -> {:error, "missing key #{k}"}
end
end
@spec _get_value(atom(), Keyword.t, any()) :: {Keyword.t, any()}
defp _get_value(key, rest, default) do
value = Keyword.get(rest, key, default)
rest1 = Keyword.delete(rest, key)
{rest1, value}
end
end
# SPDX-License-Identifier: AGPL-3.0-or-later