Current section
Files
Jump to
Current section
Files
src/docsh_internal.erl
-module(docsh_internal).
-export([merge/1,
kna/1]).
-export_type([t/0,
item/0,
kna/0]).
-type t() :: #{name := module(),
items := [item()],
description => none | iodata()}.
-type item() :: #{kind := function | type,
name := atom(),
arity := arity(),
description => none | iodata(),
exported => boolean(),
signature => iodata()}.
-type kna() :: {function | type, atom(), arity()}.
-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 -> erlang:error(not_disjoint, [Info1, Info2 | Rest]);
true ->
(module(Info1) =:= module(Info2)
orelse erlang:error(different_modules, [Info1, Info2 | Rest])),
merge([merge_two(Info1, Info2) | Rest])
end.
-spec kna(item()) -> kna().
kna(#{kind := K, name := N, arity := A}) -> {K, N, A}.
%%.
%%' Internal
%%
-spec merge_two(t(), t()) -> t().
merge_two(#{items := Items1} = Info1, #{items := Items2}) ->
ItemsByKNA = dict:to_list( docsh_lib:group_by(fun kna/1, Items1 ++ Items2) ),
NewItems = lists:map(fun merge_two_/1, ItemsByKNA),
Info1#{items := NewItems}.
-spec merge_two_({kna(), [item()]}) -> item().
merge_two_({{Kind, Name, Arity}, [Item]}) ->
#{kind := Kind, name := Name, arity := Arity} = Item,
Item;
merge_two_({{Kind, Name, Arity}, [Item1, Item2]}) ->
#{kind := Kind, name := Name, arity := Arity} = Item1,
#{kind := Kind, name := Name, arity := Arity} = Item2,
maps:merge(Item1, Item2).
are_disjoint(Info1, Info2) ->
Module = maps:get(name, Info1),
Module = maps:get(name, Info2),
#{items := Items1} = Info1,
#{items := Items2} = Info2,
Items1 -- Items2 == Items1.
module(#{name := Name}) -> Name.
%%. vim: foldmethod=marker foldmarker=%%',%%.