Current section
Files
Jump to
Current section
Files
src/boto_server.erl
%% @doc A
%% @end
-module(boto_server).
-behaviour(gen_server).
-vsn(1).
-export([child_spec/1, graph/0, start_link/1, init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3, format_status/1]).
%% @doc A
%% @end
child_spec(args) ->
#{id => boto_server,
start => {boto_server, start_link, [args]},
shutdown => brutal_kil,
restart => permanent,
type => worker}.
%% @private
graph() ->
gen_server:call(boto_server, graph).
%% @doc A
%% @end
start_link(Args) ->
gen_server:start_link({local, boto_server}, boto_server, Args, []).
%% @private
init(Args) ->
Graph = boto_graph:start(),
Table = ets:new(boto_resolvers_config, [set, protected]),
State = #{graph => Graph, resolvers => Table},
ResolverList = proplists:get_value(resolvers, Args),
Resolvers = lists:flatmap(fun get_resolver/1, ResolverList),
boto_graph:add_vertex(Graph, noarg),
[create_vertices(R, Graph) || R <- Resolvers],
[create_edges(R, Graph) || R <- Resolvers],
[store_resolvers(R, Table) || R <- Resolvers],
{ok, State}.
%% @private
handle_call(graph, _Ref, State) ->
{reply, maps:get(graph, State), State}.
%% @private
handle_cast(_M, State) ->
{noreply, State}.
%% @private
handle_info(_M, State) ->
{noreply, State}.
%% @private
terminate(_R, _S) ->
normal.
%% @private
code_change(_Vsn, State, _Extra) ->
{ok, State}.
%% @private
format_status(Status) ->
Status.
get_resolver(Mod) when is_atom(Mod) ->
Exports = apply(Mod, module_info, [exports]),
case lists:member({resolver_init, 0}, Exports) of
true ->
ModConfig = apply(Mod, resolver_init, []),
[{{Mod, Name}, Config} || {Name, Config} <- list_wrap(ModConfig)];
false ->
%TODO: prepare error message
[]
end;
get_resolver(_Mod) ->
%TODO: prepare error message
[].
create_vertices({_Resolver, Config}, Graph) ->
Input = proplists:get_value(input, Config),
[add_vertex(I, Graph) || I <- list_wrap(Input)],
Output = proplists:get_value(output, Config),
[add_vertex(O, Graph) || O <- list_wrap(Output)].
add_vertex({Vertex, Nested}, Graph) ->
boto_graph:add_vertex(Graph, Vertex),
lists:foreach(fun(I) -> add_vertex(I, Graph) end, Nested);
add_vertex(Vertex, Graph) ->
boto_graph:add_vertex(Graph, Vertex).
create_edges({Resolver, Config}, Graph) ->
Outputs = proplists:get_value(output, Config),
case proplists:get_value(input, Config) of
nil ->
ok;
[] ->
lists:foreach(fun(O) -> add_edge(O, noarg, Resolver, Graph) end, Outputs);
Inputs ->
[add_edge(O, I, Resolver, Graph) || O <- Outputs, I <- Inputs]
end.
add_edge({Output, Nested}, Input, Resolver, Graph) ->
boto_graph:add_edge(Graph, Input, Output, Resolver),
[add_edge(N, Output, Resolver, Graph) || N <- Nested];
add_edge(Output, Input, Resolver, Graph) ->
boto_graph:add_edge(Graph, Input, Output, Resolver).
store_resolvers({Resolver, Config}, Table) ->
ets:insert(Table, {Resolver, Config}).
list_wrap(nil) ->
[];
list_wrap(List) when is_list(List) ->
List;
list_wrap(Other) ->
[Other].