mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-28 16:34:13 +01:00
803270fc6b
Contribution for Google Summer of code 2016 by Gabriel Gatu
120 lines
4.0 KiB
Elixir
120 lines
4.0 KiB
Elixir
defmodule Ejabberd.Config.Attr do
|
|
@moduledoc """
|
|
Module used to work with the attributes parsed from
|
|
an elixir block (do...end).
|
|
|
|
Contains functions for extracting attrs from a block
|
|
and validation.
|
|
"""
|
|
|
|
@type attr :: {atom(), any()}
|
|
|
|
@attr_supported [
|
|
active:
|
|
[type: :boolean, default: true],
|
|
git:
|
|
[type: :string, default: ""],
|
|
name:
|
|
[type: :string, default: ""],
|
|
opts:
|
|
[type: :list, default: []],
|
|
dependency:
|
|
[type: :list, default: []]
|
|
]
|
|
|
|
@doc """
|
|
Takes a block with annotations and extracts the list
|
|
of attributes.
|
|
"""
|
|
@spec extract_attrs_from_block_with_defaults(any()) :: [attr]
|
|
def extract_attrs_from_block_with_defaults(block) do
|
|
block
|
|
|> extract_attrs_from_block
|
|
|> put_into_list_if_not_already
|
|
|> insert_default_attrs_if_missing
|
|
end
|
|
|
|
@doc """
|
|
Takes an attribute or a list of attrs and validate them.
|
|
|
|
Returns a {:ok, attr} or {:error, attr, cause} for each of the attributes.
|
|
"""
|
|
@spec validate([attr]) :: [{:ok, attr}] | [{:error, attr, atom()}]
|
|
def validate(attrs) when is_list(attrs), do: Enum.map(attrs, &valid_attr?/1)
|
|
def validate(attr), do: validate([attr]) |> List.first
|
|
|
|
@doc """
|
|
Returns the type of an attribute, given its name.
|
|
"""
|
|
@spec get_type_for_attr(atom()) :: atom()
|
|
def get_type_for_attr(attr_name) do
|
|
@attr_supported
|
|
|> Keyword.get(attr_name)
|
|
|> Keyword.get(:type)
|
|
end
|
|
|
|
@doc """
|
|
Returns the default value for an attribute, given its name.
|
|
"""
|
|
@spec get_default_for_attr(atom()) :: any()
|
|
def get_default_for_attr(attr_name) do
|
|
@attr_supported
|
|
|> Keyword.get(attr_name)
|
|
|> Keyword.get(:default)
|
|
end
|
|
|
|
# Private API
|
|
|
|
# Given an elixir block (do...end) returns a list with the annotations
|
|
# or a single annotation.
|
|
@spec extract_attrs_from_block(any()) :: [attr] | attr
|
|
defp extract_attrs_from_block({:__block__, [], attrs}), do: Enum.map(attrs, &extract_attrs_from_block/1)
|
|
defp extract_attrs_from_block({:@, _, [attrs]}), do: extract_attrs_from_block(attrs)
|
|
defp extract_attrs_from_block({attr_name, _, [value]}), do: {attr_name, value}
|
|
defp extract_attrs_from_block(nil), do: []
|
|
|
|
# In case extract_attrs_from_block returns a single attribute,
|
|
# then put it into a list. (Ensures attrs are always into a list).
|
|
@spec put_into_list_if_not_already([attr] | attr) :: [attr]
|
|
defp put_into_list_if_not_already(attrs) when is_list(attrs), do: attrs
|
|
defp put_into_list_if_not_already(attr), do: [attr]
|
|
|
|
# Given a list of attributes, it inserts the missing attribute with their
|
|
# default value.
|
|
@spec insert_default_attrs_if_missing([attr]) :: [attr]
|
|
defp insert_default_attrs_if_missing(attrs) do
|
|
Enum.reduce @attr_supported, attrs, fn({attr_name, _}, acc) ->
|
|
case Keyword.has_key?(acc, attr_name) do
|
|
true -> acc
|
|
false -> Keyword.put(acc, attr_name, get_default_for_attr(attr_name))
|
|
end
|
|
end
|
|
end
|
|
|
|
# Given an attribute, validates it and return a tuple with
|
|
# {:ok, attr} or {:error, attr, cause}
|
|
@spec valid_attr?(attr) :: {:ok, attr} | {:error, attr, atom()}
|
|
defp valid_attr?({attr_name, param} = attr) do
|
|
case Keyword.get(@attr_supported, attr_name) do
|
|
nil -> {:error, attr, :attr_not_supported}
|
|
[{:type, param_type} | _] -> case is_of_type?(param, param_type) do
|
|
true -> {:ok, attr}
|
|
false -> {:error, attr, :type_not_supported}
|
|
end
|
|
end
|
|
end
|
|
|
|
# Given an attribute value and a type, it returns a true
|
|
# if the value its of the type specified, false otherwise.
|
|
|
|
# Usefoul for checking if an attr value respects the type
|
|
# specified for the annotation.
|
|
@spec is_of_type?(any(), atom()) :: boolean()
|
|
defp is_of_type?(param, type) when type == :boolean and is_boolean(param), do: true
|
|
defp is_of_type?(param, type) when type == :string and is_bitstring(param), do: true
|
|
defp is_of_type?(param, type) when type == :list and is_list(param), do: true
|
|
defp is_of_type?(param, type) when type == :atom and is_atom(param), do: true
|
|
defp is_of_type?(_param, type) when type == :any, do: true
|
|
defp is_of_type?(_, _), do: false
|
|
end
|