110 lines
3.6 KiB
Erlang
110 lines
3.6 KiB
Erlang
%% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 80 -*-
|
|
%%% Copyright 2012 Erlware, LLC. All Rights Reserved.
|
|
%%%
|
|
%%% This file is provided to you under the Apache License,
|
|
%%% Version 2.0 (the "License"); you may not use this file
|
|
%%% except in compliance with the License. You may obtain
|
|
%%% a copy of the License at
|
|
%%%
|
|
%%% http://www.apache.org/licenses/LICENSE-2.0
|
|
%%%
|
|
%%% Unless required by applicable law or agreed to in writing,
|
|
%%% software distributed under the License is distributed on an
|
|
%%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
%%% KIND, either express or implied. See the License for the
|
|
%%% specific language governing permissions and limitations
|
|
%%% under the License.
|
|
%%%-------------------------------------------------------------------
|
|
%%% @author Joe Armstrong
|
|
%%% @author Eric Merritt
|
|
%%% @doc
|
|
%%% This is a pretty simple topological sort for erlang. It was
|
|
%%% originally written for ermake by Joe Armstrong back in '98. It
|
|
%%% has been pretty heavily modified by Eric Merritt since '06 and
|
|
%%% modified again for relx/rebar3 by Tristan Sloughter.
|
|
%%%
|
|
%%% A partial order on the set S is a set of pairs {Xi,Xj} such that
|
|
%%% some relation between Xi and Xj is obeyed.
|
|
%%%
|
|
%%% A topological sort of a partial order is a sequence of elements
|
|
%%% [X1, X2, X3 ...] such that if whenever {Xi, Xj} is in the partial
|
|
%%% order i < j
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-module(providers_topo).
|
|
|
|
-export([sort/1]).
|
|
|
|
%%====================================================================
|
|
%% Types
|
|
%%====================================================================
|
|
-type pair() :: {{atom(), atom()}, {atom(), atom()}}.
|
|
-type name() :: {atom(), atom()}.
|
|
-type element() :: name() | pair().
|
|
|
|
%%====================================================================
|
|
%% API
|
|
%%====================================================================
|
|
|
|
%% @doc Do a topological sort on the list of pairs.
|
|
-spec sort([pair()]) -> {ok, [{atom(), atom()}]} | {error, any()}.
|
|
sort(Pairs) ->
|
|
iterate(Pairs, [], all(Pairs)).
|
|
|
|
%%====================================================================
|
|
%% Internal Functions
|
|
%%====================================================================
|
|
|
|
%% @doc Iterate over the system. @private
|
|
-spec iterate([pair()], [name()], [name()]) ->
|
|
{ok, [name()]} | {error, string()}.
|
|
iterate([], L, All) ->
|
|
{ok, remove_duplicates(L ++ subtract(All, L))};
|
|
iterate(Pairs, L, All) ->
|
|
case subtract(lhs(Pairs), rhs(Pairs)) of
|
|
[] ->
|
|
{error, "Cycle found in providers dependencies."};
|
|
Lhs ->
|
|
iterate(remove_pairs(Lhs, Pairs), L ++ Lhs, All)
|
|
end.
|
|
|
|
-spec all([pair()]) -> [{atom(), atom()}].
|
|
all(L) ->
|
|
lhs(L) ++ rhs(L).
|
|
|
|
-spec lhs([pair()]) -> [{atom(), atom()}].
|
|
lhs(L) ->
|
|
[X || {X, _} <- L].
|
|
|
|
-spec rhs([pair()]) -> [{atom(), atom()}].
|
|
rhs(L) ->
|
|
[Y || {_, Y} <- L].
|
|
|
|
%% @doc all the elements in L1 which are not in L2
|
|
%% @private
|
|
-spec subtract([element()], [element()]) -> [element()].
|
|
subtract(L1, L2) ->
|
|
[X || X <- L1, not lists:member(X, L2)].
|
|
|
|
%% @doc remove dups from the list. @private
|
|
-spec remove_duplicates([element()]) -> [element()].
|
|
remove_duplicates([H|T]) ->
|
|
case lists:member(H, T) of
|
|
true ->
|
|
remove_duplicates(T);
|
|
false ->
|
|
[H|remove_duplicates(T)]
|
|
end;
|
|
remove_duplicates([]) ->
|
|
[].
|
|
|
|
%% @doc
|
|
%% removes all pairs from L2 where the first element
|
|
%% of each pair is a member of L1
|
|
%%
|
|
%% L2' L1 = [X] L2 = [{X,Y}].
|
|
%% @private
|
|
-spec remove_pairs([{atom(), atom()}], [pair()]) -> [pair()].
|
|
remove_pairs(L1, L2) ->
|
|
[All || All={X, _Y} <- L2, not lists:member(X, L1)].
|