17 May 2025 |
| oddlama changed their display name from oddlama to Malte. | 20:12:20 |
20 May 2025 |
| kongrooparadox joined the room. | 21:44:52 |
21 May 2025 |
| oddlama changed their display name from Malte to oddlama. | 17:42:04 |
SomeoneSerge (UTC+U[-12,12]) | I see that nixos/lib and nixos/modules use normal path values in _file (except for modulesPath = toString ./eval-config.nix ), but somehow in man configuration.nix we get relative paths without hashes? How does that happen | 17:48:12 |
Matt Sturgeon | path values only get copied to the store when used with string interpolation (e.g. "${./some/path}" ). If you instead stringify them with toString , then they are not copied.
E.g.
"${./foo}"
=> "/nix/store/r1l6z113nnzx44iiqp728f50lpczp577-foo"
toString ./foo
=> "/home/matt/foo"
./foo
=> /home/matt/foo
| 17:53:58 |
SomeoneSerge (UTC+U[-12,12]) | Ohhh so nixos/modules actually does pass strings around and not paths? | 18:00:37 |
SomeoneSerge (UTC+U[-12,12]) | nixos/lib/eval-config.nix
71- pkgsModule = rec {
72: _file = ./eval-config.nix;
73: key = _file;
| 18:02:08 |
SomeoneSerge (UTC+U[-12,12]) | This is 100% a "path" | 18:02:19 |
SomeoneSerge (UTC+U[-12,12]) | $ man configuration.nix
...
nixpkgs.pkgs
...
Declared by:
<nixpkgs/nixos/modules/misc/nixpkgs.nix>
| 18:03:29 |
SomeoneSerge (UTC+U[-12,12]) | So there is some magic happening that makes this ./eval-config.nix into a string that is relative to.... hmmm parent of the checkout | 18:03:57 |
Matt Sturgeon | IIRC specialArgs.modulesPath is removed if it is a prefix of the stringified _file . | 18:05:42 |
Matt Sturgeon |
actually does pass strings around
No, but when rendering files in errors, docs, etc they are stringified using toString | 18:06:40 |
| Spaenny joined the room. | 18:21:49 |
23 May 2025 |
SomeoneSerge (UTC+U[-12,12]) | Btw that magic was in pkgs/ in nixosOptionsDoc | 22:32:42 |
SomeoneSerge (UTC+U[-12,12]) | Next one! Given (evalModules {...}).options and a path in options , is there a ready tool to tell which definition (with location) won the priority? I see I can access definitionsWithLocations which is an unfiltered list, and, to further complicate things, in case of an attrsOf option there's just the top-level list, which doesn't directly relate paths under the option to definitions or locations. | 22:36:26 |
Matt Sturgeon | For simple/flat options, all the definition listed in the option are the ones that "won" the priority.
Specifically, all of opt.definitions and opt.definitionsWithLocations have opt.highestPrio .
That's because most wrappers like mkIf , mkMerge , mkOverride , and mkOrder are resolved before creating the option's definitions list.
However, this is only done for the option's definitions (i.e. the top-level). Wrappers on nested attrs are applied by the option's type when it merges the option's definitions.
I don't know of an "easy" way to filter for definitions of a specific attr on an attrsOf type.
There are some functions like lib.modules.mergeAttrDefinitionsWithPrio , so maybe something similar exists or could be written that exposes definition location for attrsOf sub-attrs?
If you were dealing with a submodule, and had an explicit suboption declared, then you could access the suboption's definitions within the submodule eval. E.g., you could expose them to the wider module eval by defining an internal option in the submodule. | 22:48:40 |
24 May 2025 |
jappie | I'm trying to create a settings option for services.dovecot2 (https://github.com/NixOS/nixpkgs/pull/388463/) and running into some issues trying to use a submodule for the type of the settings option.
Dovecot allows you to do something like this:
# dovecot config format:
service imap-login {
restart_request_count = 1
inet_listener imaps {
port = 993
ssl = yes
}
}
# Nix:
"service imap-login" = {
restart_request_count = 1;
"inet_listener imaps" = {
port = 993;
ssl = true;
};
};
Technically the following works for options.settings.type :
type =
let
inherit (lib.types) bool int listOf nullOr oneOf str submodule;
inherit (lib) elemAt singleton split;
any = oneOf [ int str bool (lib.types.lazyAttrsOf configValueType) ];
configValueType = nullOr (oneOf [
any
(listOf any)
]);
in
configValueType;
But a better solution would be to define the section type & section name (e.g. service & imap-login or inet_listener & imaps from above) in the options part of a submodule instead of hiding it in the parser that writes the config file:
type =
let
inherit (lib.types) attrsOf bool int listOf nullOr oneOf str submodule;
inherit (lib) elemAt singleton split;
section = submodule (
{ name, config, ... }:
let
# split on first space
splits = elemAt (elemAt (split "^([^ ]+)( +(.+))?$" name) 1);
typeDefault = splits 0;
nameDefault = splits 1; # 2;
in
{
options = {
section = {
type = mkOption {
description = "...";
type = str;
default = typeDefault;
};
name = mkOption {
description = "...";
type = nullOr str;
default = nameDefault;
};
};
};
freeformType = configValueType;
}
);
any = oneOf [ int str bool (lib.types.lazyAttrsOf section) ];
configValueType = nullOr (oneOf [
any
(listOf any)
]);
in
configValueType;
However this throws and then infrecs trying to evaluate description :
… from call site
at /nix/store/iarry5sq6zg1wm91f4dn68a91nqs369w-source/nixos/modules/services/mail/dovecot.nix:794:65:
793| }
794| // optionalAttrs (cfg.settings ? mail_uid && cfg.settings.mail_uid != null && cfg.createMailUser) {
| ^
795| ${cfg.settings.mail_uid} =
… while calling anonymous lambda
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:818:27:
817| (
818| name: defs:
| ^
819| let
… while evaluating the attribute 'optionalValue.value'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/modules.nix:1148:41:
1147|
1148| optionalValue = if isDefined then { value = mergedValue; } else { };
| ^
1149| };
… while calling the 'throw' builtin
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/modules.nix:1139:11:
1138| in
1139| throw "A definition for option `${showOption loc}' is not of type `${type.description}'. Definition values:${showDefs allInvalid}"
| ^
1140| else
… while evaluating the attribute 'description'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:9:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… while evaluating description
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:26:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… while evaluating the attribute '_module.freeformType.description'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:9:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… while evaluating description
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:26:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… from call site
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:1026:13:
1025| description = "null or ${
1026| optionDescriptionPhrase (class: class == "noun" || class == "conjunction") elemType
| ^
1027| }";
… while calling 'optionDescriptionPhrase'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:290:23:
289| optionDescriptionPhrase =
290| unparenthesize: t:
| ^
291| if unparenthesize (t.descriptionClass or null) then t.description else "(${t.description})";
… while evaluating the attribute 'description'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:9:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… while evaluating description
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:26:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… from call site
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:1383:18:
1382| else
1383| "${optionDescriptionPhrase (class: class == "noun" || class == "conjunction") t1} or ${
| ^
1384| optionDescriptionPhrase (
… while calling 'optionDescriptionPhrase'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:290:23:
289| optionDescriptionPhrase =
290| unparenthesize: t:
| ^
291| if unparenthesize (t.descriptionClass or null) then t.description else "(${t.description})";
… while evaluating the attribute 'description'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:9:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… while evaluating description
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:26:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… from call site
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:1384:17:
1383| "${optionDescriptionPhrase (class: class == "noun" || class == "conjunction") t1} or ${
1384| optionDescriptionPhrase (
| ^
1385| class: class == "noun" || class == "conjunction" || class == "composite"
… while calling 'optionDescriptionPhrase'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:290:23:
289| optionDescriptionPhrase =
290| unparenthesize: t:
| ^
291| if unparenthesize (t.descriptionClass or null) then t.description else "(${t.description})";
… while evaluating the attribute 'description'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:9:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… while evaluating description
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:26:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… from call site
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:808:22:
807| (if lazy then "lazy attribute set" else "attribute set")
808| + " of ${optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType}";
| ^
809| descriptionClass = "composite";
… while calling 'optionDescriptionPhrase'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:290:23:
289| optionDescriptionPhrase =
290| unparenthesize: t:
| ^
291| if unparenthesize (t.descriptionClass or null) then t.description else "(${t.description})";
… while evaluating the attribute 'description'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:9:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
error: infinite recursion encountered
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:9:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
Is what I'm trying to do here not possible with submodules due to the recursion or am I missing something?
| 12:06:15 |
jappie | * I'm trying to create a settings option for services.dovecot2 (https://github.com/NixOS/nixpkgs/pull/388463/) and running into some issues trying to use a submodule for the type of the settings option.
Dovecot allows you to do something like this:
# dovecot config format:
service imap-login {
restart_request_count = 1
inet_listener imaps {
port = 993
ssl = yes
}
}
# Nix:
"service imap-login" = {
restart_request_count = 1;
"inet_listener imaps" = {
port = 993;
ssl = true;
};
};
Technically the following works for options.settings.type :
type =
let
inherit (lib.types) bool int listOf nullOr oneOf str submodule;
inherit (lib) elemAt singleton split;
any = oneOf [ int str bool (lib.types.lazyAttrsOf configValueType) ];
configValueType = nullOr (oneOf [
any
(listOf any)
]);
in
configValueType;
But a better solution would be to define the section type & section name (e.g. service & imap-login or inet_listener & imaps from above) in the options part of a submodule instead of hiding it in the parser that writes the config file:
type =
let
inherit (lib.types) attrsOf bool int listOf nullOr oneOf str submodule;
inherit (lib) elemAt singleton split;
section = submodule (
{ name, config, ... }:
let
# split on first space
splits = elemAt (elemAt (split "^([^ ]+)( +(.+))?$" name) 1);
typeDefault = splits 0;
nameDefault = splits 1;
in
{
options = {
section = {
type = mkOption {
description = "...";
type = str;
default = typeDefault;
};
name = mkOption {
description = "...";
type = nullOr str;
default = nameDefault;
};
};
};
freeformType = configValueType;
}
);
any = oneOf [ int str bool (lib.types.lazyAttrsOf section) ];
configValueType = nullOr (oneOf [
any
(listOf any)
]);
in
configValueType;
However this throws and then infrecs trying to evaluate description :
… from call site
at /nix/store/iarry5sq6zg1wm91f4dn68a91nqs369w-source/nixos/modules/services/mail/dovecot.nix:794:65:
793| }
794| // optionalAttrs (cfg.settings ? mail_uid && cfg.settings.mail_uid != null && cfg.createMailUser) {
| ^
795| ${cfg.settings.mail_uid} =
… while calling anonymous lambda
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:818:27:
817| (
818| name: defs:
| ^
819| let
… while evaluating the attribute 'optionalValue.value'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/modules.nix:1148:41:
1147|
1148| optionalValue = if isDefined then { value = mergedValue; } else { };
| ^
1149| };
… while calling the 'throw' builtin
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/modules.nix:1139:11:
1138| in
1139| throw "A definition for option `${showOption loc}' is not of type `${type.description}'. Definition values:${showDefs allInvalid}"
| ^
1140| else
… while evaluating the attribute 'description'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:9:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… while evaluating description
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:26:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… while evaluating the attribute '_module.freeformType.description'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:9:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… while evaluating description
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:26:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… from call site
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:1026:13:
1025| description = "null or ${
1026| optionDescriptionPhrase (class: class == "noun" || class == "conjunction") elemType
| ^
1027| }";
… while calling 'optionDescriptionPhrase'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:290:23:
289| optionDescriptionPhrase =
290| unparenthesize: t:
| ^
291| if unparenthesize (t.descriptionClass or null) then t.description else "(${t.description})";
… while evaluating the attribute 'description'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:9:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… while evaluating description
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:26:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… from call site
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:1383:18:
1382| else
1383| "${optionDescriptionPhrase (class: class == "noun" || class == "conjunction") t1} or ${
| ^
1384| optionDescriptionPhrase (
… while calling 'optionDescriptionPhrase'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:290:23:
289| optionDescriptionPhrase =
290| unparenthesize: t:
| ^
291| if unparenthesize (t.descriptionClass or null) then t.description else "(${t.description})";
… while evaluating the attribute 'description'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:9:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… while evaluating description
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:26:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… from call site
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:1384:17:
1383| "${optionDescriptionPhrase (class: class == "noun" || class == "conjunction") t1} or ${
1384| optionDescriptionPhrase (
| ^
1385| class: class == "noun" || class == "conjunction" || class == "composite"
… while calling 'optionDescriptionPhrase'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:290:23:
289| optionDescriptionPhrase =
290| unparenthesize: t:
| ^
291| if unparenthesize (t.descriptionClass or null) then t.description else "(${t.description})";
… while evaluating the attribute 'description'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:9:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… while evaluating description
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:26:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
… from call site
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:808:22:
807| (if lazy then "lazy attribute set" else "attribute set")
808| + " of ${optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType}";
| ^
809| descriptionClass = "composite";
… while calling 'optionDescriptionPhrase'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:290:23:
289| optionDescriptionPhrase =
290| unparenthesize: t:
| ^
291| if unparenthesize (t.descriptionClass or null) then t.description else "(${t.description})";
… while evaluating the attribute 'description'
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:9:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
error: infinite recursion encountered
at /nix/store/p893dkrzm5rxvhnqh092prgi1a7dzmcy-source/lib/types.nix:266:9:
265| functor;
266| description = if description == null then name else description;
| ^
267| };
Is what I'm trying to do here not possible with submodules due to the recursion or am I missing something?
| 12:06:55 |
25 May 2025 |
Matt Sturgeon |
lazyAttrsOf
Slightly off topic, but this probably isn't what you want in a freeform settings type; if a use does settings.foo = mkIf false {} , then foo will be defined, but as an "empty value" stub; usually an empty value is a throw expression throwing something like "option settings.foo is used but not defined" . This will be a problem when you later want to serialize settings into the program's config format.
| 00:06:33 |
Matt Sturgeon | *
lazyAttrsOf
Slightly off topic, but this probably isn't what you want in a freeform settings type; if a user defines settings.foo = mkIf false {} , then foo will be defined, but as an "empty value" stub; usually an empty value is a throw expression throwing something like "option settings.foo is used but not defined" . This will be a problem when you later want to serialize settings into the program's config format.
| 00:06:48 |
Matt Sturgeon |
However this throws and then infrecs trying to evaluate description :
Typically, these RFC42-style types define their own description. Looking in pkgs/pkgs-lib/formats.nix you'll see descriptions like "TOML value", "Lua value", "JSON value", etc.
| 00:11:06 |
Matt Sturgeon |
section submodule of { type :: string, name :: string, ... :: configValueType }
It's neat how it auto-splits the attrname into type and name options, however one concern I have with your "section" type is naming conflicts. E.g. what would happen if the section needed to define actual config with the key "name" or "type" ?
There aren't many good solutions to this, and we have a similar issue with libconfuse sections in #401565. In fact, this program may even be using a libconfuse-compatible config format, so perhaps we can collaborate on a solution there?
One solution may be to _ prefix these options? this would indicate they are "internal" to the nix type, and should reduce the chance of naming conflicts.
Incidentally, libconfuse calls these elements "section name" and "section title" respectively. Libconfuse is further complicated by also supporting sections that don't take a "title", so we'd need to decide how to represent that. With your design, it could be that the title option is nullable.
| 00:20:34 |
Matt Sturgeon |
# split on first space
splits = elemAt (elemAt (split "^([^ ]+)( +(.+))?$" name) 1);
typeDefault = splits 0;
nameDefault = splits 1;
Personally I would match this with regex groups instead of trying to use split . The trouble with split is that it'll split an arbitrary number of times.
splits = builtins.match "([^ ]+) (.+)" name;
typeDefault = if splits == null then name else builtins.elemAt splits 0; # aka libconfuse "name"
nameDefault = if splits == null then null else builtins.elemAt splits 1; # aka libconfuse "title"
| 00:26:09 |
Matt Sturgeon | *
However this throws and then infrecs trying to evaluate description :
Typically, these RFC42-style types define their own description. Looking in pkgs/pkgs-lib/formats.nix you'll see descriptions like "TOML value", "Lua value", "JSON value", etc.
Specifying an explicit description should avoid the inf-rec:
in
configValueType // {
description = "TODO: write desc";
};
| 00:27:41 |
jappie | Thanks for the feedback! I used lazyAttrsOf to avoid some uncaught infinite recursion (maxCallDepth reached or something) in an earlier revision, but it seems fine to just use attrsOf now, and setting a description avoids the infrec.
It is eerie how similar libconfuse & dovecot's config formats are... I cannot find any reference to libconfuse in dovecot's source though. Btw, credit for coming up with section.{type,name} goes to 2xsaiko (Dovecot module maintainer).
one concern I have with your "section" type is naming conflicts. E.g. what would happen if the section needed to define actual config with the key "name" or "type"?
I'm not sure I understand what you're saying here... both name & type are in the section attrs, so unless some Dovecot setting or plugin has an attribute called section we should be fine.
Example:
settings = {
service = [ # this sets section.type = service; & section.name = "";
{
section.name = "imap"; # now this section will get parsed to "service imap { ... }"
restart_request_count = 1;
}
]
# or we can do this:
foobar = {
section = {
type = "service";
name = "imap";
};
restart_request_count = 1;
};
};
Though it's not a bad idea to prefix section with an underscore imo, better safe than sorry.
Incidentally, libconfuse calls these elements "section name" and "section title" respectively. Libconfuse is further complicated by also supporting sections that don't take a "title", so we'd need to decide how to represent that. With your design, it could be that the title option is nullable.
yeah this is literally dovecot lol
2xsaiko & I have started calling these elements section type & section name respectively, Dovecot 2.4 has 'named filters' (sections with only a type, e.g. auth_policy { server_url = foobar } is the same as auth_policy_server_url = foobar ) and named list filters of which there can be multiple with different names (e.g. namespace inbox {} , namespace virtual {} ).
So the title / name / second parameter of a section seems nullable for both Dovecot & libconfuse.
| 11:30:48 |
jappie | huh... the below code still throws
error: A definition for option `services.dovecot2.settings.mail_uid' is not of type `Dovecot config value'. Definition values:
- In `/nix/store/sgdcq1qbjgx0wsb1rwn2yi3k5vsr4pg6-source/hosts/Flugel/services/mail.nix': "dovemail"
even though "dovemail" is a string and str should be allowed
type =
let
inherit (lib.types) attrsOf bool int listOf nullOr oneOf str submodule;
section = submodule (
{ name, config, ... }: {
freeformType = configValueType;
}
);
configValueType =
nullOr (oneOf [
int
str
bool
(listOf configValueType)
(attrsOf section) # this doesn't work
# (attrsOf configValueType) # this works
])
// {
description = "Dovecot config value";
};
in
configValueType;
so putting a submodule with freeformType = configValueType in configValueType instead of referring directly to itself throws everything off
| 11:56:49 |
Matt Sturgeon |
so putting a submodule with freeformType = configValueType in configValueType instead of referring directly to itself throws everything off
I believe submodules only support attrsOf or lazyAttrsOf freeformTypes. So that may be part of the issue.
I'd need to look in more detail, ideally in vim instead of matrix, but maybe the relationship between the submodule and the oneOf should be tweaked?
| 12:00:17 |
jappie | and the reason for attrsOf section is because Dovecot allows the following:
service auth {
unix_listener auth-master {
# ...
}
}
both service auth and unix_listener auth-master are named list filters and thus need to support section.{type,name}
| 12:08:00 |
Matt Sturgeon |
I'm not sure I understand what you're saying here... both name & type are in the section attrs, so unless some Dovecot setting or plugin has an attribute called section we should be fine.
Hm, I initially read it as them being top-level attrs within a freeform "section". I guess I misread.
| 12:01:20 |
jappie | * and the reason I want attrsOf section instead of attrsOf configValueType is because Dovecot allows the following:
service auth {
unix_listener auth-master {
# ...
}
}
both service auth and unix_listener auth-master are named list filters and thus need to support section.{type,name}
| 12:08:21 |