From 266fe92eaa4a36ecd6e50c22bfe502cea422c72b Mon Sep 17 00:00:00 2001 From: cpli Date: Tue, 20 Feb 2024 20:55:18 +0000 Subject: [PATCH] add: implement `utils.networking.portsToRanges` Signed-off-by: Janik H. --- utils/networking.nix | 102 +++++++++++++++++++++++++++++++++---------- 1 file changed, 79 insertions(+), 23 deletions(-) diff --git a/utils/networking.nix b/utils/networking.nix index 309a5d9..c7b43cd 100644 --- a/utils/networking.nix +++ b/utils/networking.nix @@ -1,24 +1,80 @@ -{ lib, ... }: { - /* - Generates inclusive portrange given a lower and a higher number - - Type: - utils.networking.portrange:: Int -> Int -> [ Int ] - */ - portrange = - from: - to: - let - helper = input: - if input.counter >= 0 then - helper - { - list = [ (from + input.counter) ] ++ input.list; - counter = input.counter - 1; - } else input.list; - in - lib.throwIf (from > to) "the second input has to be larger then the first one, otherwise you have a negative range which is impossible or not a range." - helper - { list = [ ]; counter = to - from; } - ; +{ lib, utils, ... }: let + inherit (builtins) length head; + inherit (lib.trivial) throwIf; + inherit (lib.strings) concatStrings; + inherit (lib.lists) foldr last range; + inherit (utils.networking) splitWhen; +in { + + /** + # Description + + Generates inclusive portrange given a lower and a higher number, + additionally throws an error if the end of the range is not strictly + greater. + + # Type + + ``` + utils.networking.portrange :: Int -> Int -> [ Int ] + ``` + */ + portrange = from: to: let ports = range from to; + in throwIf (length ports == 0) (concatStrings [ + "the second input has to be larger then the first one, " + "otherwise you have a negative range which is impossible " + "or not a range." + ]) ports; + + /** + # Example + + ```nix + let formatRange = { start, end }: "${toString start}-${toString end}" + in map (x: if isAttrs x then formatRange x else x) + (portsToRanges [ 1 2 3 ]) + => [ "1-3" ] + ``` + + # Type + + ``` + utils.networking.portsToRanges :: [Int] -> [Int + { start end :: Int }] + ``` + */ + portsToRanges = ports: let + partitioned = splitWhen (a: b: a + 1 == b) ports; + format = range: + if length range == 1 + then head range + else { start = head range; end = last range; }; + in map format partitioned; + + /** + # Example + + ```nix + # split into strictly ordered sublists + splitWhen (a: b: a < b) [ 7 8 9 4 5 6 1 2 3 ] + => [ [ 7 8 9 ] [ 4 5 6 ] [ 1 2 3 ] ] + ``` + + # Type + + ``` + utils.networking.splitWhen :: (a -> a -> Bool) -> [a] -> [[a]] + ``` + */ + splitWhen = p: xs: let + unfold = { xs, xss }: + if length xs == 0 then xss else [xs] ++ xss; + + addElement = x: { xs, xss }: + if length xs == 0 + then { xs = [x]; inherit xss; } + else if p x (head xs) + then { xs = [x] ++ xs; inherit xss; } + else { xs = [x]; xss = [xs] ++ xss; }; + in unfold (foldr addElement { xs = []; xss = []; } xs); + }