diff --git a/lib/generators.nix b/lib/generators.nix index d424256..a73eb26 100644 --- a/lib/generators.nix +++ b/lib/generators.nix @@ -55,6 +55,28 @@ "'${value.value}'" else throw "lib.cosmic.generators.toRON: char type must be a single character." + else if value.__type == "enum" then + if value ? variant then + if value ? value then + if builtins.isList value.value then + let + count = builtins.length value.value; + in + if count == 0 then + "${value.variant}()" + else + "${value.variant}(\n${ + lib.concatImapStringsSep "\n" ( + index: element: + "${indent nextIndent}${toRON' nextIndent element}${lib.optionalString (index != count) ","}" + ) value.value + },\n${indent startIndent})" + else + throw "lib.cosmic.generators.toRON: enum type must have a list of values." + else + toString value.variant + else + throw "lib.cosmic.generators.toRON: enum type must have a variant." else if value.__type == "map" then let keys = builtins.attrNames value.value; diff --git a/lib/types.nix b/lib/types.nix index 3f718cc..2b9b74b 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -15,8 +15,8 @@ example = { autotile = true; autotile_behavior = { - __type = "raw"; - value = "PerWorkspace"; + __type = "enum"; + variant = "PerWorkspace"; }; }; description = '' @@ -42,6 +42,10 @@ str ]); + hexColor = lib.types.strMatching "^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$" // { + description = "hex color"; + }; + rawRon = lib.mkOptionType { check = value: @@ -68,15 +72,36 @@ name = "rawRon"; }; - rawRonEnum = + ronChar = lib.mkOptionType { + check = + value: + let + keys = builtins.attrNames value; + in + builtins.isAttrs value + && + [ + "__type" + "value" + ] == keys + && value.__type == "char" + && builtins.isString value.value + && builtins.stringLength value.value == 1; + description = "RON char"; + descriptionClass = "noun"; + merge = lib.options.mergeEqualOption; + name = "ronChar"; + }; + + ronEnum = let - rawRonEnum' = - values: + ronEnum' = + variants: let - name = "rawRonEnum"; + name = "ronEnum"; show = v: ''"${v}"''; in - if !builtins.all (value: builtins.isString value) values then + if !builtins.all (value: builtins.isString value) variants then throw "All values in the enum must be strings." else lib.mkOptionType { @@ -89,49 +114,28 @@ && [ "__type" - "value" + "variant" ] == keys - && value.__type == "raw" - && builtins.elem value.value values; + && value.__type == "enum" + && builtins.elem value.variant variants; description = - if values == [ ] then - "impossible (empty raw RON enum)" - else if builtins.length values == 1 then - "raw RON value ${show (builtins.head values)} (singular enum)" + if variants == [ ] then + "impossible (empty RON enum)" + else if builtins.length variants == 1 then + "RON enum variant ${show (builtins.head variants)} (singular RON enum)" else - "one of the following raw RON values: ${lib.concatMapStringsSep ", " show values}"; - descriptionClass = if builtins.length values < 2 then "noun" else "conjunction"; + "one of the following RON enum variants: ${lib.concatMapStringsSep ", " show variants}"; + descriptionClass = if builtins.length variants < 2 then "noun" else "conjunction"; functor = lib.defaultFunctor name // { - payload = { inherit values; }; - type = payload: rawRonEnum' payload.values; - binOp = a: b: { values = lib.unique (a.values + b.values); }; + payload = { inherit variants; }; + type = payload: ronEnum' payload.variants; + binOp = a: b: { variants = lib.unique (a.variants + b.variants); }; }; merge = lib.options.mergeEqualOption; inherit name; }; in - rawRonEnum'; - - ronChar = lib.mkOptionType { - check = - value: - let - keys = builtins.attrNames value; - in - builtins.isAttrs value - && - [ - "__type" - "value" - ] == keys - && value.__type == "char" - && builtins.isString value.value - && builtins.stringLength value.value == 1; - description = "RON char"; - descriptionClass = "noun"; - merge = lib.options.mergeEqualOption; - name = "ronChar"; - }; + ronEnum'; ronMap = lib.mkOptionType { check = @@ -157,17 +161,17 @@ }; merge = loc: defs: { __type = "map"; - value = builtins.foldl' ( - first: def: lib.recursiveUpdate first.value.value def.value.value - ) (builtins.head defs) (builtins.tail defs); + value = builtins.foldl' (first: def: lib.recursiveUpdate first def.value.value) { } defs; }; name = "ronMap"; }; ronMapOf = let - name = "ronMapOf"; ronMapOf' = + let + name = "ronMapOf"; + in elemType: lib.mkOptionType { check = @@ -243,29 +247,35 @@ description = "RON named struct"; descriptionClass = "noun"; merge = loc: defs: { - __name = builtins.foldl' ( - first: def: - if def.value.__name != first.value.__name then - throw "The option '${lib.showOption loc}' has conflicting definition values: ${ - lib.options.showDefs [ - first - def - ] - }\nUse `lib.mkForce value` or `lib.mkDefault value` to change the priority on any of these definitions." + __name = + if builtins.length defs == 0 then + abort "This case should not happen." + else if builtins.length defs == 1 then + (builtins.head defs).value.__name else - first.value.__name - ) (builtins.head defs) (builtins.tail defs); - value = builtins.foldl' ( - first: def: lib.recursiveUpdate first.value.value def.value.value - ) (builtins.head defs) (builtins.tail defs); + builtins.foldl' ( + first: def: + if def.value.__name != first.value.__name then + throw "The option '${lib.showOption loc}' has conflicting definition values: ${ + lib.options.showDefs [ + first + def + ] + }\nUse `lib.mkForce value` or `lib.mkDefault value` to change the priority on any of these definitions." + else + first.value.__name + ) (builtins.head defs) (builtins.tail defs); + value = builtins.foldl' (first: def: lib.recursiveUpdate first def.value.value) { } defs; }; name = "ronNamedStruct"; }; ronNamedStructOf = let - name = "ronNamedStructOf"; ronNamedStructOf' = + let + name = "ronNamedStructOf"; + in elemType: lib.mkOptionType { check = @@ -302,18 +312,24 @@ ); in { - __name = builtins.foldl' ( - first: def: - if def.value.__name != first.value.__name then - throw "The option '${lib.showOption loc}' has conflicting definition values: ${ - lib.options.showDefs [ - first - def - ] - }\nUse `lib.mkForce value` or `lib.mkDefault value` to change the priority on any of these definitions." + __name = + if builtins.length defs == 0 then + abort "This case should not happen." + else if builtins.length defs == 1 then + (builtins.head defs).value.__name else - first.value.__name - ) (builtins.head defs) (builtins.tail defs); + builtins.foldl' ( + first: def: + if def.value.__name != first.value.__name then + throw "The option '${lib.showOption loc}' has conflicting definition values: ${ + lib.options.showDefs [ + first + def + ] + }\nUse `lib.mkForce value` or `lib.mkDefault value` to change the priority on any of these definitions." + else + first.value.__name + ) (builtins.head defs) (builtins.tail defs); value = builtins.mapAttrs (n: v: v.value) ( lib.filterAttrs (n: v: v ? value) ( lib.zipAttrsWith ( @@ -352,8 +368,10 @@ ronOptionalOf = let - name = "ronOptionalOf"; ronOptionalOf' = + let + name = "ronOptionalOf"; + in elemType: lib.mkOptionType { check = @@ -425,10 +443,157 @@ name = "ronTuple"; }; + ronTupleEnum = + let + ronTupleEnum' = + let + name = "ronTupleEnum"; + show = v: ''"${v}"''; + in + variants: + if !builtins.all (value: builtins.isString value) variants then + throw "All variants in the enum must be strings." + else + lib.mkOptionType { + check = + value: + let + keys = builtins.attrNames value; + in + builtins.isAttrs value + && + [ + "__type" + "value" + "variant" + ] == keys + && value.__type == "enum" + && builtins.elem value.variant variants + && builtins.isList value.value; + description = + if variants == [ ] then + "impossible (empty RON tuple enum)" + else if builtins.length variants == 1 then + "RON enum variant ${show (builtins.head variants)} with a value (singular RON tuple enum)" + else + "one of the following RON tuple enum variants: ${ + lib.concatMapStringsSep ", " show variants + } with a value"; + descriptionClass = if builtins.length variants < 2 then "noun" else "conjunction"; + functor = lib.defaultFunctor name // { + payload = { inherit variants; }; + type = payload: ronTupleEnum' payload.variants; + binOp = a: b: { variants = lib.unique (a.variants + b.variants); }; + }; + merge = lib.options.mergeEqualOption; + inherit name; + }; + in + ronTupleEnum'; + + ronTupleEnumOf = + let + ronTupleEnumOf' = + let + name = "ronTupleEnumOf"; + show = v: ''"${v}"''; + in + elemType: variants: + if !builtins.all (value: builtins.isString value) variants then + throw "All variants in the enum must be strings." + else + lib.mkOptionType { + check = + value: + let + keys = builtins.attrNames value; + in + builtins.isAttrs value + && + [ + "__type" + "value" + "variant" + ] == keys + && value.__type == "enum" + && builtins.elem value.variant variants + && builtins.isList value.value; + description = + if variants == [ ] then + "impossible (empty RON tuple enum)" + else if builtins.length variants == 1 then + "RON enum variant ${show (builtins.head variants)} with a ${ + lib.types.optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType + } value (singular RON tuple enum)" + else + "one of the following RON tuple enum variants: ${ + lib.concatMapStringsSep ", " show variants + } with a ${ + lib.types.optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType + } value"; + descriptionClass = if builtins.length variants < 2 then "noun" else "conjunction"; + functor = lib.defaultFunctor name // { + payload = { inherit elemType variants; }; + type = payload: ronTupleEnumOf' payload.elemType payload.variants; + binOp = a: b: { + variants = lib.unique (a.variants + b.variants); + elemType = a.elemType.typeMerge b.elemType.functor; + }; + }; + getSubModules = elemType.getSubModules; + getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "*" ]); + merge = loc: defs: { + __type = "enum"; + value = map (x: x.value) ( + builtins.filter (x: x ? value) ( + builtins.concatLists ( + lib.imap1 ( + n: def: + lib.imap1 ( + m: def': + (lib.mergeDefinitions (loc ++ [ "[definition ${toString n}-entry ${toString m}]" ]) elemType [ + { + inherit (def) file; + value = def'; + } + ]).optionalValue + ) def.value.value + ) defs + ) + ) + ); + variant = + if builtins.length defs == 0 then + abort "This case should not happen." + else if builtins.length defs == 1 then + (builtins.head defs).value.variant + else + builtins.foldl' ( + first: def: + if def.value.variant != first.value.variant then + throw "The option '${lib.showOption (loc ++ [ "variant" ])}' has conflicting definition values: ${ + lib.options.showDefs [ + first + def + ] + }\nUse `lib.mkForce value` or `lib.mkDefault value` to change the priority on any of these definitions." + else + first.value.variant + ) (builtins.head defs) (builtins.tail defs); + }; + inherit name; + nestedTypes.elemType = elemType; + substSubModules = m: ronTupleEnumOf' (elemType.substSubModules m) variants; + }; + in + ronTupleEnumOf'; + ronTupleOf = let - name = "ronTupleOf"; ronTupleOf' = + let + name = "ronTupleOf"; + in elemType: lib.mkOptionType { check = @@ -486,6 +651,4 @@ }; in ronTupleOf'; - - hexColor = lib.types.strMatching "^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"; } diff --git a/modules/apps/by-name/cosmic-term/default.nix b/modules/apps/by-name/cosmic-term/default.nix index 4aaf24a..ed14cbd 100644 --- a/modules/apps/by-name/cosmic-term/default.nix +++ b/modules/apps/by-name/cosmic-term/default.nix @@ -13,14 +13,14 @@ lib.cosmic.applications.mkCosmicApplication { settingsOptions = { app_theme = mkNullOrOption { - type = lib.types.rawRonEnum [ + type = lib.types.ronEnum [ "Dark" "Light" "System" ]; example = { - __type = "raw"; - value = "Dark"; + __type = "enum"; + variant = "Dark"; }; description = '' Controls the theme of the terminal. @@ -143,8 +143,8 @@ lib.cosmic.applications.mkCosmicApplication { settingsExample = { app_theme = { - __type = "raw"; - value = "Dark"; + __type = "enum"; + variant = "Dark"; }; bold_font_weight = 700; dim_font_weight = 300; diff --git a/modules/files.nix b/modules/files.nix index 047d0a1..a2e3ac6 100644 --- a/modules/files.nix +++ b/modules/files.nix @@ -15,8 +15,8 @@ entries = { autotile = true; autotile_behavior = { - __type = "raw"; - value = "PerWorkspace"; + __type = "enum"; + variant = "PerWorkspace"; }; xkb_config = { rules = ""; @@ -110,8 +110,9 @@ value = [ "Virtual-1" { - __type = "raw"; - value = ''Path("/usr/share/backgrounds/cosmic/webb-inspired-wallpaper-system76.jpg")''; + __type = "enum"; + variant = "Path"; + value = [ "/usr/share/backgrounds/cosmic/webb-inspired-wallpaper-system76.jpg" ]; } ]; }