Extract properties.nix and modules.nix from options.nix.
svn path=/nixpkgs/trunk/; revision=16339
This commit is contained in:
parent
9cd30e68e3
commit
b09382fcd1
@ -6,7 +6,9 @@ let
|
|||||||
stringsWithDeps = import ./strings-with-deps.nix;
|
stringsWithDeps = import ./strings-with-deps.nix;
|
||||||
attrsets = import ./attrsets.nix;
|
attrsets = import ./attrsets.nix;
|
||||||
sources = import ./sources.nix;
|
sources = import ./sources.nix;
|
||||||
|
modules = import ./modules.nix;
|
||||||
options = import ./options.nix;
|
options = import ./options.nix;
|
||||||
|
properties = import ./properties.nix;
|
||||||
types = import ./types.nix;
|
types = import ./types.nix;
|
||||||
meta = import ./meta.nix;
|
meta = import ./meta.nix;
|
||||||
debug = import ./debug.nix;
|
debug = import ./debug.nix;
|
||||||
@ -15,9 +17,9 @@ let
|
|||||||
|
|
||||||
in
|
in
|
||||||
{ inherit trivial lists strings stringsWithDeps attrsets sources options
|
{ inherit trivial lists strings stringsWithDeps attrsets sources options
|
||||||
types meta debug maintainers;
|
properties modules types meta debug maintainers;
|
||||||
}
|
}
|
||||||
# !!! don't include everything at top-level; perhaps only the most
|
# !!! don't include everything at top-level; perhaps only the most
|
||||||
# commonly used functions.
|
# commonly used functions.
|
||||||
// trivial // lists // strings // stringsWithDeps // attrsets // sources
|
// trivial // lists // strings // stringsWithDeps // attrsets // sources
|
||||||
// options // types // meta // debug // misc
|
// properties // options // types // meta // debug // misc
|
||||||
|
71
pkgs/lib/modules.nix
Normal file
71
pkgs/lib/modules.nix
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# NixOS module handling.
|
||||||
|
|
||||||
|
let lib = import ./default.nix; in
|
||||||
|
|
||||||
|
with { inherit (builtins) head tail; };
|
||||||
|
with import ./trivial.nix;
|
||||||
|
with import ./lists.nix;
|
||||||
|
with import ./misc.nix;
|
||||||
|
with import ./attrsets.nix;
|
||||||
|
with import ./properties.nix;
|
||||||
|
|
||||||
|
rec {
|
||||||
|
|
||||||
|
# Unfortunately this can also be a string.
|
||||||
|
isPath = x: !(
|
||||||
|
builtins.isFunction x
|
||||||
|
|| builtins.isAttrs x
|
||||||
|
|| builtins.isInt x
|
||||||
|
|| builtins.isBool x
|
||||||
|
|| builtins.isList x
|
||||||
|
);
|
||||||
|
|
||||||
|
importIfPath = path:
|
||||||
|
if isPath path then
|
||||||
|
import path
|
||||||
|
else
|
||||||
|
path;
|
||||||
|
|
||||||
|
applyIfFunction = f: arg:
|
||||||
|
if builtins.isFunction f then
|
||||||
|
f arg
|
||||||
|
else
|
||||||
|
f;
|
||||||
|
|
||||||
|
moduleClosure = initModules: args:
|
||||||
|
let
|
||||||
|
moduleImport = m:
|
||||||
|
(applyIfFunction (importIfPath m) args) // {
|
||||||
|
# used by generic closure to avoid duplicated imports.
|
||||||
|
key = m;
|
||||||
|
};
|
||||||
|
|
||||||
|
removeKeys = list: map (m: removeAttrs m ["key"]) list;
|
||||||
|
|
||||||
|
getImports = m:
|
||||||
|
if m ? config || m ? options then
|
||||||
|
attrByPath ["imports"] [] m
|
||||||
|
else
|
||||||
|
toList (rmProperties (attrByPath ["require"] [] (delayProperties m)));
|
||||||
|
|
||||||
|
getImportedPaths = m: filter isPath (getImports m);
|
||||||
|
getImportedSets = m: filter (x: !isPath x) (getImports m);
|
||||||
|
|
||||||
|
inlineImportedSets = list:
|
||||||
|
lib.concatMap (m:[m] ++ map moduleImport (getImportedSets m)) list;
|
||||||
|
in
|
||||||
|
removeKeys (inlineImportedSets (lazyGenericClosure {
|
||||||
|
startSet = map moduleImport initModules;
|
||||||
|
operator = m: map moduleImport (getImportedPaths m);
|
||||||
|
}));
|
||||||
|
|
||||||
|
selectDeclsAndDefs = modules:
|
||||||
|
lib.concatMap (m:
|
||||||
|
if m ? config || m ? options then
|
||||||
|
attrByPath ["options"] [] m
|
||||||
|
++ attrByPath ["config"] [] m
|
||||||
|
else
|
||||||
|
[ m ]
|
||||||
|
) modules;
|
||||||
|
|
||||||
|
}
|
@ -7,6 +7,8 @@ with import ./trivial.nix;
|
|||||||
with import ./lists.nix;
|
with import ./lists.nix;
|
||||||
with import ./misc.nix;
|
with import ./misc.nix;
|
||||||
with import ./attrsets.nix;
|
with import ./attrsets.nix;
|
||||||
|
with import ./properties.nix;
|
||||||
|
with import ./modules.nix;
|
||||||
|
|
||||||
rec {
|
rec {
|
||||||
|
|
||||||
@ -253,64 +255,6 @@ rec {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
# Unfortunately this can also be a string.
|
|
||||||
isPath = x: !(
|
|
||||||
builtins.isFunction x
|
|
||||||
|| builtins.isAttrs x
|
|
||||||
|| builtins.isInt x
|
|
||||||
|| builtins.isBool x
|
|
||||||
|| builtins.isList x
|
|
||||||
);
|
|
||||||
|
|
||||||
importIfPath = path:
|
|
||||||
if isPath path then
|
|
||||||
import path
|
|
||||||
else
|
|
||||||
path;
|
|
||||||
|
|
||||||
applyIfFunction = f: arg:
|
|
||||||
if builtins.isFunction f then
|
|
||||||
f arg
|
|
||||||
else
|
|
||||||
f;
|
|
||||||
|
|
||||||
moduleClosure = initModules: args:
|
|
||||||
let
|
|
||||||
moduleImport = m:
|
|
||||||
(applyIfFunction (importIfPath m) args) // {
|
|
||||||
# used by generic closure to avoid duplicated imports.
|
|
||||||
key = m;
|
|
||||||
};
|
|
||||||
|
|
||||||
removeKeys = list: map (m: removeAttrs m ["key"]) list;
|
|
||||||
|
|
||||||
getImports = m:
|
|
||||||
if m ? config || m ? options then
|
|
||||||
attrByPath ["imports"] [] m
|
|
||||||
else
|
|
||||||
toList (rmProperties (attrByPath ["require"] [] (delayProperties m)));
|
|
||||||
|
|
||||||
getImportedPaths = m: filter isPath (getImports m);
|
|
||||||
getImportedSets = m: filter (x: !isPath x) (getImports m);
|
|
||||||
|
|
||||||
inlineImportedSets = list:
|
|
||||||
lib.concatMap (m:[m] ++ map moduleImport (getImportedSets m)) list;
|
|
||||||
in
|
|
||||||
removeKeys (inlineImportedSets (lazyGenericClosure {
|
|
||||||
startSet = map moduleImport initModules;
|
|
||||||
operator = m: map moduleImport (getImportedPaths m);
|
|
||||||
}));
|
|
||||||
|
|
||||||
selectDeclsAndDefs = modules:
|
|
||||||
lib.concatMap (m:
|
|
||||||
if m ? config || m ? options then
|
|
||||||
attrByPath ["options"] [] m
|
|
||||||
++ attrByPath ["config"] [] m
|
|
||||||
else
|
|
||||||
[ m ]
|
|
||||||
) modules;
|
|
||||||
|
|
||||||
|
|
||||||
fixableMergeFun = merge: f: config:
|
fixableMergeFun = merge: f: config:
|
||||||
merge (
|
merge (
|
||||||
# remove require because this is not an option.
|
# remove require because this is not an option.
|
||||||
@ -384,363 +328,4 @@ rec {
|
|||||||
) [] options;
|
) [] options;
|
||||||
|
|
||||||
|
|
||||||
/* Option Properties */
|
|
||||||
# Generalize the problem of delayable properties. Any property can be created
|
|
||||||
|
|
||||||
|
|
||||||
# Tell that nothing is defined. When properties are evaluated, this type
|
|
||||||
# is used to remove an entry. Thus if your property evaluation semantic
|
|
||||||
# implies that you have to mute the content of an attribute, then your
|
|
||||||
# property should produce this value.
|
|
||||||
isNotdef = attrs: (typeOf attrs) == "notdef";
|
|
||||||
mkNotdef = {_type = "notdef";};
|
|
||||||
|
|
||||||
# General property type, it has a property attribute and a content
|
|
||||||
# attribute. The property attribute refer to an attribute set which
|
|
||||||
# contains a _type attribute and a list of functions which are used to
|
|
||||||
# evaluate this property. The content attribute is used to stack property
|
|
||||||
# on top of each other.
|
|
||||||
#
|
|
||||||
# The optional functions which may be contained in the property attribute
|
|
||||||
# are:
|
|
||||||
# - onDelay: run on a copied property.
|
|
||||||
# - onGlobalDelay: run on all copied properties.
|
|
||||||
# - onEval: run on an evaluated property.
|
|
||||||
# - onGlobalEval: run on a list of property stack on top of their values.
|
|
||||||
isProperty = attrs: (typeOf attrs) == "property";
|
|
||||||
mkProperty = p@{property, content, ...}: p // {
|
|
||||||
_type = "property";
|
|
||||||
};
|
|
||||||
|
|
||||||
# Go throw the stack of properties and apply the function `op' on all
|
|
||||||
# property and call the function `nul' on the final value which is not a
|
|
||||||
# property. The stack is traversed in reversed order. The `op' function
|
|
||||||
# should expect a property with a content which have been modified.
|
|
||||||
#
|
|
||||||
# Warning: The `op' function expects only one argument in order to avoid
|
|
||||||
# calls to mkProperties as the argument is already a valid property which
|
|
||||||
# contains the result of the folding inside the content attribute.
|
|
||||||
foldProperty = op: nul: attrs:
|
|
||||||
if isProperty attrs then
|
|
||||||
op (attrs // {
|
|
||||||
content = foldProperty op nul attrs.content;
|
|
||||||
})
|
|
||||||
else
|
|
||||||
nul attrs;
|
|
||||||
|
|
||||||
# Simple function which can be used as the `op' argument of the
|
|
||||||
# foldProperty function. Properties that you don't want to handle can be
|
|
||||||
# ignored with the `id' function. `isSearched' is a function which should
|
|
||||||
# check the type of a property and return a boolean value. `thenFun' and
|
|
||||||
# `elseFun' are functions which behave as the `op' argument of the
|
|
||||||
# foldProperty function.
|
|
||||||
foldFilter = isSearched: thenFun: elseFun: attrs:
|
|
||||||
if isSearched attrs.property then
|
|
||||||
thenFun attrs
|
|
||||||
else
|
|
||||||
elseFun attrs;
|
|
||||||
|
|
||||||
|
|
||||||
# Move properties from the current attribute set to the attribute
|
|
||||||
# contained in this attribute set. This trigger property handlers called
|
|
||||||
# `onDelay' and `onGlobalDelay'.
|
|
||||||
delayProperties = attrs:
|
|
||||||
let cleanAttrs = rmProperties attrs; in
|
|
||||||
if isProperty attrs then
|
|
||||||
lib.mapAttrs (a: v:
|
|
||||||
lib.addErrorContext "while moving properties on the attribute `${a}'." (
|
|
||||||
triggerPropertiesGlobalDelay a (
|
|
||||||
triggerPropertiesDelay a (
|
|
||||||
copyProperties attrs v
|
|
||||||
)))) cleanAttrs
|
|
||||||
else
|
|
||||||
attrs;
|
|
||||||
|
|
||||||
# Call onDelay functions.
|
|
||||||
triggerPropertiesDelay = name: attrs:
|
|
||||||
let
|
|
||||||
callOnDelay = p@{property, ...}:
|
|
||||||
lib.addErrorContext "while calling a onDelay function." (
|
|
||||||
if property ? onDelay then
|
|
||||||
property.onDelay name p
|
|
||||||
else
|
|
||||||
p
|
|
||||||
);
|
|
||||||
in
|
|
||||||
foldProperty callOnDelay id attrs;
|
|
||||||
|
|
||||||
# Call onGlobalDelay functions.
|
|
||||||
triggerPropertiesGlobalDelay = name: attrs:
|
|
||||||
let
|
|
||||||
globalDelayFuns = uniqListExt {
|
|
||||||
getter = property: property._type;
|
|
||||||
inputList = foldProperty (p@{property, content, ...}:
|
|
||||||
if property ? onGlobalDelay then
|
|
||||||
[ property ] ++ content
|
|
||||||
else
|
|
||||||
content
|
|
||||||
) (a: []) attrs;
|
|
||||||
};
|
|
||||||
|
|
||||||
callOnGlobalDelay = property: content:
|
|
||||||
lib.addErrorContext "while calling a onGlobalDelay function." (
|
|
||||||
property.onGlobalDelay name content
|
|
||||||
);
|
|
||||||
in
|
|
||||||
fold callOnGlobalDelay attrs globalDelayFuns;
|
|
||||||
|
|
||||||
# Expect a list of values which may have properties and return the same
|
|
||||||
# list of values where all properties have been evaluated and where all
|
|
||||||
# ignored values are removed. This trigger property handlers called
|
|
||||||
# `onEval' and `onGlobalEval'.
|
|
||||||
evalProperties = valList:
|
|
||||||
if valList != [] then
|
|
||||||
filter (x: !isNotdef x) (
|
|
||||||
lib.addErrorContext "while evaluating properties an attribute." (
|
|
||||||
triggerPropertiesGlobalEval (
|
|
||||||
map triggerPropertiesEval valList
|
|
||||||
)))
|
|
||||||
else
|
|
||||||
valList;
|
|
||||||
|
|
||||||
# Call onEval function
|
|
||||||
triggerPropertiesEval = val:
|
|
||||||
foldProperty (p@{property, ...}:
|
|
||||||
lib.addErrorContext "while calling a onEval function." (
|
|
||||||
if property ? onEval then
|
|
||||||
property.onEval p
|
|
||||||
else
|
|
||||||
p
|
|
||||||
)
|
|
||||||
) id val;
|
|
||||||
|
|
||||||
# Call onGlobalEval function
|
|
||||||
triggerPropertiesGlobalEval = valList:
|
|
||||||
let
|
|
||||||
globalEvalFuns = uniqListExt {
|
|
||||||
getter = property: property._type;
|
|
||||||
inputList =
|
|
||||||
fold (attrs: list:
|
|
||||||
foldProperty (p@{property, content, ...}:
|
|
||||||
if property ? onGlobalEval then
|
|
||||||
[ property ] ++ content
|
|
||||||
else
|
|
||||||
content
|
|
||||||
) (a: list) attrs
|
|
||||||
) [] valList;
|
|
||||||
};
|
|
||||||
|
|
||||||
callOnGlobalEval = property: valList:
|
|
||||||
lib.addErrorContext "while calling a onGlobalEval function." (
|
|
||||||
property.onGlobalEval valList
|
|
||||||
);
|
|
||||||
in
|
|
||||||
fold callOnGlobalEval valList globalEvalFuns;
|
|
||||||
|
|
||||||
# Remove all properties on top of a value and return the value.
|
|
||||||
rmProperties =
|
|
||||||
foldProperty (p@{content, ...}: content) id;
|
|
||||||
|
|
||||||
# Copy properties defined on a value on another value.
|
|
||||||
copyProperties = attrs: newAttrs:
|
|
||||||
foldProperty id (x: newAttrs) attrs;
|
|
||||||
|
|
||||||
/* If. ThenElse. Always. */
|
|
||||||
|
|
||||||
# create "if" statement that can be delayed on sets until a "then-else" or
|
|
||||||
# "always" set is reached. When an always set is reached the condition
|
|
||||||
# is ignore.
|
|
||||||
|
|
||||||
# Create a "If" property which only contains a condition.
|
|
||||||
isIf = attrs: (typeOf attrs) == "if";
|
|
||||||
mkIf = condition: content: mkProperty {
|
|
||||||
property = {
|
|
||||||
_type = "if";
|
|
||||||
onGlobalDelay = onIfGlobalDelay;
|
|
||||||
onEval = onIfEval;
|
|
||||||
inherit condition;
|
|
||||||
};
|
|
||||||
inherit content;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Create a "ThenElse" property which contains choices which can choosed by
|
|
||||||
# the evaluation of an "If" statement.
|
|
||||||
isThenElse = attrs: (typeOf attrs) == "then-else";
|
|
||||||
mkThenElse = attrs:
|
|
||||||
assert attrs ? thenPart && attrs ? elsePart;
|
|
||||||
mkProperty {
|
|
||||||
property = {
|
|
||||||
_type = "then-else";
|
|
||||||
onEval = val: throw "Missing mkIf statement.";
|
|
||||||
inherit (attrs) thenPart elsePart;
|
|
||||||
};
|
|
||||||
content = mkNotdef;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Create an "Always" property remove ignore all "If" statement.
|
|
||||||
isAlways = attrs: (typeOf attrs) == "always";
|
|
||||||
mkAlways = value:
|
|
||||||
mkProperty {
|
|
||||||
property = {
|
|
||||||
_type = "always";
|
|
||||||
onEval = p@{content, ...}: content;
|
|
||||||
inherit value;
|
|
||||||
};
|
|
||||||
content = mkNotdef;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Remove all "If" statement defined on a value.
|
|
||||||
rmIf = foldProperty (
|
|
||||||
foldFilter isIf
|
|
||||||
({content, ...}: content)
|
|
||||||
id
|
|
||||||
) id;
|
|
||||||
|
|
||||||
# Evaluate the "If" statements when either "ThenElse" or "Always"
|
|
||||||
# statement is encounter. Otherwise it remove multiple If statement and
|
|
||||||
# replace them by one "If" staement where the condition is the list of all
|
|
||||||
# conditions joined with a "and" operation.
|
|
||||||
onIfGlobalDelay = name: content:
|
|
||||||
let
|
|
||||||
# extract if statements and non-if statements and repectively put them
|
|
||||||
# in the attribute list and attrs.
|
|
||||||
ifProps =
|
|
||||||
foldProperty
|
|
||||||
(foldFilter (p: isIf p || isThenElse p || isAlways p)
|
|
||||||
# then, push the codition inside the list list
|
|
||||||
(p@{property, content, ...}:
|
|
||||||
{ inherit (content) attrs;
|
|
||||||
list = [property] ++ content.list;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# otherwise, add the propertie.
|
|
||||||
(p@{property, content, ...}:
|
|
||||||
{ inherit (content) list;
|
|
||||||
attrs = p // { content = content.attrs; };
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(attrs: { list = []; inherit attrs; })
|
|
||||||
content;
|
|
||||||
|
|
||||||
# compute the list of if statements.
|
|
||||||
evalIf = content: condition: list:
|
|
||||||
if list == [] then
|
|
||||||
mkIf condition content
|
|
||||||
else
|
|
||||||
let p = head list; in
|
|
||||||
|
|
||||||
# evaluate the condition.
|
|
||||||
if isThenElse p then
|
|
||||||
if condition then
|
|
||||||
copyProperties content p.thenPart
|
|
||||||
else
|
|
||||||
copyProperties content p.elsePart
|
|
||||||
# ignore the condition.
|
|
||||||
else if isAlways p then
|
|
||||||
copyProperties content p.value
|
|
||||||
# otherwise (isIf)
|
|
||||||
else
|
|
||||||
evalIf content (condition && p.condition) (tail list);
|
|
||||||
in
|
|
||||||
evalIf ifProps.attrs true ifProps.list;
|
|
||||||
|
|
||||||
# Evaluate the condition of the "If" statement to either get the value or
|
|
||||||
# to ignore the value.
|
|
||||||
onIfEval = p@{property, content, ...}:
|
|
||||||
if property.condition then
|
|
||||||
content
|
|
||||||
else
|
|
||||||
mkNotdef;
|
|
||||||
|
|
||||||
/* mkOverride */
|
|
||||||
|
|
||||||
# Create an "Override" statement which allow the user to define
|
|
||||||
# prioprities between values. The default priority is 100 and the lowest
|
|
||||||
# priorities are kept. The template argument must reproduce the same
|
|
||||||
# attribute set hierachy to override leaves of the hierarchy.
|
|
||||||
isOverride = attrs: (typeOf attrs) == "override";
|
|
||||||
mkOverride = priority: template: content: mkProperty {
|
|
||||||
property = {
|
|
||||||
_type = "override";
|
|
||||||
onDelay = onOverrideDelay;
|
|
||||||
onGlobalEval = onOverrideGlobalEval;
|
|
||||||
inherit priority template;
|
|
||||||
};
|
|
||||||
inherit content;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Sugar to override the default value of the option by making a new
|
|
||||||
# default value based on the configuration.
|
|
||||||
mkDefaultValue = content: mkOverride 1000 {} content;
|
|
||||||
|
|
||||||
# Make the template traversal in function of the property traversal. If
|
|
||||||
# the template define a non-empty attribute set, then the property is
|
|
||||||
# copied only on all mentionned attributes inside the template.
|
|
||||||
# Otherwise, the property is kept on all sub-attribute definitions.
|
|
||||||
onOverrideDelay = name: p@{property, content, ...}:
|
|
||||||
let inherit (property) template; in
|
|
||||||
if isAttrs template && template != {} then
|
|
||||||
if hasAttr name template then
|
|
||||||
p // {
|
|
||||||
property = p.property // {
|
|
||||||
template = builtins.getAttr name template;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
# Do not override the attribute \name\
|
|
||||||
else
|
|
||||||
content
|
|
||||||
# Override values defined inside the attribute \name\.
|
|
||||||
else
|
|
||||||
p;
|
|
||||||
|
|
||||||
# Ignore all values which have a higher value of the priority number.
|
|
||||||
onOverrideGlobalEval = valList:
|
|
||||||
let
|
|
||||||
defaultPrio = 100;
|
|
||||||
|
|
||||||
inherit (builtins) lessThan;
|
|
||||||
|
|
||||||
getPrioVal =
|
|
||||||
foldProperty
|
|
||||||
(foldFilter isOverride
|
|
||||||
(p@{property, content, ...}:
|
|
||||||
if content ? priority && lessThan content.priority property.priority then
|
|
||||||
content
|
|
||||||
else
|
|
||||||
content // {
|
|
||||||
inherit (property) priority;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
(p@{property, content, ...}:
|
|
||||||
content // {
|
|
||||||
value = p // { content = content.value; };
|
|
||||||
}
|
|
||||||
)
|
|
||||||
) (value: { inherit value; });
|
|
||||||
|
|
||||||
addDefaultPrio = x:
|
|
||||||
if x ? priority then x
|
|
||||||
else x // { priority = defaultPrio; };
|
|
||||||
|
|
||||||
prioValList = map (x: addDefaultPrio (getPrioVal x)) valList;
|
|
||||||
|
|
||||||
higherPrio =
|
|
||||||
if prioValList == [] then
|
|
||||||
defaultPrio
|
|
||||||
else
|
|
||||||
fold (x: min:
|
|
||||||
if lessThan x.priority min then
|
|
||||||
x.priority
|
|
||||||
else
|
|
||||||
min
|
|
||||||
) (head prioValList).priority (tail prioValList);
|
|
||||||
in
|
|
||||||
map (x:
|
|
||||||
if x.priority == higherPrio then
|
|
||||||
x.value
|
|
||||||
else
|
|
||||||
mkNotdef
|
|
||||||
) prioValList;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
371
pkgs/lib/properties.nix
Normal file
371
pkgs/lib/properties.nix
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
# Nixpkgs/NixOS properties. Generalize the problem of delayable (not yet
|
||||||
|
# evaluable) properties like mkIf.
|
||||||
|
|
||||||
|
let lib = import ./default.nix; in
|
||||||
|
|
||||||
|
with { inherit (builtins) head tail; };
|
||||||
|
with import ./trivial.nix;
|
||||||
|
with import ./lists.nix;
|
||||||
|
with import ./misc.nix;
|
||||||
|
with import ./attrsets.nix;
|
||||||
|
|
||||||
|
rec {
|
||||||
|
|
||||||
|
inherit (lib) typeOf;
|
||||||
|
|
||||||
|
# Tell that nothing is defined. When properties are evaluated, this type
|
||||||
|
# is used to remove an entry. Thus if your property evaluation semantic
|
||||||
|
# implies that you have to mute the content of an attribute, then your
|
||||||
|
# property should produce this value.
|
||||||
|
isNotdef = attrs: (typeOf attrs) == "notdef";
|
||||||
|
mkNotdef = {_type = "notdef";};
|
||||||
|
|
||||||
|
# General property type, it has a property attribute and a content
|
||||||
|
# attribute. The property attribute refer to an attribute set which
|
||||||
|
# contains a _type attribute and a list of functions which are used to
|
||||||
|
# evaluate this property. The content attribute is used to stack property
|
||||||
|
# on top of each other.
|
||||||
|
#
|
||||||
|
# The optional functions which may be contained in the property attribute
|
||||||
|
# are:
|
||||||
|
# - onDelay: run on a copied property.
|
||||||
|
# - onGlobalDelay: run on all copied properties.
|
||||||
|
# - onEval: run on an evaluated property.
|
||||||
|
# - onGlobalEval: run on a list of property stack on top of their values.
|
||||||
|
isProperty = attrs: (typeOf attrs) == "property";
|
||||||
|
mkProperty = p@{property, content, ...}: p // {
|
||||||
|
_type = "property";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Go through the stack of properties and apply the function `op' on all
|
||||||
|
# property and call the function `nul' on the final value which is not a
|
||||||
|
# property. The stack is traversed in reversed order. The `op' function
|
||||||
|
# should expect a property with a content which have been modified.
|
||||||
|
#
|
||||||
|
# Warning: The `op' function expects only one argument in order to avoid
|
||||||
|
# calls to mkProperties as the argument is already a valid property which
|
||||||
|
# contains the result of the folding inside the content attribute.
|
||||||
|
foldProperty = op: nul: attrs:
|
||||||
|
if isProperty attrs then
|
||||||
|
op (attrs // {
|
||||||
|
content = foldProperty op nul attrs.content;
|
||||||
|
})
|
||||||
|
else
|
||||||
|
nul attrs;
|
||||||
|
|
||||||
|
# Simple function which can be used as the `op' argument of the
|
||||||
|
# foldProperty function. Properties that you don't want to handle can be
|
||||||
|
# ignored with the `id' function. `isSearched' is a function which should
|
||||||
|
# check the type of a property and return a boolean value. `thenFun' and
|
||||||
|
# `elseFun' are functions which behave as the `op' argument of the
|
||||||
|
# foldProperty function.
|
||||||
|
foldFilter = isSearched: thenFun: elseFun: attrs:
|
||||||
|
if isSearched attrs.property then
|
||||||
|
thenFun attrs
|
||||||
|
else
|
||||||
|
elseFun attrs;
|
||||||
|
|
||||||
|
|
||||||
|
# Move properties from the current attribute set to the attribute
|
||||||
|
# contained in this attribute set. This trigger property handlers called
|
||||||
|
# `onDelay' and `onGlobalDelay'.
|
||||||
|
delayProperties = attrs:
|
||||||
|
let cleanAttrs = rmProperties attrs; in
|
||||||
|
if isProperty attrs then
|
||||||
|
lib.mapAttrs (a: v:
|
||||||
|
lib.addErrorContext "while moving properties on the attribute `${a}'." (
|
||||||
|
triggerPropertiesGlobalDelay a (
|
||||||
|
triggerPropertiesDelay a (
|
||||||
|
copyProperties attrs v
|
||||||
|
)))) cleanAttrs
|
||||||
|
else
|
||||||
|
attrs;
|
||||||
|
|
||||||
|
# Call onDelay functions.
|
||||||
|
triggerPropertiesDelay = name: attrs:
|
||||||
|
let
|
||||||
|
callOnDelay = p@{property, ...}:
|
||||||
|
lib.addErrorContext "while calling a onDelay function." (
|
||||||
|
if property ? onDelay then
|
||||||
|
property.onDelay name p
|
||||||
|
else
|
||||||
|
p
|
||||||
|
);
|
||||||
|
in
|
||||||
|
foldProperty callOnDelay id attrs;
|
||||||
|
|
||||||
|
# Call onGlobalDelay functions.
|
||||||
|
triggerPropertiesGlobalDelay = name: attrs:
|
||||||
|
let
|
||||||
|
globalDelayFuns = uniqListExt {
|
||||||
|
getter = property: property._type;
|
||||||
|
inputList = foldProperty (p@{property, content, ...}:
|
||||||
|
if property ? onGlobalDelay then
|
||||||
|
[ property ] ++ content
|
||||||
|
else
|
||||||
|
content
|
||||||
|
) (a: []) attrs;
|
||||||
|
};
|
||||||
|
|
||||||
|
callOnGlobalDelay = property: content:
|
||||||
|
lib.addErrorContext "while calling a onGlobalDelay function." (
|
||||||
|
property.onGlobalDelay name content
|
||||||
|
);
|
||||||
|
in
|
||||||
|
fold callOnGlobalDelay attrs globalDelayFuns;
|
||||||
|
|
||||||
|
# Expect a list of values which may have properties and return the same
|
||||||
|
# list of values where all properties have been evaluated and where all
|
||||||
|
# ignored values are removed. This trigger property handlers called
|
||||||
|
# `onEval' and `onGlobalEval'.
|
||||||
|
evalProperties = valList:
|
||||||
|
if valList != [] then
|
||||||
|
filter (x: !isNotdef x) (
|
||||||
|
lib.addErrorContext "while evaluating properties an attribute." (
|
||||||
|
triggerPropertiesGlobalEval (
|
||||||
|
map triggerPropertiesEval valList
|
||||||
|
)))
|
||||||
|
else
|
||||||
|
valList;
|
||||||
|
|
||||||
|
# Call onEval function
|
||||||
|
triggerPropertiesEval = val:
|
||||||
|
foldProperty (p@{property, ...}:
|
||||||
|
lib.addErrorContext "while calling a onEval function." (
|
||||||
|
if property ? onEval then
|
||||||
|
property.onEval p
|
||||||
|
else
|
||||||
|
p
|
||||||
|
)
|
||||||
|
) id val;
|
||||||
|
|
||||||
|
# Call onGlobalEval function
|
||||||
|
triggerPropertiesGlobalEval = valList:
|
||||||
|
let
|
||||||
|
globalEvalFuns = uniqListExt {
|
||||||
|
getter = property: property._type;
|
||||||
|
inputList =
|
||||||
|
fold (attrs: list:
|
||||||
|
foldProperty (p@{property, content, ...}:
|
||||||
|
if property ? onGlobalEval then
|
||||||
|
[ property ] ++ content
|
||||||
|
else
|
||||||
|
content
|
||||||
|
) (a: list) attrs
|
||||||
|
) [] valList;
|
||||||
|
};
|
||||||
|
|
||||||
|
callOnGlobalEval = property: valList:
|
||||||
|
lib.addErrorContext "while calling a onGlobalEval function." (
|
||||||
|
property.onGlobalEval valList
|
||||||
|
);
|
||||||
|
in
|
||||||
|
fold callOnGlobalEval valList globalEvalFuns;
|
||||||
|
|
||||||
|
# Remove all properties on top of a value and return the value.
|
||||||
|
rmProperties =
|
||||||
|
foldProperty (p@{content, ...}: content) id;
|
||||||
|
|
||||||
|
# Copy properties defined on a value on another value.
|
||||||
|
copyProperties = attrs: newAttrs:
|
||||||
|
foldProperty id (x: newAttrs) attrs;
|
||||||
|
|
||||||
|
/* If. ThenElse. Always. */
|
||||||
|
|
||||||
|
# create "if" statement that can be delayed on sets until a "then-else" or
|
||||||
|
# "always" set is reached. When an always set is reached the condition
|
||||||
|
# is ignore.
|
||||||
|
|
||||||
|
# Create a "If" property which only contains a condition.
|
||||||
|
isIf = attrs: (typeOf attrs) == "if";
|
||||||
|
mkIf = condition: content: mkProperty {
|
||||||
|
property = {
|
||||||
|
_type = "if";
|
||||||
|
onGlobalDelay = onIfGlobalDelay;
|
||||||
|
onEval = onIfEval;
|
||||||
|
inherit condition;
|
||||||
|
};
|
||||||
|
inherit content;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Create a "ThenElse" property which contains choices which can choosed by
|
||||||
|
# the evaluation of an "If" statement.
|
||||||
|
isThenElse = attrs: (typeOf attrs) == "then-else";
|
||||||
|
mkThenElse = attrs:
|
||||||
|
assert attrs ? thenPart && attrs ? elsePart;
|
||||||
|
mkProperty {
|
||||||
|
property = {
|
||||||
|
_type = "then-else";
|
||||||
|
onEval = val: throw "Missing mkIf statement.";
|
||||||
|
inherit (attrs) thenPart elsePart;
|
||||||
|
};
|
||||||
|
content = mkNotdef;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Create an "Always" property remove ignore all "If" statement.
|
||||||
|
isAlways = attrs: (typeOf attrs) == "always";
|
||||||
|
mkAlways = value:
|
||||||
|
mkProperty {
|
||||||
|
property = {
|
||||||
|
_type = "always";
|
||||||
|
onEval = p@{content, ...}: content;
|
||||||
|
inherit value;
|
||||||
|
};
|
||||||
|
content = mkNotdef;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Remove all "If" statement defined on a value.
|
||||||
|
rmIf = foldProperty (
|
||||||
|
foldFilter isIf
|
||||||
|
({content, ...}: content)
|
||||||
|
id
|
||||||
|
) id;
|
||||||
|
|
||||||
|
# Evaluate the "If" statements when either "ThenElse" or "Always"
|
||||||
|
# statement is encounter. Otherwise it remove multiple If statement and
|
||||||
|
# replace them by one "If" staement where the condition is the list of all
|
||||||
|
# conditions joined with a "and" operation.
|
||||||
|
onIfGlobalDelay = name: content:
|
||||||
|
let
|
||||||
|
# extract if statements and non-if statements and repectively put them
|
||||||
|
# in the attribute list and attrs.
|
||||||
|
ifProps =
|
||||||
|
foldProperty
|
||||||
|
(foldFilter (p: isIf p || isThenElse p || isAlways p)
|
||||||
|
# then, push the codition inside the list list
|
||||||
|
(p@{property, content, ...}:
|
||||||
|
{ inherit (content) attrs;
|
||||||
|
list = [property] ++ content.list;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# otherwise, add the propertie.
|
||||||
|
(p@{property, content, ...}:
|
||||||
|
{ inherit (content) list;
|
||||||
|
attrs = p // { content = content.attrs; };
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(attrs: { list = []; inherit attrs; })
|
||||||
|
content;
|
||||||
|
|
||||||
|
# compute the list of if statements.
|
||||||
|
evalIf = content: condition: list:
|
||||||
|
if list == [] then
|
||||||
|
mkIf condition content
|
||||||
|
else
|
||||||
|
let p = head list; in
|
||||||
|
|
||||||
|
# evaluate the condition.
|
||||||
|
if isThenElse p then
|
||||||
|
if condition then
|
||||||
|
copyProperties content p.thenPart
|
||||||
|
else
|
||||||
|
copyProperties content p.elsePart
|
||||||
|
# ignore the condition.
|
||||||
|
else if isAlways p then
|
||||||
|
copyProperties content p.value
|
||||||
|
# otherwise (isIf)
|
||||||
|
else
|
||||||
|
evalIf content (condition && p.condition) (tail list);
|
||||||
|
in
|
||||||
|
evalIf ifProps.attrs true ifProps.list;
|
||||||
|
|
||||||
|
# Evaluate the condition of the "If" statement to either get the value or
|
||||||
|
# to ignore the value.
|
||||||
|
onIfEval = p@{property, content, ...}:
|
||||||
|
if property.condition then
|
||||||
|
content
|
||||||
|
else
|
||||||
|
mkNotdef;
|
||||||
|
|
||||||
|
/* mkOverride */
|
||||||
|
|
||||||
|
# Create an "Override" statement which allow the user to define
|
||||||
|
# prioprities between values. The default priority is 100 and the lowest
|
||||||
|
# priorities are kept. The template argument must reproduce the same
|
||||||
|
# attribute set hierachy to override leaves of the hierarchy.
|
||||||
|
isOverride = attrs: (typeOf attrs) == "override";
|
||||||
|
mkOverride = priority: template: content: mkProperty {
|
||||||
|
property = {
|
||||||
|
_type = "override";
|
||||||
|
onDelay = onOverrideDelay;
|
||||||
|
onGlobalEval = onOverrideGlobalEval;
|
||||||
|
inherit priority template;
|
||||||
|
};
|
||||||
|
inherit content;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Sugar to override the default value of the option by making a new
|
||||||
|
# default value based on the configuration.
|
||||||
|
mkDefaultValue = content: mkOverride 1000 {} content;
|
||||||
|
|
||||||
|
# Make the template traversal in function of the property traversal. If
|
||||||
|
# the template define a non-empty attribute set, then the property is
|
||||||
|
# copied only on all mentionned attributes inside the template.
|
||||||
|
# Otherwise, the property is kept on all sub-attribute definitions.
|
||||||
|
onOverrideDelay = name: p@{property, content, ...}:
|
||||||
|
let inherit (property) template; in
|
||||||
|
if isAttrs template && template != {} then
|
||||||
|
if hasAttr name template then
|
||||||
|
p // {
|
||||||
|
property = p.property // {
|
||||||
|
template = builtins.getAttr name template;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
# Do not override the attribute \name\
|
||||||
|
else
|
||||||
|
content
|
||||||
|
# Override values defined inside the attribute \name\.
|
||||||
|
else
|
||||||
|
p;
|
||||||
|
|
||||||
|
# Ignore all values which have a higher value of the priority number.
|
||||||
|
onOverrideGlobalEval = valList:
|
||||||
|
let
|
||||||
|
defaultPrio = 100;
|
||||||
|
|
||||||
|
inherit (builtins) lessThan;
|
||||||
|
|
||||||
|
getPrioVal =
|
||||||
|
foldProperty
|
||||||
|
(foldFilter isOverride
|
||||||
|
(p@{property, content, ...}:
|
||||||
|
if content ? priority && lessThan content.priority property.priority then
|
||||||
|
content
|
||||||
|
else
|
||||||
|
content // {
|
||||||
|
inherit (property) priority;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
(p@{property, content, ...}:
|
||||||
|
content // {
|
||||||
|
value = p // { content = content.value; };
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) (value: { inherit value; });
|
||||||
|
|
||||||
|
addDefaultPrio = x:
|
||||||
|
if x ? priority then x
|
||||||
|
else x // { priority = defaultPrio; };
|
||||||
|
|
||||||
|
prioValList = map (x: addDefaultPrio (getPrioVal x)) valList;
|
||||||
|
|
||||||
|
higherPrio =
|
||||||
|
if prioValList == [] then
|
||||||
|
defaultPrio
|
||||||
|
else
|
||||||
|
fold (x: min:
|
||||||
|
if lessThan x.priority min then
|
||||||
|
x.priority
|
||||||
|
else
|
||||||
|
min
|
||||||
|
) (head prioValList).priority (tail prioValList);
|
||||||
|
in
|
||||||
|
map (x:
|
||||||
|
if x.priority == higherPrio then
|
||||||
|
x.value
|
||||||
|
else
|
||||||
|
mkNotdef
|
||||||
|
) prioValList;
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user