Skip to content

Commit

Permalink
various fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
MattSturgeon committed Jan 8, 2025
1 parent 4edfb76 commit 3a0417a
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 30 deletions.
5 changes: 2 additions & 3 deletions lib/fields.nix
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,8 @@
internal ? null,
# Whether the field shows up in the manual. Default: true. Use false to hide the field and any sub-options from submodules. Use "shallow" to hide only sub-options.
visible ? null,
# Whether the field is omitted from the final record when undefined. Default false.
optional ? null,
}@attrs:
assert lib.assertMsg (
(attrs.optional or false) -> (!attrs ? default)
) "mkField: `optional` is true, but a `default` is provided";
attrs // { _type = "field"; };
}
4 changes: 2 additions & 2 deletions lib/tests/modules.sh
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ checkConfigError 'define-record-mallory.nix.: "beginning of time"' config.people
checkConfigOutput '^true$' config.people.bob.isCool ./declare-record.nix ./define-record-alice.nix ./define-record-bob.nix

# record field bad default definition
checkConfigError 'In .the default value of option people.mallory.: "yeah"' config.people.mallory.isCool ./declare-record-bad-default.nix ./define-record-mallory.nix
checkConfigError 'In .the default value of field people.mallory.: "yeah"' config.people.mallory.isCool ./declare-record-bad-default.nix ./define-record-mallory.nix
checkConfigError 'A definition for option .people.mallory.isCool. is not of type .boolean.. Definition values:' config.people.mallory.isCool ./declare-record-bad-default.nix ./define-record-mallory.nix

# record field works in presence of wildcard
Expand All @@ -129,7 +129,7 @@ checkConfigOutput '^2016$' config.people.alice.nixerSince ./declare-record-wildc
checkConfigOutput '^true$' config.people.alice.mechKeyboard ./declare-record-wildcard.nix ./define-record-alice-prefs.nix

# record definition without corresponding field
checkConfigError 'A definition for option .people.mike. has an unknown field' config.people.mike.age ./declare-record.nix ./define-record-mike.nix
checkConfigError 'A definition for option .people.mike. has an unknown fields' config.people.mike.age ./declare-record.nix ./define-record-mike.nix
# record optional field without definition
checkConfigError "attribute 'age' in selection path 'config.people.alice.age' not found" config.people.alice.age ./declare-record-optional-field.nix ./define-record-alice.nix
# record optional field with definition
Expand Down
8 changes: 4 additions & 4 deletions lib/tests/modules/declare-record-bad-default.nix
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{ lib, ... }:

let
inherit (lib) mkOption types;
inherit (lib) mkField mkOption types;

person = types.record {
fields = {
nixerSince = mkOption { type = types.int; };
name = mkOption { type = types.str; };
isCool = mkOption {
nixerSince = mkField { type = types.int; };
name = mkField { type = types.str; };
isCool = mkField {
type = types.bool;
default = "yeah";
};
Expand Down
15 changes: 8 additions & 7 deletions lib/tests/modules/declare-record-optional-field.nix
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
{ lib, ... }:

let
inherit (lib) mkOption types;
inherit (lib) mkField mkOption types;

person = types.record {
fields = {
nixerSince = mkOption { type = types.int; };
name = mkOption { type = types.str; };
nixerSince = mkField { type = types.int; };
name = mkField { type = types.str; };
age = mkField {
type = types.ints.unsigned;
optional = true;
};
};
optionalFields = {
age = mkOption { type = types.ints.unsigned; };
};
wildcard = mkOption { type = types.bool; };
freeformType = types.bool;
};

in
Expand Down
8 changes: 4 additions & 4 deletions lib/tests/modules/declare-record-wildcard.nix
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{ lib, ... }:

let
inherit (lib) mkOption types;
inherit (lib) mkField mkOption types;

person = types.record {
fields = {
nixerSince = mkOption { type = types.int; };
name = mkOption { type = types.str; };
nixerSince = mkField { type = types.int; };
name = mkField { type = types.str; };
};
wildcard = mkOption { type = types.bool; };
freeformType = types.bool;
};

in
Expand Down
8 changes: 4 additions & 4 deletions lib/tests/modules/declare-record.nix
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{ lib, ... }:

let
inherit (lib) mkOption types;
inherit (lib) mkField mkOption types;

person = types.record {
fields = {
nixerSince = mkOption { type = types.int; };
name = mkOption { type = types.str; };
isCool = mkOption {
nixerSince = mkField { type = types.int; };
name = mkField { type = types.str; };
isCool = mkField {
type = types.bool;
default = true;
};
Expand Down
23 changes: 17 additions & 6 deletions lib/types/record.nix
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ let
isAttrs
;

inherit (lib.options)
showDefs
;

inherit (lib.strings)
escapeNixIdentifier
;

record =
{
fields ? { },
Expand All @@ -24,7 +32,9 @@ let
checkField =
name: field:
if field._type or null != "field" then
throw "Record field `${lib.escapeNixIdentifier name}` must be declared with `mkField`."
throw "Record field `${escapeNixIdentifier name}` must be declared with `mkField`."
else if (field.optional or false) && (field ? default) then
throw "Record field `${escapeNixIdentifier name}` is optional, but a `default` is provided."
else
field;

Expand Down Expand Up @@ -59,17 +69,18 @@ let
fieldValues = concatMapAttrs (
fieldName: field:
let
fieldOption = mergeDefinitions (loc ++ [ fieldName ]) fieldOption.type (
mergedOption = mergeDefinitions (loc ++ [ fieldName ]) field.type (
data.${fieldName} or [ ]
++ optional (field ? default) {
value = lib.mkOptionDefault fieldOption.default;
value = lib.mkOptionDefault field.default;
file = "the default value of field ${showOption loc}";
}
);
isRequired = !field.optional or false;
in
builtins.addErrorContext "while evaluating the field `${fieldName}' of option `${showOption loc}'" (
optionalAttrs (!field.optional || fieldOption.isDefined) {
${fieldName} = fieldOption.mergedValue;
optionalAttrs (isRequired || mergedOption.isDefined) {
${fieldName} = mergedOption.mergedValue;
}
)
) checkedFields;
Expand All @@ -86,7 +97,7 @@ let
else
throw ''
A definition for option `${showOption loc}' has an unknown fields:
${lib.concatMapAttrsStringSep "\n" (name: defs: "`${name}'${lib.showDefs defs}") extraData}'';
${lib.concatMapAttrsStringSep "\n" (name: defs: "`${name}'${showDefs defs}") extraData}'';
in
if freeformType == null then checkedExtraDefs else fieldValues // extraValues;
nestedTypes = lib.optionalAttrs (freeformType != null) {
Expand Down

0 comments on commit 3a0417a

Please sign in to comment.