summary refs log tree commit diff stats
path: root/by-name-overlay.nix
blob: 80aebf4de8661790ab8f18350dd06f687e771aa8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
{warn}:
# Adapted from this: https://github.com/NixOS/nixpkgs/blob/1814b56453c91192f6d5a6276079948f9fe96c18/pkgs/top-level/by-name-overlay.nix
# This file should not depend on `pkgs` and thus not use `lib`.
{
  baseDirectory,
  fileName,
  finalizeFunction,
  coImportsNameFunction ? null,
  coImportsWarnMessageObject ? null,
  useShards ? true,
}: let
  # Takes a list of attrs as input and returns one merged attr set.
  flattenAttrs = list:
    if builtins.isList list
    then
      builtins.foldl' (acc: elem:
        if builtins.isList elem
        # Merging them with `//` is okay here, as we can be sure that the attr names are
        # unique (they were separate dictionary after all)
        then acc // (flattenAttrs elem)
        else acc // elem) {}
      list
    else list;

  # From nixpkgs/lib {{{
  # These functions are taken straight out of the `nixpkgs/lib`.
  # We can't depended on `pkgs` (and thus on `lib`), because the `pkgs` module argument
  # is only defined in the `nixpkgs` module (which is imported through this function).
  mapAttrsToList = f: attrs:
    builtins.map (name: f name attrs.${name}) (builtins.attrNames attrs);

  nameValuePair = name: value: {inherit name value;};
  filterAttrs = pred: set:
    builtins.listToAttrs (builtins.concatMap (name: let
      v = set.${name};
    in
      if pred name v
      then [(nameValuePair name v)]
      else []) (builtins.attrNames set));
  # }}}

  # Module files for a single shard
  # Type: String -> String -> ListOf Path
  namesForShard = shard: type:
    if type != "directory"
    then warn "Ignored non-directory, whilst importing by-name directory (${fileName}): '${shard}'" {}
    else if useShards
    then namesForElement shard type
    else namesForElement "." type;

  namesForElement = shard: _type: let
    mkPath = name: type: let
      path = baseDirectory + "/${shard}/${name}" + "/${fileName}";
      coImportPath =
        if coImportsNameFunction != null
        then
          coImportsNameFunction
          {inherit shard name;}
        else path;
    in
      if builtins.pathExists path
      then
        if type != "directory"
        then
          # The `namesForShard` function should have already printed a warning.
          null
        else if builtins.pathExists coImportPath
        then path
        else warn "'${builtins.toString coImportPath}' does not exist. Should include ${coImportsWarnMessageObject} for '${shard}/${name}'" path
      else warn "'${builtins.toString path}' does not exist. Skipped" null;
  in
    filterAttrs (name: value: value != null)
    (builtins.mapAttrs
      mkPath
      (builtins.readDir (baseDirectory + "/${shard}")));

  # A list of all module paths.
  # These can the be simply injected into `import`
  files = flattenAttrs (mapAttrsToList namesForShard (builtins.readDir baseDirectory));
  output =
    builtins.mapAttrs
    finalizeFunction
    files;
in
  output