diff options
Diffstat (limited to 'home-manager/modules/lib')
-rw-r--r-- | home-manager/modules/lib/default.nix | 4 | ||||
-rw-r--r-- | home-manager/modules/lib/file-type.nix | 30 | ||||
-rw-r--r-- | home-manager/modules/lib/gvariant.nix | 156 | ||||
-rw-r--r-- | home-manager/modules/lib/maintainers.nix | 44 | ||||
-rw-r--r-- | home-manager/modules/lib/types-dag.nix | 63 | ||||
-rw-r--r-- | home-manager/modules/lib/types.nix | 78 |
6 files changed, 331 insertions, 44 deletions
diff --git a/home-manager/modules/lib/default.nix b/home-manager/modules/lib/default.nix index 6b2bbacc249..7c2c72f709c 100644 --- a/home-manager/modules/lib/default.nix +++ b/home-manager/modules/lib/default.nix @@ -16,8 +16,10 @@ rec { entryBefore = d.dagEntryBefore; }; + gvariant = import ./gvariant.nix { inherit lib; }; + maintainers = import ./maintainers.nix; strings = import ./strings.nix { inherit lib; }; - types = import ./types.nix { inherit dag lib; }; + types = import ./types.nix { inherit dag gvariant lib; }; shell = import ./shell.nix { inherit lib; }; zsh = import ./zsh.nix { inherit lib; }; diff --git a/home-manager/modules/lib/file-type.nix b/home-manager/modules/lib/file-type.nix index 3096a6d37bb..56a3a1286a0 100644 --- a/home-manager/modules/lib/file-type.nix +++ b/home-manager/modules/lib/file-type.nix @@ -11,7 +11,7 @@ with lib; # Arguments: # - basePathDesc docbook compatible description of the base path # - basePath the file base path - fileType = basePathDesc: basePath: types.loaOf (types.submodule ( + fileType = basePathDesc: basePath: types.attrsOf (types.submodule ( { name, config, ... }: { options = { target = mkOption { @@ -21,6 +21,7 @@ with lib; absPath = if hasPrefix "/" p then p else "${basePath}/${p}"; in removePrefix (homeDirectory + "/") absPath; + defaultText = literalExample "<name>"; description = '' Path to target file relative to ${basePathDesc}. ''; @@ -29,17 +30,20 @@ with lib; text = mkOption { default = null; type = types.nullOr types.lines; - description = "Text of the file."; + description = '' + Text of the file. If this option is null then + <link linkend="opt-home.file._name_.source">home.file.<name?>.source</link> + must be set. + ''; }; source = mkOption { type = types.path; description = '' - Path of the source file. The file name must not start - with a period since Nix will not allow such names in - the Nix store. - </para><para> - This may refer to a directory. + Path of the source file or directory. If + <link linkend="opt-home.file._name_.text">home.file.<name?>.text</link> + is non-null then this option will automatically point to a file + containing that text. ''; }; @@ -80,6 +84,18 @@ with lib; into place. ''; }; + + force = mkOption { + type = types.bool; + default = false; + visible = false; + description = '' + Whether the target path should be unconditionally replaced + by the managed file source. Warning, this will silently + delete the target regardless of whether it is a file or + link. + ''; + }; }; config = { diff --git a/home-manager/modules/lib/gvariant.nix b/home-manager/modules/lib/gvariant.nix new file mode 100644 index 00000000000..92aa7d98371 --- /dev/null +++ b/home-manager/modules/lib/gvariant.nix @@ -0,0 +1,156 @@ +# A partial and basic implementation of GVariant formatted strings. +# +# Note, this API is not considered fully stable and it might therefore +# change in backwards incompatible ways without prior notice. + +{ lib }: + +with lib; + +let + + mkPrimitive = t: v: { + _type = "gvariant"; + type = t; + value = v; + __toString = self: "@${self.type} ${toString self.value}"; + }; + + type = { + arrayOf = t: "a${t}"; + maybeOf = t: "m${t}"; + tupleOf = ts: "(${concatStrings ts})"; + string = "s"; + boolean = "b"; + uchar = "y"; + int16 = "n"; + uint16 = "q"; + int32 = "i"; + uint32 = "u"; + int64 = "x"; + uint64 = "t"; + double = "d"; + }; + + # Returns the GVariant type of a given Nix value. If no type can be + # found for the value then the empty string is returned. + typeOf = v: + with type; + if builtins.isBool v then + boolean + else if builtins.isInt v then + int32 + else if builtins.isFloat v then + double + else if builtins.isString v then + string + else if builtins.isList v then + let elemType = elemTypeOf v; + in if elemType == "" then "" else arrayOf elemType + else if builtins.isAttrs v && v ? type then + v.type + else + ""; + + elemTypeOf = vs: + if builtins.isList vs then + if vs == [ ] then "" else typeOf (head vs) + else + ""; + + mkMaybe = elemType: elem: + mkPrimitive (type.maybeOf elemType) elem // { + __toString = self: + if self.value == null then + "@${self.type} nothing" + else + "just ${toString self.value}"; + }; + +in rec { + + inherit type typeOf; + + isArray = hasPrefix "a"; + isMaybe = hasPrefix "m"; + isTuple = hasPrefix "("; + + # Returns the GVariant value that most closely matches the given Nix + # value. If no GVariant value can be found then `null` is returned. + # + # No support for dictionaries, maybe types, or variants. + mkValue = v: + if builtins.isBool v then + mkBoolean v + else if builtins.isInt v then + mkInt32 v + else if builtins.isFloat v then + mkDouble v + else if builtins.isString v then + mkString v + else if builtins.isList v then + if v == [ ] then mkArray type.string [ ] else mkArray (elemTypeOf v) v + else if builtins.isAttrs v && (v._type or "") == "gvariant" then + v + else + null; + + mkArray = elemType: elems: + mkPrimitive (type.arrayOf elemType) (map mkValue elems) // { + __toString = self: + "@${self.type} [${concatMapStringsSep "," toString self.value}]"; + }; + + mkEmptyArray = elemType: mkArray elemType [ ]; + + mkNothing = elemType: mkMaybe elemType null; + + mkJust = elem: let gvarElem = mkValue elem; in mkMaybe gvarElem.type gvarElem; + + mkTuple = elems: + let + gvarElems = map mkValue elems; + tupleType = type.tupleOf (map (e: e.type) gvarElems); + in mkPrimitive tupleType gvarElems // { + __toString = self: + "@${self.type} (${concatMapStringsSep "," toString self.value})"; + }; + + mkBoolean = v: + mkPrimitive type.boolean v // { + __toString = self: if self.value then "true" else "false"; + }; + + mkString = v: + mkPrimitive type.string v // { + __toString = self: "'${escape [ "'" "\\" ] self.value}'"; + }; + + mkObjectpath = v: + mkPrimitive type.string v // { + __toString = self: "objectpath '${escape [ "'" ] self.value}'"; + }; + + mkUchar = mkPrimitive type.uchar; + + mkInt16 = mkPrimitive type.int16; + + mkUint16 = mkPrimitive type.uint16; + + mkInt32 = v: + mkPrimitive type.int32 v // { + __toString = self: toString self.value; + }; + + mkUint32 = mkPrimitive type.uint32; + + mkInt64 = mkPrimitive type.int64; + + mkUint64 = mkPrimitive type.uint64; + + mkDouble = v: + mkPrimitive type.double v // { + __toString = self: toString self.value; + }; + +} diff --git a/home-manager/modules/lib/maintainers.nix b/home-manager/modules/lib/maintainers.nix new file mode 100644 index 00000000000..8cb8781228b --- /dev/null +++ b/home-manager/modules/lib/maintainers.nix @@ -0,0 +1,44 @@ +# Home Manager maintainers. +# +# This attribute set contains Home Manager module maintainers that do +# not have an entry in the Nixpkgs maintainer list [1]. Entries here +# are expected to be follow the same format as described in [1]. +# +# [1] https://github.com/NixOS/nixpkgs/blob/fca0d6e093c82b31103dc0dacc48da2a9b06e24b/maintainers/maintainer-list.nix#LC1 + +{ + justinlovinger = { + name = "Justin Lovinger"; + email = "git@justinlovinger.com"; + github = "JustinLovinger"; + githubId = 7183441; + }; + owm111 = { + email = "7798336+owm111@users.noreply.github.com"; + name = "Owen McGrath"; + github = "owm111"; + githubId = 7798336; + }; + cwyc = { + email = "cwyc@users.noreply.github.com"; + name = "cwyc"; + github = "cwyc"; + githubId = 16950437; + }; + berbiche = { + name = "Nicolas Berbiche"; + email = "berbiche@users.noreply.github.com"; + github = "berbiche"; + githubId = 20448408; + keys = [{ + longkeyid = "rsa4096/0xB461292445C6E696"; + fingerprint = "D446 E58D 87A0 31C7 EC15 88D7 B461 2924 45C6 E696"; + }]; + }; + olmokramer = { + name = "Olmo Kramer"; + email = "olmokramer@users.noreply.github.com"; + github = "olmokramer"; + githubId = 3612514; + }; +} diff --git a/home-manager/modules/lib/types-dag.nix b/home-manager/modules/lib/types-dag.nix index 4dbdb907b0e..2efb12645d4 100644 --- a/home-manager/modules/lib/types-dag.nix +++ b/home-manager/modules/lib/types-dag.nix @@ -7,16 +7,25 @@ let isDagEntry = e: isAttrs e && (e ? data) && (e ? after) && (e ? before); dagContentType = elemType: - types.submodule { + types.submodule ({ name, ... }: { options = { data = mkOption { type = elemType; }; after = mkOption { type = with types; uniq (listOf str); }; before = mkOption { type = with types; uniq (listOf str); }; }; - }; + config = mkIf (elemType.name == "submodule") { + data._module.args.dagName = name; + }; + }); -in { +in rec { # A directed acyclic graph of some inner type. + # + # Note, if the element type is a submodule then the `name` argument + # will always be set to the string "data" since it picks up the + # internal structure of the DAG values. To give access to the + # "actual" attribute name a new submodule argument is provided with + # the name `dagName`. dagOf = elemType: let convertAllToDags = let @@ -51,34 +60,40 @@ in { let padWidth = stringLength (toString (length list)); in fixedWidthNumber padWidth i; - convertAllToDags = defs: + convertAll = loc: defs: let - convertAttrValue = n: v: - if isDagEntry v then v else dag.entryAnywhere v; - - convertListValue = namePrefix: vs: + convertListValue = namePrefix: def: let + vs = def.value; pad = paddedIndexStr vs; - makeEntry = i: v: - nameValuePair "${namePrefix}.${pad i}" (dag.entryAnywhere v); - in listToAttrs (imap1 makeEntry vs); + makeEntry = i: v: nameValuePair "${namePrefix}.${pad i}" v; + warning = '' + In file ${def.file} + a list is being assigned to the option '${ + concatStringsSep "." loc + }'. + This will soon be an error due to the list form being deprecated. + Please use the attribute set form instead with DAG functions to + express the desired order of entries. + ''; + in warn warning (listToAttrs (imap1 makeEntry vs)); - convertValue = i: value: - if isList value then - convertListValue "unnamed-${paddedIndexStr defs i}" value + convertValue = i: def: + if isList def.value then + convertListValue "unnamed-${paddedIndexStr defs i}" def else - mapAttrs convertAttrValue value; - in imap1 (i: def: def // { value = convertValue i def.value; }) defs; + def.value; + in imap1 (i: def: def // { value = convertValue i def; }) defs; - attrEquivalent = types.attrsOf (dagContentType elemType); + dagType = dagOf elemType; in mkOptionType rec { - name = "dagOf"; - description = "DAG of ${elemType.description}s"; - check = x: isAttrs x || isList x; - merge = loc: defs: attrEquivalent.merge loc (convertAllToDags defs); - getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "<name>" ]); - getSubModules = elemType.getSubModules; - substSubModules = m: dagOf (elemType.substSubModules m); + name = "listOrDagOf"; + description = "list or DAG of ${elemType.description}s"; + check = x: isList x || dagType.check x; + merge = loc: defs: dagType.merge loc (convertAll loc defs); + getSubOptions = dagType.getSubOptions; + getSubModules = dagType.getSubModules; + substSubModules = m: listOrDagOf (elemType.substSubModules m); functor = (defaultFunctor name) // { wrapped = elemType; }; }; } diff --git a/home-manager/modules/lib/types.nix b/home-manager/modules/lib/types.nix index 78a875f519e..64a6b4a34fa 100644 --- a/home-manager/modules/lib/types.nix +++ b/home-manager/modules/lib/types.nix @@ -1,4 +1,5 @@ -{ lib, dag ? import ./dag.nix { inherit lib; } }: +{ lib, dag ? import ./dag.nix { inherit lib; } +, gvariant ? import ./gvariant.nix { inherit lib; } }: with lib; @@ -6,31 +7,84 @@ let typesDag = import ./types-dag.nix { inherit dag lib; }; -in + # Needed since the type is called gvariant and its merge attribute + # must refer back to the type. + gvar = gvariant; -{ +in rec { inherit (typesDag) dagOf listOrDagOf; selectorFunction = mkOptionType { name = "selectorFunction"; - description = - "Function that takes an attribute set and returns a list" + description = "Function that takes an attribute set and returns a list" + " containing a selection of the values of the input set"; check = isFunction; - merge = _loc: defs: - as: concatMap (select: select as) (getValues defs); + merge = _loc: defs: as: concatMap (select: select as) (getValues defs); }; overlayFunction = mkOptionType { name = "overlayFunction"; - description = - "An overlay function, takes self and super and returns" + description = "An overlay function, takes self and super and returns" + " an attribute set overriding the desired attributes."; check = isFunction; - merge = _loc: defs: - self: super: - foldl' (res: def: mergeAttrs res (def.value self super)) {} defs; + merge = _loc: defs: self: super: + foldl' (res: def: mergeAttrs res (def.value self super)) { } defs; + }; + + fontType = types.submodule { + options = { + package = mkOption { + type = types.nullOr types.package; + default = null; + example = literalExample "pkgs.dejavu_fonts"; + description = '' + Package providing the font. This package will be installed + to your profile. If <literal>null</literal> then the font + is assumed to already be available in your profile. + ''; + }; + + name = mkOption { + type = types.str; + example = "DejaVu Sans 8"; + description = '' + The family name and size of the font within the package. + ''; + }; + }; + }; + + gvariant = mkOptionType rec { + name = "gvariant"; + description = "GVariant value"; + check = v: gvar.mkValue v != null; + merge = loc: defs: + let + vdefs = map (d: d // { value = gvar.mkValue d.value; }) defs; + vals = map (d: d.value) vdefs; + defTypes = map (x: x.type) vals; + sameOrNull = x: y: if x == y then y else null; + # A bit naive to just check the first entry… + sharedDefType = foldl' sameOrNull (head defTypes) defTypes; + allChecked = all (x: check x) vals; + in if sharedDefType == null then + throw ("Cannot merge definitions of `${showOption loc}' with" + + " mismatched GVariant types given in" + + " ${showFiles (getFiles defs)}.") + else if gvar.isArray sharedDefType && allChecked then + (types.listOf gvariant).merge loc + (map (d: d // { value = d.value.value; }) vdefs) + else if gvar.isTuple sharedDefType && allChecked then + mergeOneOption loc defs + else if gvar.isMaybe sharedDefType && allChecked then + mergeOneOption loc defs + else if gvar.type.string == sharedDefType && allChecked then + types.str.merge loc defs + else if gvar.type.double == sharedDefType && allChecked then + types.float.merge loc defs + else + mergeDefaultOption loc defs; }; } |