Current section
Files
Jump to
Current section
Files
lib/amlapio.ex
defmodule Amlapio do
@moduledoc false
alias Amlapio.Utils, as: AMLUtils
alias Amlapio.DSL, as: AMLDSL
@map_function_map_types_all [:agent, :genserver, :genserver_api, :genserver_handle_call]
@map_function_map_types_allowed [:agent, :genserver_api, :genserver_handle_call]
# create a map of the function names v Keyword list of name v arity
@map_functions_names_v_arities :functions
|> Map.__info__
# group the functions by name
|> Enum.group_by(fn {name, _arity} -> name end)
# drop unwanted keys
|> Map.drop([:new])
# create a list of all Map's function names
@map_functions_names_all @map_functions_names_v_arities |> Map.keys
# a hard coded list of all Map's mutator and popper functions
@map_functions_names_mutators [:delete, :drop, :merge, :put, :put_new, :put_new_lazy, :update, :update!]
@map_functions_names_poppers [:get_and_update!, :get_and_update, :pop, :pop_lazy]
# create the accessor list by "subtracting" the others from the full list of functions
@map_functions_names_accessors @map_functions_names_v_arities
# get the names of all the functions
|> Map.keys
# subtract the mutators and poppers
|> Kernel.--(@map_functions_names_mutators ++ @map_functions_names_poppers)
# create a map of names v type i.e accessor or mutator
@map_functions_names_accessors_v_type @map_functions_names_accessors
|> Enum.map(fn name -> {name, :accessor} end)
@map_functions_names_mutators_v_type @map_functions_names_mutators
|> Enum.map(fn name -> {name, :mutator} end)
@map_functions_names_poppers_v_type @map_functions_names_poppers
|> Enum.map(fn name -> {name, :popper} end)
# create a map with all functions
@map_functions_names_v_types [
@map_functions_names_accessors_v_type,
@map_functions_names_mutators_v_type,
@map_functions_names_poppers_v_type]
|> Enum.reduce(fn l, s -> s ++ l end)
|> Enum.into(%{})
defp select_map_funs_v_types(opts) do
# only a subset of Map's functions wanted?
case opts |> Keyword.has_key?(:funs) do
# only some funs wanted
true ->
fun_names = opts |> Keyword.fetch!(:funs) |> List.wrap
# ensure all supplied function names valid
true = fun_names
|> Enum.all?(fn fun_name -> Map.has_key?(@map_functions_names_v_types, fun_name) end)
# select the wanted functions
@map_functions_names_v_types |> Map.take(fun_names)
# all funs wanted
false -> @map_functions_names_v_types
end
end
defp resolve_map_types(opts) do
map_types = opts |> Keyword.take(@map_function_map_types_all)
# need to map genserver to genserver_handle_call and genserver_api?
map_types = case map_types |> Keyword.has_key?(:genserver) do
true ->
genserver_types = map_types |> Keyword.fetch!(:genserver)
[genserver_handle_call: genserver_types,
genserver_api: genserver_types]
|> Keyword.merge(map_types)
|> Keyword.delete(:genserver)
_ -> map_types
end
# only one expected
case map_types |> length do
1 -> :ok
_ ->
raise ArgumentError, message: "found multiple map types #{inspect map_types} but one of #{inspect @map_function_map_types_all} expected"
end
# if target is genserver, map it to the api and handlke call options
map_types
|> case do
[:genserver] -> [:genserver_handle_call, :genserver_api]
x -> x
end
opts
|> Keyword.drop(@map_function_map_types_all)
|> Keyword.merge(map_types)
end
@doc false
def process_opts(opts \\ []) do
# get the fun_names_v_type
fun_names_v_types = opts |> select_map_funs_v_types
# was a function namer given? default if not.
fun_namer =
case opts |> Keyword.has_key?(:namer) do
true ->
{fun, _} = opts
|> Keyword.get(:namer)
# its an ast and needs to be eval-ed!
|> Code.eval_quoted([], __ENV__)
fun
#&create_map_fun_name/2
_ -> &AMLUtils.create_map_fun_name/2
end
opts = opts |> resolve_map_types
@map_function_map_types_allowed
|> Enum.flat_map(fn map_type ->
case opts |> Keyword.has_key?(map_type) do
true ->
case opts |> Keyword.get(map_type) do
# if the key is nil it means create api for state
x when is_nil(x) ->
[%{map_type: map_type, map_name: nil}]
# submaps
x ->
x
|> AMLUtils.list_wrap_flat_just
|> Enum.map(fn map_name -> %{map_type: map_type, map_name: map_name} end)
end
_ -> []
end
# for each map spec, enumerate the fun names and types
|> Enum.flat_map(fn %{map_name: map_name} = map_spec ->
fun_names_v_types
|> Enum.flat_map(fn {map_fun_name, fun_type} ->
@map_functions_names_v_arities
|> Map.fetch!(map_fun_name)
|> Enum.map(fn {_map_fun_name, fun_arity} ->
# create the name of the wrapper fun e.g. buttons_update!
fun_name = fun_namer.(map_name, map_fun_name)
map_spec
|> Map.merge(%{map_fun: map_fun_name,
fun_name: fun_name,
fun_type: fun_type,
fun_arity: fun_arity})
end)
end)
end)
end)
|> Enum.map(fn map_wrap -> [make: :make_fun] |> AMLDSL.map_wrap_dsl(map_wrap) end)
|> Enum.map(fn
%{fun_ast: fun_ast} -> fun_ast
_ -> nil
end)
|> AMLUtils.list_wrap_flat_just
end
defmacro __using__(opts) do
# build the wrappers for each map
opts |> __MODULE__.process_opts
# TESTING
#nil
end
end