query
On this page

attrListWith

lib.types.attrListWith

Docs pulled from | This Revision | 9 minutes ago


Contribute
Enhance the ecosystem with your expertise! Contribute to fill the gaps in documentation. Your input can make a difference.

Noogle detected

Implementation

The following is the current implementation of this function.

{
      elemType,
      asAttrs ? false,
      mergeAttrValues ? _name: values: values,
    }:
    mkOptionType rec {
      name = "attrListOf";
      description = "attribute list of ${
        optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType
      }";
      descriptionClass = "composite";
      check = {
        __functor = _self: x: isList x || isAttrs x;
        isV2MergeCoherent = true;
      };
      merge = {
        __functor =
          self: loc: defs:
          (self.v2 { inherit loc defs; }).value;
        v2 =
          { loc, defs }:
          let
            # Peel order and override properties from a value in any nesting order.
            # Returns { value, prio, overridePrio }.
            # mkOrder is stripped (we consume it for sorting).
            # mkOverride is preserved in value (mergeDefinitions strips it).
            peelProperties =
              value:
              let
                type = value._type or null;
              in
              if type == "order" then
                let
                  inner = peelProperties value.content;
                in
                {
                  inherit (inner) value overridePrio;
                  prio = value.priority;
                }
              else if type == "override" then
                let
                  inner = peelProperties value.content;
                in
                {
                  inherit (inner) prio;
                  overridePrio = value.priority;
                  # Re-wrap mkOverride around the inner value (with mkOrder stripped)
                  value = mkOverride value.priority inner.value;
                }
              else
                {
                  inherit value;
                  prio = defaultOrderPriority;
                  overridePrio = defaultOverridePriority;
                };

            # Extract { file, key, value, prio, overridePrio } from a single-key attrset,
            # optionally wrapped in mkOrder at the element level (list format).
            extractItem =
              file: raw:
              let
                hasOrder = isType "order" raw;
                item = if hasOrder then raw.content else raw;
                key = head (attrNames item);
                peeled = peelProperties item.${key};
              in
              if isAttrs item && length (attrNames item) == 1 then
                peeled
                // {
                  inherit file key;
                  prio = if hasOrder then raw.priority else peeled.prio;
                }
              else
                throw "A definition for option `${showOption loc}' is not of type `${description}'. ${
                  if !isAttrs item then
                    "Each list element must be an attribute set, but got ${builtins.typeOf item}"
                  else
                    "Each list element must be a single-key attribute set, but got ${toString (length (attrNames item))} keys"
                }.${
                  showDefs [
                    {
                      inherit file;
                      value = raw;
                    }
                  ]
                }";

            # Convert a definition to a flat list of { file, key, value, prio, overridePrio }
            defToItems =
              def:
              if isList def.value then
                map (extractItem def.file) def.value
              else
                # isAttrs: properties are on the values directly
                map (
                  key:
                  peelProperties def.value.${key}
                  // {
                    inherit (def) file;
                    inherit key;
                  }
                ) (attrNames def.value);

            allItems = concatMap defToItems defs;

            # Per key, find the highest override priority (lowest number)
            winningOverridePrio = foldl' (
              acc: item:
              let
                prev = acc.${item.key} or defaultOverridePriority;
              in
              if item.overridePrio < prev then
                acc // { ${item.key} = item.overridePrio; }
              else
                # minimize `//` operations
                acc
            ) { } allItems;

            # Keep only items at the winning override priority for their key
            items = sort (a: b: a.prio < b.prio) (
              filter (
                item: item.overridePrio == winningOverridePrio.${item.key} or defaultOverridePriority
              ) allItems
            );

            evals = filter (e: e.eval.optionalValue ? value) (
              map (item: {
                inherit (item) key file prio;
                eval = mergeDefinitions (loc ++ [ item.key ]) elemType [
                  {
                    inherit (item) file value;
                  }
                ];
              }) items
            );

            attrListValue = map (e: { ${e.key} = e.eval.optionalValue.value or e.eval.mergedValue; }) evals;
          in
          {
            headError = checkDefsForError check loc defs;
            value = if asAttrs then zipAttrsWith mergeAttrValues attrListValue else attrListValue;
            valueMeta.attrList = map (e: e.eval.checkedAndMerged.valueMeta) evals;
            /**
              The ordered list representation, especially useful when asAttrs is set.
            */
            valueMeta.attrListValue = attrListValue;
            valueMeta.definitions = map (
              e:
              mkDefinition {
                inherit (e) file;
                value = mkOrder e.prio { ${e.key} = e.eval.optionalValue.value or e.eval.mergedValue; };
              }
            ) evals;
          };
      };
      emptyValue = {
        value = if asAttrs then { } else [ ];
      };
      getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "*" ]);
      getSubModules = elemType.getSubModules;
      substSubModules =
        m:
        attrListWith {
          inherit asAttrs mergeAttrValues;
          elemType = elemType.substSubModules m;
        };
      typeMerge = t: null; # Disable type merging
      nestedTypes.elemType = elemType;
    }