diff --git a/pkgs/lib/lists.nix b/pkgs/lib/lists.nix index d032ab412ba..3a5ed2628de 100644 --- a/pkgs/lib/lists.nix +++ b/pkgs/lib/lists.nix @@ -142,4 +142,23 @@ rec { if l == [] then accu else reverse_ ([(head l)] ++ accu) (tail l); in reverse_ [] l; + + # Sort a list based on the `strictLess' function which compare the two + # elements and return true if the first argument is strictly below the + # second argument. The returned list is sorted in an increasing order. + # The implementation does a quick-sort. + sort = strictLess: list: + let + # This implementation only have one element lists on the left hand + # side of the concatenation operator. + qs = l: concat: + if l == [] then concat + else if tail l == [] then l ++ concat + else let + part = partition (strictLess (head l)) (tail l); + in + qs part.wrong ([(head l)] ++ qs part.right []); + in + qs list []; + } diff --git a/pkgs/lib/properties.nix b/pkgs/lib/properties.nix index f28d652d4e6..3964c117f14 100644 --- a/pkgs/lib/properties.nix +++ b/pkgs/lib/properties.nix @@ -382,4 +382,63 @@ rec { mkNotdef ) prioValList; + /* mkOrder */ + + # Order definitions based on there index value. This property is useful + # when the result of the merge function depends on the order on the + # initial list. (e.g. concatStrings) Definitions are ordered based on + # their rank. The lowest ranked definition would be the first to element + # of the list used by the merge function. And the highest ranked + # definition would be the last. Definitions which does not have any rank + # value have the default rank of 100. + isOrder = attrs: (typeOf attrs) == "order"; + mkOrder = rank: content: mkProperty { + property = { + _type = "order"; + onGlobalEval = onOrderGlobalEval; + inherit rank; + }; + inherit content; + }; + + mkHeader = mkOrder 10; + mkFooter = mkOrder 1000; + + # Fetch the rank of each definition (add the default rank is none) and + # sort them based on their ranking. + onOrderGlobalEval = valList: + let + defaultRank = 100; + + inherit (builtins) lessThan; + + getRankVal = + foldProperty + (foldFilter isOrder + (p@{property, content, ...}: + if content ? rank then + content + else + content // { + inherit (property) rank; + } + ) + (p@{property, content, ...}: + content // { + value = p // { content = content.value; }; + } + ) + ) (value: { inherit value; }); + + addDefaultRank = x: + if x ? rank then x + else x // { rank = defaultRank; }; + + rankValList = map (x: addDefaultRank (getRankVal x)) valList; + + cmp = x: y: + builtins.lessThan x.rank y.rank; + in + map (x: x.value) (sort cmp rankValList); + }