harnais v1.0.0 Harnais.Utility View Source

Miscellaneous Utility Functions

Link to this section Summary

Functions

form_validate/1 calls Macro.validate/1 on the argument (the expected form) and if the result is :ok returns {:ok, form}, else {:error, error}

forms_normalise/1 takes zero, one or more form and normalises them to a forms returning {:ok, forms}

forms_reduce/1 takes a zero, one or more form, normalises them, and reduces the forms to a single form using Kernel.SpecialForms.unquote_splicing/1

forms_validate/1 validates the forms using form_validate/1 on each form, returning {:ok, forms} if all are valid, else {:error, error}

map_collate0_enum/2 take an enum and map/1 function and applies the arity 1 function to each element of the enum in an Enum.reduce_while/2 loop

opts_canon_keys/2 takes an opts list, together with a lookup dictionary and replaces each key with its canonical value from the dictionary, returning {:ok, opts}

opts_canon_keys!/2 takes an opts list, together with a lookup dictionary and replaces each key with its canonical value from the dictionary. Unknown keys raise a KeyError

opts_canonical_keys/2 takes a derivable opts, together with a key alias dict

opts_create_aliases_dict/1 does the same job as opts_create_alias_tuples/1 but returns a key alias dict

opts_maybe_canon_keys/2 takes an opts list, together with a lookup dictionary and replaces any key in the dictionary with its dictionary value, returning the updated opts

opts_normalise/ expects a derivable opts and returns {:ok, opts}

opts_sort_keys/ takes an opts list, together with a list of sort keys, and returns the opts sorted in the sort keys order. Duplicate keys follow one after another

opts_validate/1 returns {:ok, opts} if the argument is an opts

opts_validate_keys!/2 takes an opts list, together with the list of valid keys, and check there are no unknown keys in opts, raising a KeyError if so, else returning the opts

Link to this section Types

Link to this section Functions

Link to this function form_validate(form) View Source
form_validate(any()) :: {:ok, form()} | {:error, error()}

form_validate/1 calls Macro.validate/1 on the argument (the expected form) and if the result is :ok returns {:ok, form}, else {:error, error}.

Examples

iex> 1 |> form_validate
{:ok, 1}

iex> nil |> form_validate # nil is a valid ast
{:ok, nil}

iex> [:x, :y] |> form_validate
{:ok, [:x, :y]}

iex> form = {:x, :y} # this 2tuple is a valid form without escaping
...> form |> form_validate
{:ok, {:x, :y}}

iex> {:error, error} = {:x, :y, :z} |> form_validate
...> error |> Exception.message
"form invalid, got: {:x, :y, :z}"

iex> {:error, error} = %{a: 1, b: 2, c: 3} |> form_validate # map not a valid form
...> error |> Exception.message
"form invalid, got: %{a: 1, b: 2, c: 3}"

iex> form = %{a: 1, b: 2, c: 3} |> Macro.escape # escaped map is a valid form
...> form |> form_validate
{:ok,  %{a: 1, b: 2, c: 3} |> Macro.escape}
Link to this function forms_normalise(forms \\ []) View Source
forms_normalise(any()) :: {:ok, forms()} | {:error, error()}

forms_normalise/1 takes zero, one or more form and normalises them to a forms returning {:ok, forms}.

The list is first flattened and any nils removed before splicing.

Examples

iex> {:ok, forms} = quote(do: a = x + y) |> forms_normalise
...> forms |> hd |> Macro.to_string
"a = x + y"

iex> {:ok, forms} = [
...>  quote(do: a = x + y),
...>  quote(do: a * c)
...> ] |> forms_normalise
...> forms |> Macro.to_string
"[a = x + y, a * c]"

iex> nil |> forms_normalise
{:ok, []}

iex> {:ok, form} = [
...>  quote(do: a = x + y),
...>  nil,
...>  [
...>   quote(do: b = a / c),
...>   nil,
...>   quote(do: d = b * b),
...>  ],
...>  quote(do: e = a + d),
...> ] |> forms_normalise
...> form |> Macro.to_string
"[a = x + y, b = a / c, d = b * b, e = a + d]"
Link to this function forms_reduce(asts \\ []) View Source
forms_reduce(any()) :: {:ok, form()} | {:error, error()}

forms_reduce/1 takes a zero, one or more form, normalises them, and reduces the forms to a single form using Kernel.SpecialForms.unquote_splicing/1.

If the reduction suceeds, {:ok, reduced_form} is returned, else {:error, error}.

An empty list reduces to {:ok, nil}.

Examples

iex> {:ok, reduced_form} = quote(do: a = x + y) |> forms_reduce
...> reduced_form |> Macro.to_string
"a = x + y"

iex> {:ok, reduced_form} = [
...>  quote(do: a = x + y),
...>  quote(do: a * c)
...> ] |> forms_reduce
...> reduced_form |> Macro.to_string
"(\n  a = x + y\n  a * c\n)"

iex> {:ok, form} = nil |> forms_reduce
...> form |> Macro.to_string
"nil"

iex> {:ok, form} = [
...>  quote(do: a = x + y),
...>  nil,
...>  [
...>   quote(do: b = a / c),
...>   nil,
...>   quote(do: d = b * b),
...>  ],
...>  quote(do: e = a + d),
...> ] |> forms_reduce
...> form |> Macro.to_string
"(\n  a = x + y\n  b = a / c\n  d = b * b\n  e = a + d\n)"
Link to this function forms_validate(forms) View Source
forms_validate(any()) :: {:ok, forms()} | {:error, error()}

forms_validate/1 validates the forms using form_validate/1 on each form, returning {:ok, forms} if all are valid, else {:error, error}.

Examples

iex> [1, 2, 3] |> forms_validate
{:ok, [1, 2, 3]}

iex> [1, {2, 2}, :three] |> forms_validate
{:ok, [1, {2, 2}, :three]}

iex> {:error, error} = [1, {2, 2, 2}, %{c: 3}] |> forms_validate
...> error |> Exception.message
"forms invalid, got invalid indices: [1, 2]"
Link to this function map_collate0_enum(enum, fun) View Source
map_collate0_enum(any(), any()) :: {:ok, list()} | {:error, error()}

map_collate0_enum/2 take an enum and map/1 function and applies the arity 1 function to each element of the enum in an Enum.reduce_while/2 loop.

The mapper must return either {:ok, value} or {:error, error}.

If the latter, the Enum.reduce_while/2 is halted, returning the {:error, error}.

Otherwise the value from each {:ok, value} result are collected into a list and {:ok, values} returned.

Examples

iex> fun = fn v -> {:ok, v} end
...> [1,2,3] |> map_collate0_enum(fun)
{:ok, [1,2,3]}

iex> fun = fn
...>  3 -> {:error, %ArgumentError{message: "argument is 3"}}
...>  v -> {:ok, v}
...> end
...> {:error, error} = [1,2,3] |> map_collate0_enum(fun)
...> error |> Exception.message
"argument is 3"

iex> fun = :not_a_fun
...> {:error, error} = [1,2,3] |> map_collate0_enum(fun)
...> error |> Exception.message
"fun/1 function invalid, got: :not_a_fun"

iex> fun = fn v -> {:ok, v} end
...> {:error, error} = 42 |> map_collate0_enum(fun)
...> error |> Exception.message
"enum invalid, got: 42"
Link to this function new_error_result(opts \\ []) View Source
Link to this function new_key_error(values, term) View Source
Link to this function opts_canon_keys(opts, dict) View Source
opts_canon_keys(opts(), alias_dict()) ::
  {:ok, opts()} | {:error, {opts(), opts()}}

opts_canon_keys/2 takes an opts list, together with a lookup dictionary and replaces each key with its canonical value from the dictionary, returning {:ok, opts}.

If there are any unknown keys, {:error, {known_opts, unknown_opts}} is returned.

Examples

iex> [a: 1, b: 2, c: 3] |> opts_canon_keys(%{a: :x, b: :y, c: :z})
{:ok, [x: 1, y: 2, z: 3]}

iex> [a: 11, p: 1, b: 22, q: 2, c: 33, r: 3] |> opts_canon_keys(%{a: :x, b: :y, c: :z})
{:error, {[x: 11, y: 22, z: 33], [p: 1, q: 2, r: 3]}}
Link to this function opts_canon_keys!(opts, dict) View Source
opts_canon_keys!(opts(), alias_dict()) :: opts() | no_return()

opts_canon_keys!/2 takes an opts list, together with a lookup dictionary and replaces each key with its canonical value from the dictionary. Unknown keys raise a KeyError.

Examples

iex> [a: 1, b: 2, c: 3] |> opts_canon_keys!(%{a: :x, b: :y, c: :z})
[x: 1, y: 2, z: 3]

iex> [x: 1, y: 3, z: 3] |> opts_canon_keys!(%{a: 1, b: 2, c: 3})
** (KeyError) key :x not found in: %{a: 1, b: 2, c: 3}
Link to this function opts_canonical_keys(opts, dict) View Source
opts_canonical_keys(opts(), alias_dict()) :: {:ok, opts()} | {:error, error()}

opts_canonical_keys/2 takes a derivable opts, together with a key alias dict.

Each key in the opts is replaced with its (canonical) value from the dictionary, returning {:ok, canon_opts}.

If there are any unknown keys, {:error, error}, where error is a KeyError, will be returned.

Examples

iex> [a: 1, b: 2, c: 3] |> opts_canonical_keys(%{a: :x, b: :y, c: :z})
{:ok, [x: 1, y: 2, z: 3]}

iex> [a: 1, b: 2, c: 3] |> opts_canonical_keys([a: :x, b: :y, c: :z])
{:ok, [x: 1, y: 2, z: 3]}

iex> [a: 11, p: 1, b: 22, q: 2, c: 33, r: 3] |> opts_canonical_keys(%{a: :x, b: :y, c: :z})
{:error, %KeyError{key: [:p, :q, :r], term: %{a: :x, b: :y, c: :z}}}

iex> {:error, error} = [a: 1, b: 2, c: 3]
...> |> opts_canonical_keys([a_canon: :a, b_canon: [:b], c_canon: [:c, :cc]])
...> error |> Exception.message
"key alias dict invalid, got: %{a_canon: :a, b_canon: [:b], c_canon: [:c, :cc]}"
Link to this function opts_create_aliases_dict(aliases) View Source
opts_create_aliases_dict(alias_kvs()) :: alias_dict()

opts_create_aliases_dict/1 does the same job as opts_create_alias_tuples/1 but returns a key alias dict.

Examples

iex> [a: nil, b: [:b1], c: [:c1, :c2, :c3]] |> opts_create_aliases_dict
%{a: :a, b: :b, b1: :b, c: :c, c1: :c, c2: :c, c3: :c}
Link to this function opts_maybe_canon_keys(opts, dict) View Source
opts_maybe_canon_keys(opts(), alias_dict()) :: {:ok, opts()}

opts_maybe_canon_keys/2 takes an opts list, together with a lookup dictionary and replaces any key in the dictionary with its dictionary value, returning the updated opts.

Examples

iex> [a: 1, b: 2, c: 3] |> opts_maybe_canon_keys(%{a: :x, b: :y, c: :z})
[x: 1, y: 2, z: 3]

iex> [a: 11, p: 1, b: 22, q: 2, c: 33, r: 3] |> opts_maybe_canon_keys(%{a: :x, b: :y, c: :z})
[x: 11, p: 1, y: 22, q: 2, z: 33, r: 3]
Link to this function opts_normalise(value) View Source
opts_normalise(any()) :: {:ok, opts()} | {:error, error()}

opts_normalise/ expects a derivable opts and returns {:ok, opts}.

Any other argument causes {:error, error} to be returned.

Examples

iex> [] |> opts_normalise
{:ok, []}

iex> %{a: 1, b: 2, c: 3} |> opts_normalise
{:ok, [a: 1, b: 2, c: 3]}

iex> %{"a" => 1, :b => 2, :c => 3} |> opts_normalise
{:error, %KeyError{key: "a", term: %{:b => 2, :c => 3, "a" => 1}}}

iex> {:error, error} = 42 |> opts_normalise
...> error |> Exception.message
"opts not derivable, got: 42"

iex> [a: nil, b: [:b1], c: [:c1, :c2, :c3]] |> opts_normalise
{:ok, [a: nil, b: [:b1], c: [:c1, :c2, :c3]]}
Link to this function opts_sort_keys(opts, keys \\ []) View Source
opts_sort_keys(opts(), alias_keys()) :: opts()

opts_sort_keys/ takes an opts list, together with a list of sort keys, and returns the opts sorted in the sort keys order. Duplicate keys follow one after another.

Any keys found but not given in the sort keys follow the sorted keys in the returned opts. Any key in the sort list not found in the opts is ignored.

Examples

iex> [a: 1, b: 2, c: 3, d: 4] |> opts_sort_keys([:c, :a])
[c: 3, a: 1,  b: 2, d: 4]

iex> [a: 1, b: 2, c: 3, d: 4] |> opts_sort_keys([:d, :x, :b, :z])
[d: 4, b: 2, a: 1, c: 3]
Link to this function opts_validate(value) View Source
opts_validate(any()) :: {:ok, opts()} | {:error, error()}

opts_validate/1 returns {:ok, opts} if the argument is an opts.

Any other argument causes {:error, error} to be returned.

Examples

iex> [] |> opts_validate
{:ok, []}

iex> {:error, error} = %{a: 1, b: 2, c: 3} |> opts_validate
...> error |> Exception.message
"opts invalid, got: %{a: 1, b: 2, c: 3}"

iex> {:error, error} = %{"a" => 1, :b => 2, :c => 3} |> opts_validate
...> error |> Exception.message
"opts invalid, got: %{:b => 2, :c => 3, \"a\" => 1}"

iex> {:error, error} = 42 |> opts_validate
...> error |> Exception.message
"opts invalid, got: 42"

iex> [a: nil, b: [:b1], c: [:c1, :c2, :c3]] |> opts_validate
{:ok, [a: nil, b: [:b1], c: [:c1, :c2, :c3]]}
Link to this function opts_validate_keys!(opts, known_keys) View Source
opts_validate_keys!(opts(), list()) :: opts() | no_return()

opts_validate_keys!/2 takes an opts list, together with the list of valid keys, and check there are no unknown keys in opts, raising a KeyError if so, else returning the opts.

Examples

iex> [a: 1, b: 2, c: 3] |> opts_validate_keys!([:a, :b, :c])
[a: 1, b: 2, c: 3]

iex> [a: 1, b: 2, c: 3] |> opts_validate_keys!([:a, :b])
** (KeyError) key [:c] not found in: [:a, :b]

iex> [a: 1, b: 2, c: 3] |> opts_validate_keys!([])
[a: 1, b: 2, c: 3]