lib: let inherit (lib) attrNames filterAttrs foldl generators partition ; inherit (lib.strings) concatMapStrings hasPrefix ; /** Convert a structured Nix attribute set into Hyprland's configuration format. This function takes a nested attribute set and converts it into Hyprland-compatible configuration syntax, supporting top, bottom, and regular command sections. Commands are flattened using the `flattenAttrs` function, and attributes are formatted as `key = value` pairs. Lists are expanded as duplicate keys to match Hyprland's expected format. Configuration: * `topCommandsPrefixes` - A list of prefixes to define **top** commands (default: `["$"]`). * `bottomCommandsPrefixes` - A list of prefixes to define **bottom** commands (default: `[]`). Attention: - The function ensures top commands appear **first** and bottom commands **last**. - The generated configuration is a **single string**, suitable for writing to a config file. - Lists are converted into multiple entries, ensuring compatibility with Hyprland. # Inputs Structured function argument: : topCommandsPrefixes (optional, default: `["$"]`) : A list of prefixes that define **top** commands. Any key starting with one of these prefixes will be placed at the beginning of the configuration. : bottomCommandsPrefixes (optional, default: `[]`) : A list of prefixes that define **bottom** commands. Any key starting with one of these prefixes will be placed at the end of the configuration. Value: : The attribute set to be converted to Hyprland configuration format. # Type ``` toHyprlang :: AttrSet -> AttrSet -> String ``` # Examples :::{.example} ```nix let config = { "$mod" = "SUPER"; monitor = { "HDMI-A-1" = "1920x1080@60,0x0,1"; }; exec = [ "waybar" "dunst" ]; }; in lib.toHyprlang {} config ``` **Output:** ```nix "$mod = SUPER" "monitor:HDMI-A-1 = 1920x1080@60,0x0,1" "exec = waybar" "exec = dunst" ``` ::: */ toHyprlang = { topCommandsPrefixes ? ["$"], bottomCommandsPrefixes ? [], }: attrs: let toHyprlang' = attrs: let # Specially configured `toKeyValue` generator with support for duplicate keys # and a legible key-value separator. mkCommands = generators.toKeyValue { mkKeyValue = generators.mkKeyValueDefault {} " = "; listsAsDuplicateKeys = true; indent = ""; # No indent, since we don't have nesting }; # Flatten the attrset, combining keys in a "path" like `"a:b:c" = "x"`. # Uses `flattenAttrs` with a colon separator. commands = flattenAttrs (p: k: "${p}:${k}") attrs; # General filtering function to check if a key starts with any prefix in a given list. filterCommands = list: n: foldl (acc: prefix: acc || hasPrefix prefix n) false list; # Partition keys into top commands and the rest result = partition (filterCommands topCommandsPrefixes) (attrNames commands); topCommands = filterAttrs (n: _: builtins.elem n result.right) commands; remainingCommands = removeAttrs commands result.right; # Partition remaining commands into bottom commands and regular commands result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong; bottomCommands = filterAttrs (n: _: builtins.elem n result2.right) remainingCommands; regularCommands = removeAttrs remainingCommands result2.right; in # Concatenate strings from mapping `mkCommands` over top, regular, and bottom commands. concatMapStrings mkCommands [ topCommands regularCommands bottomCommands ]; in toHyprlang' attrs; /** Flatten a nested attribute set into a flat attribute set, using a custom key separator function. This function recursively traverses a nested attribute set and produces a flat attribute set where keys are joined using a user-defined function (`pred`). It allows transforming deeply nested structures into a single-level attribute set while preserving key-value relationships. Configuration: * `pred` - A function `(string -> string -> string)` defining how keys should be concatenated. # Inputs Structured function argument: : pred (required) : A function that determines how parent and child keys should be combined into a single key. It takes a `prefix` (parent key) and `key` (current key) and returns the joined key. Value: : The nested attribute set to be flattened. # Type ``` flattenAttrs :: (String -> String -> String) -> AttrSet -> AttrSet ``` # Examples :::{.example} ```nix let nested = { a = "3"; b = { c = "4"; d = "5"; }; }; separator = (prefix: key: "${prefix}.${key}"); # Use dot notation in lib.flattenAttrs separator nested ``` **Output:** ```nix { "a" = "3"; "b.c" = "4"; "b.d" = "5"; } ``` ::: */ flattenAttrs = pred: attrs: let flattenAttrs' = prefix: attrs: builtins.foldl' ( acc: key: let value = attrs.${key}; newKey = if prefix == "" then key else pred prefix key; in acc // ( if builtins.isAttrs value then flattenAttrs' newKey value else {"${newKey}" = value;} ) ) {} (builtins.attrNames attrs); in flattenAttrs' "" attrs; in { inherit flattenAttrs toHyprlang; }