From 89a9fa0f3fc2a22b4d828d46afb1b56d9f296ce7 Mon Sep 17 00:00:00 2001 From: Anton Mosich Date: Mon, 1 Sep 2025 23:40:41 +0200 Subject: [PATCH] generators: rewrite toSCFG The former toSCFG implementation had several shortcomings which did not consider a few possibilities SCFG provides. Details were lined out in #7465. The new interface needs more verbosity, but reflects better the properties of the SCFG format. I also chose to use the names used in the [Specification]. [Specification]: https://git.sr.ht/~emersion/scfg#specification-draft --- modules/lib/generators.nix | 94 ++++++------------- tests/lib/generators/toscfg-empty.nix | 2 +- .../lib/generators/toscfg-example-result.txt | 3 +- tests/lib/generators/toscfg-example.nix | 71 +++++++++----- 4 files changed, 80 insertions(+), 90 deletions(-) diff --git a/modules/lib/generators.nix b/modules/lib/generators.nix index 7ee2afc72..ee8461df5 100644 --- a/modules/lib/generators.nix +++ b/modules/lib/generators.nix @@ -211,9 +211,15 @@ toSCFG = { }: let - inherit (lib) concatStringsSep mapAttrsToList any; + inherit (lib) concatStringsSep any; inherit (builtins) typeOf replaceStrings elem; + filterNullDirectives = lib.filter ( + directive: + !(directive ? "params" || directive ? "children") + || !(directive.params or [ null ] == [ null ] && directive.children or [ ] == [ ]) + ); + # ListOf String -> String indentStrings = let @@ -221,8 +227,8 @@ # the strings themselves *will* contain newlines, so you need # to normalize the list by joining and resplitting them. unlines = lib.splitString "\n"; - lines = lib.concatStringsSep "\n"; - indentAll = lines: concatStringsSep "\n" (map (x: " " + x) lines); + lines = concatStringsSep "\n"; + indentAll = lines: concatStringsSep "\n" (map (x: "\t" + x) lines); in stringsWithNewlines: indentAll (unlines (lines stringsWithNewlines)); @@ -256,7 +262,7 @@ "\\" "\r" "\n" - " " + "\t" ]; # OneOf [Int Float String Bool] -> String @@ -284,7 +290,7 @@ # Bool -> ListOf (OneOf [Int Float String Bool]) -> String toOptParamsString = cond: list: - lib.optionalString (cond) ( + lib.optionalString cond ( lib.pipe list [ (map literalValueToString) (concatStringsSep " ") @@ -292,65 +298,25 @@ ] ); - # Attrset Conversion - # String -> AttrsOf Anything -> String - convertAttrsToSCFG = - name: attrs: - let - optParamsString = toOptParamsString (attrs ? "_params") attrs._params; - in - '' - ${name}${optParamsString} { - ${indentStrings (convertToAttrsSCFG' attrs)} - }''; - - # Attrset Conversion - # AttrsOf Anything -> ListOf String - convertToAttrsSCFG' = - attrs: - mapAttrsToList convertAttributeToSCFG ( - lib.filterAttrs (name: val: !isNull val && name != "_params") attrs - ); - - # List Conversion - # String -> ListOf (OneOf [Int Float String Bool]) -> String - convertListOfFlatAttrsToSCFG = - name: list: - let - optParamsString = toOptParamsString (list != [ ]) list; - in - "${name}${optParamsString}"; - - # Combined Conversion - # String -> Anything -> String - convertAttributeToSCFG = - name: value: - lib.throwIf (name == "") "Directive must not be empty" ( - let - vType = typeOf value; - in - if - elem vType [ - "int" - "float" - "bool" - "string" - ] - then - "${name} ${literalValueToString value}" - else if vType == "set" then - convertAttrsToSCFG name value - else if vType == "list" then - convertListOfFlatAttrsToSCFG name value - else - throw '' - Cannot convert type `(${typeOf value})` to SCFG: - ${name} = ${toString value} - '' - ); + # Directive Conversion + # ListOf NameParamChildrenTriplet -> ListOf String + convertDirectivesToSCFG = + directives: + map ( + directive: + (literalValueToString directive.name) + + toOptParamsString (directive ? "params" && directive.params != null) directive.params + + lib.optionalString (directive ? "children" && directive.children != null) ( + " " + + '' + { + ${indentStrings (convertDirectivesToSCFG directive.children)} + }'' + ) + ) (filterNullDirectives directives); in - attrs: - lib.optionalString (attrs != { }) '' - ${concatStringsSep "\n" (convertToAttrsSCFG' attrs)} + directives: + lib.optionalString (directives != [ ]) '' + ${lib.concatStringsSep "\n" (convertDirectivesToSCFG directives)} ''; } diff --git a/tests/lib/generators/toscfg-empty.nix b/tests/lib/generators/toscfg-empty.nix index 4d52ce3e7..fb1d6f74e 100644 --- a/tests/lib/generators/toscfg-empty.nix +++ b/tests/lib/generators/toscfg-empty.nix @@ -1,7 +1,7 @@ { lib, ... }: { - home.file."toscfg-empty-result.txt".text = lib.hm.generators.toSCFG { } { }; + home.file."toscfg-empty-result.txt".text = lib.hm.generators.toSCFG { } [ ]; nmt.script = '' assertFileContent \ diff --git a/tests/lib/generators/toscfg-example-result.txt b/tests/lib/generators/toscfg-example-result.txt index 7a538d5a1..45810900e 100644 --- a/tests/lib/generators/toscfg-example-result.txt +++ b/tests/lib/generators/toscfg-example-result.txt @@ -2,8 +2,9 @@ dir { blk1 p1 "\"p2\"" { sub1 arg11 arg12 sub2 arg21 arg22 + sub2 arg1 arg2 sub3 arg31 arg32 { - sub-sub1 + "sub sub1" sub-sub2 arg321 arg322 } } diff --git a/tests/lib/generators/toscfg-example.nix b/tests/lib/generators/toscfg-example.nix index ada602148..93c65994b 100644 --- a/tests/lib/generators/toscfg-example.nix +++ b/tests/lib/generators/toscfg-example.nix @@ -1,35 +1,58 @@ { lib, ... }: { - home.file."toscfg-example-result.txt".text = lib.hm.generators.toSCFG { } { - dir = { - blk1 = { - _params = [ + home.file."toscfg-example-result.txt".text = lib.hm.generators.toSCFG { } ( + lib.singleton { + name = "dir"; + children = lib.singleton { + name = "blk1"; + params = [ "p1" ''"p2"'' ]; - sub1 = [ - "arg11" - "arg12" + children = [ + { + name = "sub1"; + params = [ + "arg11" + "arg12" + ]; + } + { + name = "sub2"; + params = [ + "arg21" + "arg22" + ]; + } + { + name = "sub2"; + params = [ + "arg1" + "arg2" + ]; + } + { + name = "sub3"; + params = [ + "arg31" + "arg32" + ]; + children = [ + { name = "sub sub1"; } + { + name = "sub-sub2"; + params = [ + "arg321" + "arg322" + ]; + } + ]; + } ]; - sub2 = [ - "arg21" - "arg22" - ]; - sub3 = { - _params = [ - "arg31" - "arg32" - ]; - sub-sub1 = [ ]; - sub-sub2 = [ - "arg321" - "arg322" - ]; - }; }; - }; - }; + } + ); nmt.script = '' assertFileContent \