Something went wrong. Try again.
What happens if I push all of nixpkgs
Something went wrong. Try again.
Nix
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514# Definitions related to run-time type checking. Used in particular# to type-check NixOS configurations.{ lib }:
let inherit (lib) elem flip isAttrs isBool isDerivation isFloat isFunction isInt isList isString isStorePath throwIf toDerivation toList ; inherit (lib.lists) all concatLists count elemAt filter foldl' head imap1 last length tail ; inherit (lib.attrsets) attrNames filterAttrs hasAttr mapAttrs optionalAttrs zipAttrsWith ; inherit (lib.options) getFiles getValues mergeDefaultOption mergeEqualOption mergeOneOption mergeUniqueOption showFiles showOption ; inherit (lib.strings) concatMapStringsSep concatStringsSep escapeNixString hasInfix isStringLike ; inherit (lib.trivial) boolToString ;
inherit (lib.modules) mergeDefinitions fixupOptionType mergeOptionDecls ;
inAttrPosSuffix = v: name: let pos = builtins.unsafeGetAttrPos name v; in if pos == null then "" else " at ${pos.file}:${toString pos.line}:${toString pos.column}";
# Internal functor to help for migrating functor.wrapped to functor.payload.elemType # Note that individual attributes can be overridden if needed. elemTypeFunctor = name: { elemType, ... }@payload: { inherit name payload; wrappedDeprecationMessage = makeWrappedDeprecationMessage payload; type = outer_types.types.${name}; binOp = a: b: let merged = a.elemType.typeMerge b.elemType.functor; in if merged == null then null else { elemType = merged; }; }; makeWrappedDeprecationMessage = payload: { loc }: lib.warn '' The deprecated `${lib.optionalString (loc != null) "type."}functor.wrapped` attribute ${ lib.optionalString (loc != null) "of the option `${showOption loc}` " }is accessed, use `${lib.optionalString (loc != null) "type."}nestedTypes.elemType` instead. '' payload.elemType;
outer_types = rec { isType = type: x: (x._type or "") == type;
setType = typeName: value: value // { _type = typeName; };
# Default type merging function # takes two type functors and return the merged type defaultTypeMerge = f: f': let mergedWrapped = f.wrapped.typeMerge f'.wrapped.functor; mergedPayload = f.binOp f.payload f'.payload;
hasPayload = assert (f'.payload != null) == (f.payload != null); f.payload != null; hasWrapped = assert (f'.wrapped != null) == (f.wrapped != null); f.wrapped != null;
typeFromPayload = if mergedPayload == null then null else f.type mergedPayload; typeFromWrapped = if mergedWrapped == null then null else f.type mergedWrapped; in # Abort early: cannot merge different types if f.name != f'.name then null else
if hasPayload then # Just return the payload if returning wrapped is deprecated if f ? wrappedDeprecationMessage then typeFromPayload else if hasWrapped then # Has both wrapped and payload throw '' Type ${f.name} defines both `functor.payload` and `functor.wrapped` at the same time, which is not supported.
Use either `functor.payload` or `functor.wrapped` but not both.
If your code worked before remove either `functor.wrapped` or `functor.payload` from the type definition. '' else typeFromPayload else if hasWrapped then typeFromWrapped else f.type;
# Default type functor defaultFunctor = name: { inherit name; type = types.${name} or null; wrapped = null; payload = null; binOp = a: b: null; };
isOptionType = isType "option-type"; mkOptionType = { # Human-readable representation of the type, should be equivalent to # the type function name. name, # Description of the type, defined recursively by embedding the wrapped type if any. description ? null, # A hint for whether or not this description needs parentheses. Possible values: # - "noun": a noun phrase # Example description: "positive integer", # - "conjunction": a phrase with a potentially ambiguous "or" connective # Example description: "int or string" # - "composite": a phrase with an "of" connective # Example description: "list of string" # - "nonRestrictiveClause": a noun followed by a comma and a clause # Example description: "positive integer, meaning >0" # See the `optionDescriptionPhrase` function. descriptionClass ? null, # DO NOT USE WITHOUT KNOWING WHAT YOU ARE DOING! # Function applied to each definition that must return false when a definition # does not match the type. It should not check more than the root of the value, # because checking nested values reduces laziness, leading to unnecessary # infinite recursions in the module system. # Further checks of nested values should be performed by throwing in # the merge function. # Strict and deep type checking can be performed by calling lib.deepSeq on # the merged value. # # See https://github.com/NixOS/nixpkgs/pull/6794 that introduced this change, # https://github.com/NixOS/nixpkgs/pull/173568 and # https://github.com/NixOS/nixpkgs/pull/168295 that attempted to revert this, # https://github.com/NixOS/nixpkgs/issues/191124 and # https://github.com/NixOS/nixos-search/issues/391 for what happens if you ignore # this disclaimer. check ? (x: true), # Merge a list of definitions together into a single value. # This function is called with two arguments: the location of # the option in the configuration as a list of strings # (e.g. ["boot" "loader "grub" "enable"]), and a list of # definition values and locations (e.g. [ { file = "/foo.nix"; # value = 1; } { file = "/bar.nix"; value = 2 } ]). merge ? mergeDefaultOption, # Whether this type has a value representing nothingness. If it does, # this should be a value of the form { value = <the nothing value>; } # If it doesn't, this should be {} # This may be used when a value is required for `mkIf false`. This allows the extra laziness in e.g. `lazyAttrsOf`. emptyValue ? { }, # Return a flat attrset of sub-options. Used to generate # documentation. getSubOptions ? prefix: { }, # List of modules if any, or null if none. getSubModules ? null, # Function for building the same option type with a different list of # modules. substSubModules ? m: null, # Function that merge type declarations. # internal, takes a functor as argument and returns the merged type. # returning null means the type is not mergeable typeMerge ? defaultTypeMerge functor, # The type functor. # internal, representation of the type as an attribute set. # name: name of the type # type: type function. # wrapped: the type wrapped in case of compound types. # payload: values of the type, two payloads of the same type must be # combinable with the binOp binary operation. # binOp: binary operation that merge two payloads of the same type. functor ? defaultFunctor name, # The deprecation message to display when this type is used by an option # If null, the type isn't deprecated deprecationMessage ? null, # The types that occur in the definition of this type. This is used to # issue deprecation warnings recursively. Can also be used to reuse # nested types nestedTypes ? { }, }: { _type = "option-type"; inherit name check merge emptyValue getSubOptions getSubModules substSubModules typeMerge deprecationMessage nestedTypes descriptionClass ; functor = if functor ? wrappedDeprecationMessage then functor // { wrapped = functor.wrappedDeprecationMessage { loc = null; }; } else functor; description = if description == null then name else description; };
# optionDescriptionPhrase :: (str -> bool) -> optionType -> str # # Helper function for producing unambiguous but readable natural language # descriptions of types. # # Parameters # # optionDescriptionPhase unparenthesize optionType # # `unparenthesize`: A function from descriptionClass string to boolean. # It must return true when the class of phrase will fit unambiguously into # the description of the caller. # # `optionType`: The option type to parenthesize or not. # The option whose description we're returning. # # Return value # # The description of the `optionType`, with parentheses if there may be an # ambiguity. optionDescriptionPhrase = unparenthesize: t: if unparenthesize (t.descriptionClass or null) then t.description else "(${t.description})";
noCheckForDocsModule = { # When generating documentation, our goal isn't to check anything. # Quite the opposite in fact. Generating docs is somewhat of a # challenge, evaluating modules in a *lacking* context. Anything # that makes the docs avoid an error is a win. config._module.check = lib.mkForce false; _file = "<built-in module that disables checks for the purpose of documentation generation>"; };
# When adding new types don't forget to document them in # nixos/doc/manual/development/option-types.section.md! types = rec {
raw = mkOptionType { name = "raw"; description = "raw value"; descriptionClass = "noun"; check = value: true; merge = mergeOneOption; };
anything = mkOptionType { name = "anything"; description = "anything"; descriptionClass = "noun"; check = value: true; merge = loc: defs: let getType = value: if isAttrs value && isStringLike value then "stringCoercibleSet" else builtins.typeOf value;
# Returns the common type of all definitions, throws an error if they # don't have the same type commonType = foldl' ( type: def: if getType def.value == type then type else throw "The option `${showOption loc}' has conflicting option types in ${showFiles (getFiles defs)}" ) (getType (head defs).value) defs;
mergeFunction = { # Recursively merge attribute sets set = (attrsOf anything).merge; # This is the type of packages, only accept a single definition stringCoercibleSet = mergeOneOption; lambda = loc: defs: arg: anything.merge (loc ++ [ "<function body>" ]) ( map (def: { file = def.file; value = def.value arg; }) defs ); # Otherwise fall back to only allowing all equal definitions } .${commonType} or mergeEqualOption; in mergeFunction loc defs; };
unspecified = mkOptionType { name = "unspecified"; description = "unspecified value"; descriptionClass = "noun"; };
bool = mkOptionType { name = "bool"; description = "boolean"; descriptionClass = "noun"; check = isBool; merge = mergeEqualOption; };
boolByOr = mkOptionType { name = "boolByOr"; description = "boolean (merged using or)"; descriptionClass = "noun"; check = isBool; merge = loc: defs: foldl' ( result: def: # Under the assumption that .check always runs before merge, we can assume that all defs.*.value # have been forced, and therefore we assume we don't introduce order-dependent strictness here result || def.value ) false defs; };
int = mkOptionType { name = "int"; description = "signed integer"; descriptionClass = "noun"; check = isInt; merge = mergeEqualOption; };
# Specialized subdomains of int ints = let betweenDesc = lowest: highest: "${toString lowest} and ${toString highest} (both inclusive)"; between = lowest: highest: assert lib.assertMsg (lowest <= highest) "ints.between: lowest must be smaller than highest"; addCheck int (x: x >= lowest && x <= highest) // { name = "intBetween"; description = "integer between ${betweenDesc lowest highest}"; }; ign = lowest: highest: name: docStart: between lowest highest // { inherit name; description = docStart + "; between ${betweenDesc lowest highest}"; }; unsign = bit: range: ign 0 (range - 1) "unsignedInt${toString bit}" "${toString bit} bit unsigned integer"; sign = bit: range: ign (0 - (range / 2)) ( range / 2 - 1 ) "signedInt${toString bit}" "${toString bit} bit signed integer";
in { # TODO: Deduplicate with docs in nixos/doc/manual/development/option-types.section.md /** An int with a fixed range.
# Example :::{.example} ## `lib.types.ints.between` usage example
```nix (ints.between 0 100).check (-1) => false (ints.between 0 100).check (101) => false (ints.between 0 0).check 0 => true ```
::: */ inherit between;
unsigned = addCheck types.int (x: x >= 0) // { name = "unsignedInt"; description = "unsigned integer, meaning >=0"; descriptionClass = "nonRestrictiveClause"; }; positive = addCheck types.int (x: x > 0) // { name = "positiveInt"; description = "positive integer, meaning >0"; descriptionClass = "nonRestrictiveClause"; }; u8 = unsign 8 256; u16 = unsign 16 65536; # the biggest int Nix accepts is 2^63 - 1 (9223372036854775808) # the smallest int Nix accepts is -2^63 (-9223372036854775807) u32 = unsign 32 4294967296; # u64 = unsign 64 18446744073709551616;
s8 = sign 8 256; s16 = sign 16 65536; s32 = sign 32 4294967296; };
# Alias of u16 for a port number port = ints.u16;
float = mkOptionType { name = "float"; description = "floating point number"; descriptionClass = "noun"; check = isFloat; merge = mergeEqualOption; };
number = either int float;
numbers = let betweenDesc = lowest: highest: "${builtins.toJSON lowest} and ${builtins.toJSON highest} (both inclusive)"; in { between = lowest: highest: assert lib.assertMsg (lowest <= highest) "numbers.between: lowest must be smaller than highest"; addCheck number (x: x >= lowest && x <= highest) // { name = "numberBetween"; description = "integer or floating point number between ${betweenDesc lowest highest}"; };
nonnegative = addCheck number (x: x >= 0) // { name = "numberNonnegative"; description = "nonnegative integer or floating point number, meaning >=0"; descriptionClass = "nonRestrictiveClause"; }; positive = addCheck number (x: x > 0) // { name = "numberPositive"; description = "positive integer or floating point number, meaning >0"; descriptionClass = "nonRestrictiveClause"; }; };
str = mkOptionType { name = "str"; description = "string"; descriptionClass = "noun"; check = isString; merge = mergeEqualOption; };
nonEmptyStr = mkOptionType { name = "nonEmptyStr"; description = "non-empty string"; descriptionClass = "noun"; check = x: str.check x && builtins.match "[ \t\n]*" x == null; inherit (str) merge; };
# Allow a newline character at the end and trim it in the merge function. singleLineStr = let inherit (strMatching "[^\n\r]*\n?") check merge; in mkOptionType { name = "singleLineStr"; description = "(optionally newline-terminated) single-line string"; descriptionClass = "noun"; inherit check; merge = loc: defs: lib.removeSuffix "\n" (merge loc defs); };
strMatching = pattern: mkOptionType { name = "strMatching ${escapeNixString pattern}"; description = "string matching the pattern ${pattern}"; descriptionClass = "noun"; check = x: str.check x && builtins.match pattern x != null; inherit (str) merge; functor = defaultFunctor "strMatching" // { type = payload: strMatching payload.pattern; payload = { inherit pattern; }; binOp = lhs: rhs: if lhs == rhs then lhs else null; }; };
# Merge multiple definitions by concatenating them (with the given # separator between the values). separatedString = sep: mkOptionType rec { name = "separatedString"; description = if sep == "" then "Concatenated string" # for types.string. else "strings concatenated with ${builtins.toJSON sep}"; descriptionClass = "noun"; check = isString; merge = loc: defs: concatStringsSep sep (getValues defs); functor = (defaultFunctor name) // { payload = { inherit sep; }; type = payload: types.separatedString payload.sep; binOp = lhs: rhs: if lhs.sep == rhs.sep then { inherit (lhs) sep; } else null; }; };
lines = separatedString "\n"; commas = separatedString ","; envVar = separatedString ":";
# Deprecated; should not be used because it quietly concatenates # strings, which is usually not what you want. # We use a lib.warn because `deprecationMessage` doesn't trigger in nested types such as `attrsOf string` string = lib.warn "The type `types.string` is deprecated. See https://github.com/NixOS/nixpkgs/pull/66346 for better alternative types." ( separatedString "" // { name = "string"; } );
passwdEntry = entryType: addCheck entryType (str: !(hasInfix ":" str || hasInfix "\n" str)) // { name = "passwdEntry ${entryType.name}"; description = "${ optionDescriptionPhrase (class: class == "noun") entryType }, not containing newlines or colons"; descriptionClass = "nonRestrictiveClause"; };
attrs = mkOptionType { name = "attrs"; description = "attribute set"; check = isAttrs; merge = loc: foldl' (res: def: res // def.value) { }; emptyValue = { value = { }; }; };
# A package is a top-level store path (/nix/store/hash-name). This includes: # - derivations # - more generally, attribute sets with an `outPath` or `__toString` attribute # pointing to a store path, e.g. flake inputs # - strings with context, e.g. "${pkgs.foo}" or (toString pkgs.foo) # - hardcoded store path literals (/nix/store/hash-foo) or strings without context # ("/nix/store/hash-foo"). These get a context added to them using builtins.storePath. # If you don't need a *top-level* store path, consider using pathInStore instead. package = mkOptionType { name = "package"; descriptionClass = "noun"; check = x: isDerivation x || isStorePath x; merge = loc: defs: let res = mergeOneOption loc defs; in if builtins.isPath res || (builtins.isString res && !builtins.hasContext res) then toDerivation res else res; };
shellPackage = package // { check = x: isDerivation x && hasAttr "shellPath" x; };
pkgs = addCheck ( unique { message = "A Nixpkgs pkgs set can not be merged with another pkgs set."; } attrs // { name = "pkgs"; descriptionClass = "noun"; description = "Nixpkgs package set"; } ) (x: (x._type or null) == "pkgs");
path = pathWith { absolute = true; };
pathInStore = pathWith { inStore = true; };
pathWith = { inStore ? null, absolute ? null, }: throwIf (inStore != null && absolute != null && inStore && !absolute) "In pathWith, inStore means the path must be absolute" mkOptionType { name = "path"; description = ( (if absolute == null then "" else (if absolute then "absolute " else "relative ")) + "path" + ( if inStore == null then "" else (if inStore then " in the Nix store" else " not in the Nix store") ) ); descriptionClass = "noun";
merge = mergeEqualOption; functor = defaultFunctor "path" // { type = pathWith; payload = { inherit inStore absolute; }; binOp = lhs: rhs: if lhs == rhs then lhs else null; };
check = x: let isInStore = lib.path.hasStorePathPrefix ( if builtins.isPath x then x # Discarding string context is necessary to convert the value to # a path and safe as the result is never used in any derivation. else /. + builtins.unsafeDiscardStringContext x ); isAbsolute = builtins.substring 0 1 (toString x) == "/"; isExpectedType = ( if inStore == null || inStore then isStringLike x else isString x # Do not allow a true path, which could be copied to the store later on. ); in isExpectedType && (inStore == null || inStore == isInStore) && (absolute == null || absolute == isAbsolute); };
listOf = elemType: mkOptionType rec { name = "listOf"; description = "list of ${ optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType }"; descriptionClass = "composite"; check = isList; merge = loc: defs: map (x: x.value) ( filter (x: x ? value) ( concatLists ( imap1 ( n: def: imap1 ( m: def': (mergeDefinitions (loc ++ [ "[definition ${toString n}-entry ${toString m}]" ]) elemType [ { inherit (def) file; value = def'; } ]).optionalValue ) def.value ) defs ) ) ); emptyValue = { value = [ ]; }; getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "*" ]); getSubModules = elemType.getSubModules; substSubModules = m: listOf (elemType.substSubModules m); functor = (elemTypeFunctor name { inherit elemType; }) // { type = payload: types.listOf payload.elemType; }; nestedTypes.elemType = elemType; };
nonEmptyListOf = elemType: let list = addCheck (types.listOf elemType) (l: l != [ ]); in list // { description = "non-empty ${optionDescriptionPhrase (class: class == "noun") list}"; emptyValue = { }; # no .value attr, meaning unset substSubModules = m: nonEmptyListOf (elemType.substSubModules m); };
attrsOf = elemType: attrsWith { inherit elemType; };
# A version of attrsOf that's lazy in its values at the expense of # conditional definitions not working properly. E.g. defining a value with # `foo.attr = mkIf false 10`, then `foo ? attr == true`, whereas with # attrsOf it would correctly be `false`. Accessing `foo.attr` would throw an # error that it's not defined. Use only if conditional definitions don't make sense. lazyAttrsOf = elemType: attrsWith { inherit elemType; lazy = true; };
# base type for lazyAttrsOf and attrsOf attrsWith = let # Push down position info. pushPositions = map ( def: mapAttrs (n: v: { inherit (def) file; value = v; }) def.value ); binOp = lhs: rhs: let elemType = lhs.elemType.typeMerge rhs.elemType.functor; lazy = if lhs.lazy == rhs.lazy then lhs.lazy else null; placeholder = if lhs.placeholder == rhs.placeholder then lhs.placeholder else if lhs.placeholder == "name" then rhs.placeholder else if rhs.placeholder == "name" then lhs.placeholder else null; in if elemType == null || lazy == null || placeholder == null then null else { inherit elemType lazy placeholder; }; in { elemType, lazy ? false, placeholder ? "name", }: mkOptionType { name = if lazy then "lazyAttrsOf" else "attrsOf"; description = (if lazy then "lazy attribute set" else "attribute set") + " of ${optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType}"; descriptionClass = "composite"; check = isAttrs; merge = if lazy then ( # Lazy merge Function loc: defs: zipAttrsWith ( name: defs: let merged = mergeDefinitions (loc ++ [ name ]) elemType defs; # mergedValue will trigger an appropriate error when accessed in merged.optionalValue.value or elemType.emptyValue.value or merged.mergedValue ) # Push down position info. (pushPositions defs) ) else ( # Non-lazy merge Function loc: defs: mapAttrs (n: v: v.value) ( filterAttrs (n: v: v ? value) ( zipAttrsWith (name: defs: (mergeDefinitions (loc ++ [ name ]) elemType (defs)).optionalValue) # Push down position info. (pushPositions defs) ) ) ); emptyValue = { value = { }; }; getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "<${placeholder}>" ]); getSubModules = elemType.getSubModules; substSubModules = m: attrsWith { elemType = elemType.substSubModules m; inherit lazy placeholder; }; functor = (elemTypeFunctor "attrsWith" { inherit elemType lazy placeholder; }) // { # Custom type merging required because of the "placeholder" attribute inherit binOp; }; nestedTypes.elemType = elemType; };
# TODO: deprecate this in the future: loaOf = elemType: types.attrsOf elemType // { name = "loaOf"; deprecationMessage = "Mixing lists with attribute values is no longer" + " possible; please use `types.attrsOf` instead. See" + " https://github.com/NixOS/nixpkgs/issues/1800 for the motivation."; nestedTypes.elemType = elemType; };
attrTag = tags: let tags_ = tags; in let tags = mapAttrs ( n: opt: builtins.addErrorContext "while checking that attrTag tag ${lib.strings.escapeNixIdentifier n} is an option with a type${inAttrPosSuffix tags_ n}" ( throwIf (opt._type or null != "option") "In attrTag, each tag value must be an option, but tag ${lib.strings.escapeNixIdentifier n} ${ if opt ? _type then if opt._type == "option-type" then "was a bare type, not wrapped in mkOption." else "was of type ${lib.strings.escapeNixString opt._type}." else "was not." }" opt // { declarations = opt.declarations or ( let pos = builtins.unsafeGetAttrPos n tags_; in if pos == null then [ ] else [ pos.file ] ); declarationPositions = opt.declarationPositions or ( let pos = builtins.unsafeGetAttrPos n tags_; in if pos == null then [ ] else [ pos ] ); } ) ) tags_; choicesStr = concatMapStringsSep ", " lib.strings.escapeNixIdentifier (attrNames tags); in mkOptionType { name = "attrTag"; description = "attribute-tagged union"; descriptionClass = "noun"; getSubOptions = prefix: mapAttrs (tagName: tagOption: tagOption // { loc = prefix ++ [ tagName ]; }) tags; check = v: isAttrs v && length (attrNames v) == 1 && tags ? ${head (attrNames v)}; merge = loc: defs: let choice = head (attrNames (head defs).value); checkedValueDefs = map ( def: assert (length (attrNames def.value)) == 1; if (head (attrNames def.value)) != choice then throw "The option `${showOption loc}` is defined both as `${choice}` and `${head (attrNames def.value)}`, in ${showFiles (getFiles defs)}." else { inherit (def) file; value = def.value.${choice}; } ) defs; in if tags ? ${choice} then { ${choice} = (lib.modules.evalOptionValue (loc ++ [ choice ]) tags.${choice} checkedValueDefs).value; } else throw "The option `${showOption loc}` is defined as ${lib.strings.escapeNixIdentifier choice}, but ${lib.strings.escapeNixIdentifier choice} is not among the valid choices (${choicesStr}). Value ${choice} was defined in ${showFiles (getFiles defs)}."; nestedTypes = tags; functor = defaultFunctor "attrTag" // { type = { tags, ... }: types.attrTag tags; payload = { inherit tags; }; binOp = let # Add metadata in the format that submodules work with wrapOptionDecl = option: { options = option; _file = "<attrTag {...}>"; pos = null; }; in a: b: { tags = a.tags // b.tags // mapAttrs ( tagName: bOpt: lib.mergeOptionDecls # FIXME: loc is not accurate; should include prefix # Fortunately, it's only used for error messages, where a "relative" location is kinda ok. # It is also returned though, but use of the attribute seems rare? [ tagName ] [ (wrapOptionDecl a.tags.${tagName}) (wrapOptionDecl bOpt) ] // { # mergeOptionDecls is not idempotent in these attrs: declarations = a.tags.${tagName}.declarations ++ bOpt.declarations; declarationPositions = a.tags.${tagName}.declarationPositions ++ bOpt.declarationPositions; } ) (builtins.intersectAttrs a.tags b.tags); }; }; };
# A value produced by `lib.mkLuaInline` luaInline = mkOptionType { name = "luaInline"; description = "inline lua"; descriptionClass = "noun"; check = x: x._type or null == "lua-inline"; merge = mergeEqualOption; };
uniq = unique { message = ""; };
unique = { message }: type: mkOptionType rec { name = "unique"; inherit (type) description descriptionClass check; merge = mergeUniqueOption { inherit message; inherit (type) merge; }; emptyValue = type.emptyValue; getSubOptions = type.getSubOptions; getSubModules = type.getSubModules; substSubModules = m: uniq (type.substSubModules m); functor = elemTypeFunctor name { elemType = type; } // { type = payload: types.unique { inherit message; } payload.elemType; }; nestedTypes.elemType = type; };
# Null or value of ... nullOr = elemType: mkOptionType rec { name = "nullOr"; description = "null or ${ optionDescriptionPhrase (class: class == "noun" || class == "conjunction") elemType }"; descriptionClass = "conjunction"; check = x: x == null || elemType.check x; merge = loc: defs: let nrNulls = count (def: def.value == null) defs; in if nrNulls == length defs then null else if nrNulls != 0 then throw "The option `${showOption loc}` is defined both null and not null, in ${showFiles (getFiles defs)}." else elemType.merge loc defs; emptyValue = { value = null; }; getSubOptions = elemType.getSubOptions; getSubModules = elemType.getSubModules; substSubModules = m: nullOr (elemType.substSubModules m); functor = (elemTypeFunctor name { inherit elemType; }) // { type = payload: types.nullOr payload.elemType; }; nestedTypes.elemType = elemType; };
functionTo = elemType: mkOptionType { name = "functionTo"; description = "function that evaluates to a(n) ${ optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType }"; descriptionClass = "composite"; check = isFunction; merge = loc: defs: { # An argument attribute has a default when it has a default in all definitions __functionArgs = lib.zipAttrsWith (_: lib.all (x: x)) ( lib.map (fn: lib.functionArgs fn.value) defs ); __functor = _: callerArgs: (mergeDefinitions (loc ++ [ "<function body>" ]) elemType ( map (fn: { inherit (fn) file; value = fn.value callerArgs; }) defs )).mergedValue; }; getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "<function body>" ]); getSubModules = elemType.getSubModules; substSubModules = m: functionTo (elemType.substSubModules m); functor = (elemTypeFunctor "functionTo" { inherit elemType; }) // { type = payload: types.functionTo payload.elemType; }; nestedTypes.elemType = elemType; };
# A submodule (like typed attribute set). See NixOS manual. submodule = modules: submoduleWith { shorthandOnlyDefinesConfig = true; modules = toList modules; };
# A module to be imported in some other part of the configuration. deferredModule = deferredModuleWith { };
# A module to be imported in some other part of the configuration. # `staticModules`' options will be added to the documentation, unlike # options declared via `config`. deferredModuleWith = attrs@{ staticModules ? [ ], }: mkOptionType { name = "deferredModule"; description = "module"; descriptionClass = "noun"; check = x: isAttrs x || isFunction x || path.check x; merge = loc: defs: { imports = staticModules ++ map ( def: lib.setDefaultModuleLocation "${def.file}, via option ${showOption loc}" def.value ) defs; }; inherit (submoduleWith { modules = staticModules; }) getSubOptions getSubModules ; substSubModules = m: deferredModuleWith ( attrs // { staticModules = m; } ); functor = defaultFunctor "deferredModuleWith" // { type = types.deferredModuleWith; payload = { inherit staticModules; }; binOp = lhs: rhs: { staticModules = lhs.staticModules ++ rhs.staticModules; }; }; };
# The type of a type! optionType = mkOptionType { name = "optionType"; description = "optionType"; descriptionClass = "noun"; check = value: value._type or null == "option-type"; merge = loc: defs: if length defs == 1 then (head defs).value else let # Prepares the type definitions for mergeOptionDecls, which # annotates submodules types with file locations optionModules = map ( { value, file }: { _file = file; # There's no way to merge types directly from the module system, # but we can cheat a bit by just declaring an option with the type options = lib.mkOption { type = value; }; } ) defs; # Merges all the types into a single one, including submodule merging. # This also propagates file information to all submodules mergedOption = fixupOptionType loc (mergeOptionDecls loc optionModules); in mergedOption.type; };
submoduleWith = { modules, specialArgs ? { }, shorthandOnlyDefinesConfig ? false, description ? null, class ? null, }@attrs: let inherit (lib.modules) evalModules;
allModules = defs: map ( { value, file }: if isAttrs value && shorthandOnlyDefinesConfig then { _file = file; config = value; } else { _file = file; imports = [ value ]; } ) defs;
base = evalModules { inherit class specialArgs; modules = [ { # This is a work-around for the fact that some sub-modules, # such as the one included in an attribute set, expects an "args" # attribute to be given to the sub-module. As the option # evaluation does not have any specific attribute name yet, we # provide a default for the documentation and the freeform type. # # This is necessary as some option declaration might use the # "name" attribute given as argument of the submodule and use it # as the default of option declarations. # # We use lookalike unicode single angle quotation marks because # of the docbook transformation the options receive. In all uses # > and < wouldn't be encoded correctly so the encoded values # would be used, and use of `<` and `>` would break the XML document. # It shouldn't cause an issue since this is cosmetic for the manual. _module.args.name = lib.mkOptionDefault "‹name›"; } ] ++ modules; };
freeformType = base._module.freeformType;
name = "submodule";
in mkOptionType { inherit name; description = if description != null then description else let docsEval = base.extendModules { modules = [ noCheckForDocsModule ]; }; in docsEval._module.freeformType.description or name; check = x: isAttrs x || isFunction x || path.check x; merge = loc: defs: (base.extendModules { modules = [ { _module.args.name = last loc; } ] ++ allModules defs; prefix = loc; }).config; emptyValue = { value = { }; }; getSubOptions = prefix: let docsEval = ( base.extendModules { inherit prefix; modules = [ noCheckForDocsModule ]; } ); # Intentionally shadow the freeformType from the possibly *checked* # configuration. See `noCheckForDocsModule` comment. inherit (docsEval._module) freeformType; in docsEval.options // optionalAttrs (freeformType != null) { # Expose the sub options of the freeform type. Note that the option # discovery doesn't care about the attribute name used here, so this # is just to avoid conflicts with potential options from the submodule _freeformOptions = freeformType.getSubOptions prefix; }; getSubModules = modules; substSubModules = m: submoduleWith ( attrs // { modules = m; } ); nestedTypes = lib.optionalAttrs (freeformType != null) { freeformType = freeformType; }; functor = defaultFunctor name // { type = types.submoduleWith; payload = { inherit modules class specialArgs shorthandOnlyDefinesConfig description ; }; binOp = lhs: rhs: { class = # `or null` was added for backwards compatibility only. `class` is # always set in the current version of the module system. if lhs.class or null == null then rhs.class or null else if rhs.class or null == null then lhs.class or null else if lhs.class or null == rhs.class then lhs.class or null else throw "A submoduleWith option is declared multiple times with conflicting class values \"${toString lhs.class}\" and \"${toString rhs.class}\"."; modules = lhs.modules ++ rhs.modules; specialArgs = let intersecting = builtins.intersectAttrs lhs.specialArgs rhs.specialArgs; in if intersecting == { } then lhs.specialArgs // rhs.specialArgs else throw "A submoduleWith option is declared multiple times with the same specialArgs \"${toString (attrNames intersecting)}\""; shorthandOnlyDefinesConfig = if lhs.shorthandOnlyDefinesConfig == null then rhs.shorthandOnlyDefinesConfig else if rhs.shorthandOnlyDefinesConfig == null then lhs.shorthandOnlyDefinesConfig else if lhs.shorthandOnlyDefinesConfig == rhs.shorthandOnlyDefinesConfig then lhs.shorthandOnlyDefinesConfig else throw "A submoduleWith option is declared multiple times with conflicting shorthandOnlyDefinesConfig values"; description = if lhs.description == null then rhs.description else if rhs.description == null then lhs.description else if lhs.description == rhs.description then lhs.description else throw "A submoduleWith option is declared multiple times with conflicting descriptions"; }; }; };
# A value from a set of allowed ones. enum = values: let inherit (lib.lists) unique; show = v: if builtins.isString v then ''"${v}"'' else if builtins.isInt v then builtins.toString v else if builtins.isBool v then boolToString v else ''<${builtins.typeOf v}>''; in mkOptionType rec { name = "enum"; description = # Length 0 or 1 enums may occur in a design pattern with type merging # where an "interface" module declares an empty enum and other modules # provide implementations, each extending the enum with their own # identifier. if values == [ ] then "impossible (empty enum)" else if builtins.length values == 1 then "value ${show (builtins.head values)} (singular enum)" else "one of ${concatMapStringsSep ", " show values}"; descriptionClass = if builtins.length values < 2 then "noun" else "conjunction"; check = flip elem values; merge = mergeEqualOption; functor = (defaultFunctor name) // { payload = { inherit values; }; type = payload: types.enum payload.values; binOp = a: b: { values = unique (a.values ++ b.values); }; }; };
# Either value of type `t1` or `t2`. either = t1: t2: mkOptionType rec { name = "either"; description = if t1.descriptionClass or null == "nonRestrictiveClause" then # Plain, but add comma "${t1.description}, or ${ optionDescriptionPhrase (class: class == "noun" || class == "conjunction") t2 }" else "${optionDescriptionPhrase (class: class == "noun" || class == "conjunction") t1} or ${ optionDescriptionPhrase ( class: class == "noun" || class == "conjunction" || class == "composite" ) t2 }"; descriptionClass = "conjunction"; check = x: t1.check x || t2.check x; merge = loc: defs: let defList = map (d: d.value) defs; in if all (x: t1.check x) defList then t1.merge loc defs else if all (x: t2.check x) defList then t2.merge loc defs else mergeOneOption loc defs; typeMerge = f': let mt1 = t1.typeMerge (elemAt f'.payload.elemType 0).functor; mt2 = t2.typeMerge (elemAt f'.payload.elemType 1).functor; in if (name == f'.name) && (mt1 != null) && (mt2 != null) then functor.type mt1 mt2 else null; functor = elemTypeFunctor name { elemType = [ t1 t2 ]; }; nestedTypes.left = t1; nestedTypes.right = t2; };
# Any of the types in the given list oneOf = ts: let head' = if ts == [ ] then throw "types.oneOf needs to get at least one type in its argument" else head ts; tail' = tail ts; in foldl' either head' tail';
# Either value of type `coercedType` or `finalType`, the former is # converted to `finalType` using `coerceFunc`. coercedTo = coercedType: coerceFunc: finalType: assert lib.assertMsg ( coercedType.getSubModules == null ) "coercedTo: coercedType must not have submodules (it’s a ${coercedType.description})"; mkOptionType rec { name = "coercedTo"; description = "${optionDescriptionPhrase (class: class == "noun") finalType} or ${ optionDescriptionPhrase (class: class == "noun") coercedType } convertible to it"; check = x: (coercedType.check x && finalType.check (coerceFunc x)) || finalType.check x; merge = loc: defs: let coerceVal = val: if coercedType.check val then coerceFunc val else val; in finalType.merge loc (map (def: def // { value = coerceVal def.value; }) defs); emptyValue = finalType.emptyValue; getSubOptions = finalType.getSubOptions; getSubModules = finalType.getSubModules; substSubModules = m: coercedTo coercedType coerceFunc (finalType.substSubModules m); typeMerge = t: null; functor = (defaultFunctor name) // { wrappedDeprecationMessage = makeWrappedDeprecationMessage { elemType = finalType; }; }; nestedTypes.coercedType = coercedType; nestedTypes.finalType = finalType; }; /** Augment the given type with an additional type check function.
:::{.warning} This function has some broken behavior see: [#396021](https://github.com/NixOS/nixpkgs/issues/396021) Fixing is not trivial, we appreciate any help! ::: */ addCheck = elemType: check: elemType // { check = x: elemType.check x && check x; };
};
/** Merges two option types together.
:::{.note} Uses the type merge function of the first type, to merge it with the second type.
Usually types can only be merged if they are of the same type :::
# Inputs
: `a` (option type): The first option type. : `b` (option type): The second option type.
# Returns
- The merged option type. - `{ _type = "merge-error"; error = "Cannot merge types"; }` if the types can't be merged.
# Examples :::{.example} ## `lib.types.mergeTypes` usage example ```nix let enumAB = lib.types.enum ["A" "B"]; enumXY = lib.types.enum ["X" "Y"]; # This operation could be notated as: [ A ] | [ B ] -> [ A B ] merged = lib.types.mergeTypes enumAB enumXY; # -> enum [ "A" "B" "X" "Y" ] in assert merged.check "A"; # true assert merged.check "B"; # true assert merged.check "X"; # true assert merged.check "Y"; # true merged.check "C" # false ``` ::: */ mergeTypes = a: b: assert isOptionType a && isOptionType b; let merged = a.typeMerge b.functor; in if merged == null then setType "merge-error" { error = "Cannot merge types"; } else merged; };
inouter_types // outer_types.types