niri: move config to nix

Signed-off-by: faukah <fau@faukah.com>
Change-Id: I6a6a6964cffaba7ce75488a48739398758d49363
This commit is contained in:
fau 2025-07-27 18:06:22 +02:00 committed by faukah
commit c5915e1d24
4 changed files with 690 additions and 434 deletions

323
modules/wms/niri/kdl.nix Normal file
View file

@ -0,0 +1,323 @@
# https://github.com/NixOS/nixpkgs/pull/426828
# The KDL document language (https://kdl.dev/)
/*
Original file: formats.nix
Copyright (c) 2003-2025 Eelco Dolstra and the Nixpkgs/NixOS contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* Modificatied by faukah (2025)
*
* This modified version is part of the Nichts project and is licensed under
* the GNU General Public License v3.0.
*
* See <https://www.gnu.org/licenses/> for details.
*/
{
lib,
pkgs,
...
}:
let
inherit (lib.types)
listOf
attrsOf
str
bool
number
oneOf
nullOr
submodule
coercedTo
path
;
inherit (lib.lists) isList;
inherit (lib.attrsets) isAttrs;
inherit (lib.trivial) isBool;
in
{
type = (
let
mergeUniq =
mergeOne:
lib.mergeUniqueOption {
message = "";
merge =
loc: defs:
let
inherit (lib.head defs) file value;
in
mergeOne file loc value;
};
mergeFlat =
elemType: loc: file: value:
if value ? _type then
throw "${lib.showOption loc} has wrong type: expected '${elemType.description}', got `${value._type}`"
else
elemType.merge loc [ { inherit file value; } ];
uniqFlatListOf =
elemType:
lib.mkOptionType {
name = "uniqFlatListOf";
inherit (listOf elemType) description descriptionClass;
check = isList;
merge = mergeUniq (
file: loc: lib.imap1 (i: mergeFlat elemType (loc ++ [ "[entry ${toString i}]" ]) file)
);
};
uniqFlatAttrsOf =
elemType:
lib.mkOptionType {
name = "uniqFlatAttrsOf";
inherit (attrsOf elemType) description descriptionClass;
check = isAttrs;
merge = mergeUniq (file: loc: lib.mapAttrs (name: mergeFlat elemType (loc ++ [ name ]) file));
};
kdlUntypedValue = lib.mkOptionType {
name = "kdlUntypedValue";
description = "KDL value without type annotation";
descriptionClass = "noun";
inherit
(nullOr (oneOf [
str
bool
number
path
]))
check
merge
;
};
kdlTypedValue = lib.mkOptionType {
name = "kdlTypedValue";
description = "KDL value with type annotation";
descriptionClass = "noun";
check = isAttrs;
merge =
(submodule {
options = {
type = lib.mkOption {
type = nullOr str;
default = null;
description = ''
[Type annotation](https://kdl.dev/spec/#name-type-annotation) of a [KDL value](https://kdl.dev/spec/#name-value).
'';
};
value = lib.mkOption {
type = kdlUntypedValue;
description = ''
Scalar part of a [KDL value](https://kdl.dev/spec/#name-value)
'';
};
};
}).merge;
};
# https://kdl.dev/spec/#name-value
kdlValue = lib.mkOptionType {
name = "kdlValue";
description = "KDL value";
descriptionClass = "noun";
inherit (coercedTo kdlUntypedValue (value: { inherit value; }) kdlTypedValue) check merge;
nestedTypes = {
type = nullOr str;
scalar = kdlUntypedValue;
};
};
# https://kdl.dev/spec/#name-node
kdlNode = lib.mkOptionType {
name = "kdlNode";
description = "KDL node";
descriptionClass = "noun";
check = isAttrs;
merge =
(submodule {
options = {
type = lib.mkOption {
type = nullOr str;
default = null;
description = ''
[Type annotation](https://kdl.dev/spec/#name-type-annotation) of a KDL node.
'';
};
name = lib.mkOption {
type = str;
description = ''
Name of a [KDL node](https://kdl.dev/spec/#name-node).
'';
};
arguments = lib.mkOption {
type = uniqFlatListOf kdlValue;
default = [ ];
description = ''
[Arguments](https://kdl.dev/spec/#name-argument) of a KDL node.
'';
};
properties = lib.mkOption {
type = uniqFlatAttrsOf kdlValue;
default = { };
description = ''
[Properties](https://kdl.dev/spec/#name-property) of a KDL node.
'';
};
children = lib.mkOption {
type = kdlDocument;
default = [ ];
description = ''
[Children](https://kdl.dev/spec/#children-block) of a KDL node.
'';
};
};
}).merge;
nestedTypes = {
name = str;
type = nullOr str;
arguments = uniqFlatListOf kdlValue;
properties = uniqFlatAttrsOf kdlValue;
children = kdlDocument;
};
};
kdlDocument = lib.mkOptionType {
name = "kdlDocument";
description = "KDL document";
descriptionClass = "noun";
check = isList;
merge = mergeUniq (
file:
let
mergeDocument =
loc: toplevel:
builtins.concatLists (
lib.imap1 (i: mergeDocumentEntry (loc ++ [ "[entry ${toString i}]" ])) toplevel
);
mergeDocumentEntry =
loc: value:
let
inherit (lib.options) showDefs;
defs = [ { inherit file value; } ];
in
if isList value then
mergeDocument loc value
else if value ? _type then
if value._type == "if" then
if isBool value.condition then
if value.condition then mergeDocumentEntry loc value.content else [ ]
else
throw "`mkIf` called with non-Boolean condition at ${lib.showOption loc}. Definition value:${showDefs defs}"
else if value._type == "merge" then
throw ''
${lib.showOption loc} has wrong type: expected a KDL node or document, got 'merge'.
note: `mkMerge` is potentially ambiguous in a KDL document, as "merging" is application-specific. if you intended to "splat" all the nodes in a KDL document, you can just insert the list of nodes directly. you can arbitrarily nest KDL documents, and they will be concatenated.
''
else
throw "${lib.showOption loc} has wrong type: expected a KDL node or document, got '${value._type}'. Definition value:${showDefs defs}"
else if kdlNode.check value then
[ (kdlNode.merge loc [ { inherit file value; } ]) ]
else
throw "${lib.showOption loc} has wrong type: expected a KDL node or document. Definition value:${showDefs defs}";
in
mergeDocument
);
nestedTypes.node = kdlNode;
};
in
kdlDocument
);
lib = {
/**
Helper function for generating attrsets expected by pkgs.formats.kdl
# Example
```nix
let
settingsFormat = pkgs.formats.kdl { };
inherit (settingsFormat.lib) node;
in
settingsFormat.generate "sample.kdl" [
(node "foo" null [ ] { } [
(node "bar" null [ "baz" ] { a = 1; } [ ])
])
]
```
# Arguments
name
: The name of the node, represented by a string
type
: The type annotation of the node, represented by a string, or null to avoid generating a type annotation
arguments
: The arguments of the node, represented as a list of KDL values
properties
: The properties of the node, represented as an attrset of KDL values
children
: The children of the node, represented as a list of nodes
*/
node = name: type: arguments: properties: children: {
inherit
name
type
arguments
properties
children
;
};
/**
Helper function for generating the format of a typed value as expected by pkgs.formats.kdl
type
: The type of the value, represented by a string
value
: The value itself
*/
typed = type: value: { inherit type value; };
};
generate =
name: value:
pkgs.callPackage (
{ runCommand, jsonkdl }:
runCommand name
{
nativeBuildInputs = [ jsonkdl ];
value = builtins.toJSON value;
passAsFile = [ "value" ];
}
''
jsonkdl --kdl-v1 -- "$valuePath" "$out"
''
) { };
}