1 |
|
%% @doc Service dependency management |
2 |
|
|
3 |
|
-module(mongoose_service_deps). |
4 |
|
|
5 |
|
-export([resolve_deps/1, sort_deps/1]). |
6 |
|
|
7 |
|
-type service_list() :: mongoose_service:service_list(). |
8 |
|
-type service_map() :: mongoose_service:service_map(). |
9 |
|
|
10 |
|
%% @doc Add dependencies to the service map |
11 |
|
-spec resolve_deps(service_map()) -> service_map(). |
12 |
|
resolve_deps(Services) when is_map(Services) -> |
13 |
16 |
resolve_deps(maps:to_list(Services), #{}). |
14 |
|
|
15 |
|
-spec resolve_deps(service_list(), service_map()) -> service_map(). |
16 |
|
resolve_deps([], Acc) -> |
17 |
16 |
Acc; |
18 |
|
resolve_deps([{Service, Opts} | Rest], Acc) -> |
19 |
16 |
{DepsToResolve, NewAcc} = |
20 |
|
case Acc of |
21 |
|
#{Service := PreviousOpts} -> |
22 |
:-( |
{[], Acc#{Service => merge_opts(PreviousOpts, Opts)}}; |
23 |
|
#{} -> |
24 |
16 |
Deps = [{Dep, #{}} || Dep <- mongoose_service:get_deps(Service)], |
25 |
16 |
{Deps, Acc#{Service => Opts}} |
26 |
|
end, |
27 |
16 |
resolve_deps(DepsToResolve ++ Rest, NewAcc). |
28 |
|
|
29 |
|
%% Deps always have no options, so one argument has to be an empty map |
30 |
:-( |
merge_opts(#{}, Opts) -> Opts; |
31 |
:-( |
merge_opts(Opts, #{}) -> Opts. |
32 |
|
|
33 |
|
%% @doc Sort services (which already include their dependencies) according to the dependency graph |
34 |
|
-spec sort_deps(service_map()) -> service_list(). |
35 |
|
sort_deps(Services) when is_map(Services) -> |
36 |
31 |
DepsGraph = digraph:new([acyclic, private]), |
37 |
31 |
try |
38 |
31 |
[add_service_with_deps(Service, DepsGraph) || Service <- maps:keys(Services)], |
39 |
31 |
[{Service, maps:get(Service, Services)} || Service <- digraph_utils:topsort(DepsGraph)] |
40 |
|
after |
41 |
31 |
digraph:delete(DepsGraph) |
42 |
|
end. |
43 |
|
|
44 |
|
add_service_with_deps(Service, DepsGraph) -> |
45 |
31 |
digraph:add_vertex(DepsGraph, Service), |
46 |
31 |
lists:foreach(fun(DepService) -> add_service_dep(Service, DepService, DepsGraph) end, |
47 |
|
mongoose_service:get_deps(Service)). |
48 |
|
|
49 |
|
add_service_dep(Service, DepService, DepsGraph) -> |
50 |
:-( |
digraph:add_vertex(DepsGraph, DepService), |
51 |
:-( |
case digraph:add_edge(DepsGraph, DepService, Service) of |
52 |
|
{error, Error} -> |
53 |
:-( |
{bad_edge, CyclePath} = Error, |
54 |
:-( |
error(#{what => resolving_dependencies_aborted, |
55 |
|
text => <<"Service dependency cycle found">>, |
56 |
|
service => Service, |
57 |
|
dep_service => DepService, |
58 |
|
cycle_path => CyclePath}); |
59 |
|
_Edge -> |
60 |
:-( |
ok |
61 |
|
end. |