summaryrefslogtreecommitdiffstats
path: root/by-name-overlay.nix
diff options
context:
space:
mode:
Diffstat (limited to 'by-name-overlay.nix')
-rw-r--r--by-name-overlay.nix165
1 files changed, 119 insertions, 46 deletions
diff --git a/by-name-overlay.nix b/by-name-overlay.nix
index 4d700a8..2d03e75 100644
--- a/by-name-overlay.nix
+++ b/by-name-overlay.nix
@@ -1,25 +1,33 @@
+# nixLib - A library of nix functions for vhack.eu
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+# This file is part of vhack.eu's nix library.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
{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,
-}: 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;
+ fileName ? null,
+ fileRegex ? null,
+ finalizeFunction ? name: value: value,
+ useShards ? true,
+ relativePaths ? false,
+}:
+assert fileName == null -> fileRegex != null;
+assert fileRegex == null -> fileName != null; let
+ finalFileRegex =
+ if fileRegex == null
+ then "^${escapeRegex fileName}$"
+ else fileRegex;
+ fileDisplay =
+ if fileName == null
+ then "matching ${fileRegex}"
+ else fileName;
# From nixpkgs/lib {{{
# These functions are taken straight out of the `nixpkgs/lib`.
@@ -28,6 +36,12 @@
mapAttrsToList = f: attrs:
builtins.map (name: f name attrs.${name}) (builtins.attrNames attrs);
+ stringToCharacters = s: builtins.genList (p: builtins.substring p 1 s) (builtins.stringLength s);
+ escape = list: builtins.replaceStrings list (map (c: "\\${c}") list);
+ escapeRegex = escape (stringToCharacters "\\[{()^$?*+|.");
+
+ mapAttrs' = f: set: builtins.listToAttrs (map (attr: f attr set.${attr}) (builtins.attrNames set));
+
nameValuePair = name: value: {inherit name value;};
filterAttrs = pred: set:
builtins.listToAttrs (builtins.concatMap (name: let
@@ -38,39 +52,98 @@
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 let
- mkPath = name: _type: let
- path = baseDirectory + "/${shard}/${name}" + "/${fileName}";
- coImportPath =
- if coImportsNameFunction != null
- then
- coImportsNameFunction
- {inherit shard name;}
- else path;
+ rawByName = {
+ baseDirectory,
+ finalFileRegex,
+ fileDisplay,
+ finalizeFunction,
+ useShards,
+ relativePaths,
+ }: 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;
+
+ # 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 (${fileDisplay}): '${shard}'" {}
+ else if useShards
+ then namesForElementShard shard type
+ else namesForElementDirect shard type;
+
+ # Type: String -> String -> String -> ListOf Path
+ mkPath = shard: name: toplevelType: let
+ rawPath = baseDirectory + "/${shard}/${name}";
+ paths = filterAttrs (_: v: v != null) (mapAttrs' (name: value:
+ if builtins.match finalFileRegex name != null
+ then nameValuePair name value
+ else nameValuePair name null) (builtins.readDir rawPath));
+
+ checkPath = pathSegment: type: let
+ path = rawPath + "/${pathSegment}";
in
if builtins.pathExists path
then
- if builtins.pathExists coImportPath
- then path
- else warn "'${builtins.toString coImportPath}' does not exist. Should include ${coImportsWarnMessageObject} for '${shard}/${name}'" path
+ if toplevelType != "directory"
+ then
+ # The `namesForShard` function should have already printed a warning.
+ [null]
+ else if relativePaths
+ then pathSegment
+ else path
else warn "'${builtins.toString path}' does not exist. Skipped" null;
in
- filterAttrs (name: value: value != null)
- (builtins.mapAttrs
- mkPath
- (builtins.readDir (baseDirectory + "/${shard}")));
+ if toplevelType != "directory"
+ then
+ # The `namesForShard` function should have already printed a warning.
+ [null]
+ else if (builtins.attrValues paths) == []
+ then warn "'${fileDisplay}' did not match anything in ${builtins.toString rawPath}. Skipped" [null]
+ else mapAttrsToList checkPath paths;
+
+ filterNull = list: builtins.filter (value: value != null) list;
+ # Type: String -> String -> AttrSet
+ namesForElementShard = shard: _type: (builtins.mapAttrs
+ (name: type: filterNull (mkPath shard name type))
+ (builtins.readDir (baseDirectory + "/${shard}")));
+
+ # Type: String -> String -> AttrSet
+ namesForElementDirect = name: type: {"${name}" = filterNull ((mkPath ".") name type);};
- # 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;
+ # A list of all paths.
+ output = flattenAttrs (mapAttrsToList namesForShard (builtins.readDir baseDirectory));
+ in
+ output;
+
+ firstPass = rawByName {
+ inherit
+ baseDirectory
+ finalFileRegex
+ fileDisplay
+ finalizeFunction
+ useShards
+ relativePaths
+ ;
+ };
+
+ secondPass =
+ if fileName != null
+ then
+ builtins.mapAttrs (name: value: builtins.head value)
+ (filterAttrs (_: value: value != []) firstPass)
+ else firstPass;
in
- output
+ builtins.mapAttrs
+ finalizeFunction
+ secondPass