Current section
Files
Jump to
Current section
Files
src/docsh_internal.erl
-module(docsh_internal).
-export([grouped/1,
merge/1]).
-type t() :: flat() | grouped().
%% Internal documentation format used for merging module info
%% fetched from different sources (edoc, debug info, etc).
%% This format is flat hence its suitability for merging.
-type flat() :: [{atom(), any()}].
%% This format contains all info about an entitiy under a `{name(), arity()}' key.
%% Entity kind has to be inferred from the entry's content.
-type grouped() :: #{{name(), arity()} => any()}.
-type name() :: atom().
-define(a2b(A), atom_to_binary(A, utf8)).
-define(a2l(A), atom_to_list(A)).
-define(i2b(I), integer_to_binary(I)).
-define(il2b(IOList), iolist_to_binary(IOList)).
%%
%%' Public
%%
-spec merge([Info]) -> MergedInfo when
Info :: t(),
MergedInfo :: t().
merge([]) -> [];
merge([Info]) -> Info;
merge([Info1, Info2 | Rest]) ->
case are_disjoint(Info1, Info2) of
false -> error(not_disjoint, [Info1, Info2 | Rest]);
true ->
(module(Info1) =:= module(Info2)
orelse error(different_modules, [Info1, Info2 | Rest])),
merge([merge2(Info1, Info2) | Rest])
end.
-spec grouped(t()) -> t().
grouped(#{} = Grouped) -> Grouped;
grouped([_|_] = Internal) ->
Dict = docsh_lib:group_by(fun item_name_and_arity/1, Internal),
maps:from_list(dict:to_list(Dict)).
%%.
%%' Internal
%%
merge2(Info1, Info2) ->
%% TODO: this might discard valuable module info from Info2
Info1 ++ lists:keydelete(module, 1, Info2).
are_disjoint(Info1, Info2) ->
Keys1 = proplists:get_keys(Info1) -- [module],
Keys2 = proplists:get_keys(Info2) -- [module],
Keys1 -- Keys2 == Keys1.
module(Info) ->
{_, Props} = lists:keyfind(module, 1, Info),
{_, Mod} = lists:keyfind(name, 1, Props),
Mod.
item_name_and_arity({module, _Info}) -> {module, 0};
item_name_and_arity({{type, NameArity}, _}) -> NameArity;
item_name_and_arity({{spec, NameArity}, _}) -> NameArity;
item_name_and_arity({{function, NameArity}, _}) -> NameArity.
%%. vim: foldmethod=marker foldmarker=%%',%%.