Current section

Files

Jump to
elixir_radius lib radius dict.ex
Raw

lib/radius/dict.ex

defmodule Radius.Dict do
alias Radius.Dict.Parser
alias Radius.Dict.EntryNotFoundError
defmacro __using__(_) do
quote do
require Radius.Dict
import Radius.Dict
end
end
extra_dictionaries = Application.compile_env(:elixir_radius, :extra_dictionaries, [])
{includes, generic_attributes, generic_values} =
Path.join(["dicts", "dictionary"])
|> File.read!()
|> Parser.parse_index()
filtered_includes =
if config_includes = Application.compile_env(:elixir_radius, :included_dictionaries) do
Enum.filter(includes, &(&1 in config_includes))
else
includes
end
dict_files =
Enum.map(filtered_includes, fn dict -> Path.join(["dicts", "dictionary.#{dict}"]) end)
{vendors, attributes, values} =
Enum.reduce(
dict_files ++ extra_dictionaries,
{[], generic_attributes, generic_values},
fn dict_file, {acc_vendors, acc_attributes, acc_values} ->
dict =
dict_file
|> File.read!()
|> Parser.parse()
{acc_attributes, rest} =
case Map.pop(dict, :attributes) do
{[], rest} -> {acc_attributes, rest}
{attributes, rest} -> {attributes ++ acc_attributes, rest}
end
{acc_values, rest} =
case Map.pop(rest, :values) do
{[], rest} -> {acc_values, rest}
{values, rest} -> {values ++ acc_values, rest}
end
acc_vendors =
if vendor = Map.get(rest, :vendor) do
[vendor | acc_vendors]
else
acc_vendors
end
{acc_vendors, acc_attributes, acc_values}
end
)
for attribute <- attributes do
attr_fun_name = "attr_#{attribute.name}" |> String.replace("-", "_") |> String.to_atom()
defmacro unquote(attr_fun_name)(), do: unquote(attribute.id)
defmacro unquote(attr_fun_name)(val), do: {unquote(attribute.id), val}
def attribute_by_id(unquote(attribute.id)), do: unquote(Macro.escape(attribute))
def attribute_by_name(unquote(attribute.name)), do: unquote(Macro.escape(attribute))
end
for val <- values do
val_fun_name = "val_#{val.attr}_#{val.name}" |> String.replace("-", "_") |> String.to_atom()
defmacro unquote(val_fun_name)(), do: unquote(val.value)
def value_by_value(unquote(val.attr), unquote(val.value)), do: unquote(Macro.escape(val))
def value_by_name(unquote(val.attr), unquote(val.name)), do: unquote(Macro.escape(val))
end
for vendor <- vendors do
mod = Module.concat(__MODULE__, :"Vendor#{String.replace(vendor.name, "-", "_")}")
[tl, ll] = Keyword.get(vendor.opts, :format) || [1, 1]
vendor_data = %{id: vendor.id, name: vendor.name, format: {tl, ll}, module: mod}
def vendor_by_id(unquote(vendor.id)), do: unquote(Macro.escape(vendor_data))
def vendor_by_name(unquote(vendor.name)), do: unquote(Macro.escape(vendor_data))
attribute_funs =
for attribute <- vendor.attributes do
attr_fun_name = "attr_#{attribute.name}" |> String.replace("-", "_") |> String.to_atom()
quote location: :keep do
defmacro unquote(attr_fun_name)(), do: unquote(attribute.id)
defmacro unquote(attr_fun_name)(val), do: {unquote(attribute.id), val}
def attribute_by_id(unquote(attribute.id)), do: unquote(Macro.escape(attribute))
def attribute_by_name(unquote(attribute.name)), do: unquote(Macro.escape(attribute))
end
end
elixir_compiler_limit = 1024
value_funs =
if Enum.count(vendor.attributes ++ vendor.values) < elixir_compiler_limit do
for val <- vendor.values do
val_fun_name =
"val_#{val.attr}_#{val.name}" |> String.replace("-", "_") |> String.to_atom()
quote location: :keep do
defmacro unquote(val_fun_name)(), do: unquote(val.value)
def value_by_value(unquote(val.attr), unquote(val.value)),
do: unquote(Macro.escape(val))
def value_by_name(unquote(val.attr), unquote(val.name)),
do: unquote(Macro.escape(val))
end
end
else
for {attr, vals} <- Enum.group_by(vendor.values, & &1.attr) do
val_fun_name = "val_#{attr}" |> String.replace("-", "_") |> String.to_atom()
quote location: :keep do
defmacro unquote(val_fun_name)(val_name),
do:
Enum.find_value(
unquote(Macro.escape(vals)),
&(&1.name == val_name && &1.value)
)
def value_by_value(unquote(attr), val_value),
do:
Enum.find(
unquote(Macro.escape(vals)),
&(&1.value == val_value)
)
def value_by_name(unquote(attr), val_name),
do:
Enum.find(
unquote(Macro.escape(vals)),
&(&1.name == val_name)
)
end
end
end
IO.puts("Compiling #{mod}")
Module.create(mod, attribute_funs ++ value_funs, Macro.Env.location(__ENV__))
end
def attribute_by_id(id), do: raise(EntryNotFoundError, type: :attribute, key: id)
def attribute_by_name(name), do: raise(EntryNotFoundError, type: :attribute, key: name)
def value_by_value(attr, _val), do: raise(EntryNotFoundError, type: :value, key: attr)
def value_by_name(attr, _val_name), do: raise(EntryNotFoundError, type: :value, key: attr)
def vendor_by_id(id), do: raise(EntryNotFoundError, type: :vendor, key: id)
def vendor_by_name(name), do: raise(EntryNotFoundError, type: :vendor, key: name)
end