Skip to content

Commit

Permalink
lib: add flattenAttrs, remove category processing
Browse files Browse the repository at this point in the history
Flattening attributes means we no longer need to process categories
separately. For all intents and purposes, they do not exist.

Simplify the codebase once again, while introducing an easy to grasp
recursive function.

Add a bit of documentation for toHyprlang, though I doubt it's clear
enough even now. Still needs proper NixDoc.
  • Loading branch information
fufexan committed Feb 20, 2025
1 parent 241dfd0 commit d723956
Showing 1 changed file with 100 additions and 37 deletions.
137 changes: 100 additions & 37 deletions nix/lib.nix
Original file line number Diff line number Diff line change
@@ -1,59 +1,122 @@
lib: with lib; {
lib: with lib; let
toHyprlang = {
topCommandsPrefixes ? ["$"],
bottomCommandsPrefixes ? [],
}: attrs: let
toHyprlang' = attrs: let
toStr = x:
if isBool x
then boolToString x
else toString x;

categories = filterAttrs (n: v: isAttrs v || (isList v && all isAttrs v)) attrs;
mkCategory = parent: attrs:
if lib.isList attrs
then concatMapStringsSep "\n" (a: mkCategory parent a) attrs
else
concatStringsSep "\n" (
mapAttrsToList (
k: v:
if isAttrs v
then mkCategory "${parent}:${k}" v
else if isList v
then concatMapStringsSep "\n" (item: "${parent}:${k} = ${toStr item}") v
else "${parent}:${k} = ${toStr v}"
)
attrs
);

mkCommands = toKeyValue {
mkKeyValue = mkKeyValueDefault {} " = ";
# Specially configured `toKeyValue` generator with support for duplicate
# keys and legible key-value separator.
mkCommands = generators.toKeyValue {
mkKeyValue = generators.mkKeyValueDefault {} " = ";
listsAsDuplicateKeys = true;
# No indent, since we don't have nesting
indent = "";
};

allCommands = filterAttrs (n: v: !(isAttrs v || (isList v && all isAttrs v))) attrs;
# Flatten the attrset, combining keys in a "path" like `"a:b:c" = "x"`.
# See `flattenAttrs` for more info.
commands = flattenAttrs (p: k: "${p}:${k}") attrs;

# General filtering command. Used for filtering top/bottom commands.
# Takes a list of prefixes and an element to check.
# Returns true if any of the prefixes matched, otherwise false.
filterCommands = list: n: foldl (acc: prefix: acc || hasPrefix prefix n) false list;

# Get topCommands attr names
result = partition (filterCommands topCommandsPrefixes) (attrNames allCommands);
# Filter top commands from all commands
topCommands = filterAttrs (n: _: (builtins.elem n result.right)) allCommands;
# FIXME(docs): improve explanations for the below amalgamation

# Get topCommands attribute names
result = partition (filterCommands topCommandsPrefixes) (attrNames commands);
# Filter top commands from all commands, using the attribute names
# previously obtained
topCommands = filterAttrs (n: _: (builtins.elem n result.right)) commands;
# Remaining commands = allcallCommands - topCommands
remainingCommands = removeAttrs allCommands result.right;
remainingCommands = removeAttrs commands result.right;

# Get bottomCommands attr names
# Get bottomCommands attr names from commands remaining (in result.wrong)
result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong;
# Filter bottom commands from remainingCommands
# Filter bottom commands from remainingCommands using the attribute names
# previously obtained
bottomCommands = filterAttrs (n: _: (builtins.elem n result2.right)) remainingCommands;
# Regular commands = allCommands - topCommands - bottomCommands
regularCommands = removeAttrs remainingCommands result2.right;
in
mkCommands topCommands
+ concatStringsSep "\n" (mapAttrsToList mkCategory categories)
+ mkCommands regularCommands
+ mkCommands bottomCommands;
# Concatenate the strings resulting from mapping `mkCommands` over the
# list of commands.
concatMapStrings mkCommands [
topCommands
regularCommands
bottomCommands
];
in
toHyprlang' attrs;


/**
Flatten a nested attribute set into a flat attribute set, joining keys with a user-defined function.
This function takes a function `pred` that determines how nested keys should be joined,
and an attribute set `attrs` that should be flattened.
## Example
```nix
let
nested = {
a = "3";
b = { c = "4"; d = "5"; };
};
separator = (prefix: key: "${prefix}.${key}"); # Use dot notation
in flattenAttrs separator nested
```
**Output:**
```nix
{
"a" = "3";
"b.c" = "4";
"b.d" = "5";
}
```
## Parameters
- **pred** : function `(string -> string -> string)`
A function that takes a prefix and a key and returns the new flattened key.
- **attrs** : attrset
The nested attribute set to be flattened.
## Returns
- **attrset** : A flattened attribute set where keys are joined according to `pred`.
## Notes
- This function works recursively for any level of nesting.
- It does not modify non-attribute values.
- If `pred` is `prefix: key: "${prefix}.${key}"`, it mimics dot notation.
*/
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;
}

0 comments on commit d723956

Please sign in to comment.