{ lib }:
rec {
  /**
    Create a URL-safe slug from any string
  */
  slug = str:
    let
      # Replace non-alphanumeric characters with hyphens
      replaced = join ""
        (
          builtins.map
            (c:
              if (c >= "a" && c <= "z") || (c >= "0" && c <= "9")
              then c
              else "-"
            )
            (with lib; stringToCharacters (toLower str)));

      # Remove leading and trailing hyphens
      trimHyphens = s:
        let
          matched = builtins.match "(-*)([^-].*[^-]|[^-])(-*)" s;
        in
        with lib; optionalString (!isNull matched) (builtins.elemAt matched 1);

      collapseHyphens = s:
        let
          result = builtins.replaceStrings [ "--" ] [ "-" ] s;
        in
        if result == s then s else collapseHyphens result;
    in
    trimHyphens (collapseHyphens replaced);

  join = lib.concatStringsSep;

  splitLines = s: with builtins; filter (x: !isList x) (split "\n" s);

  indent = prefix: s:
    with lib.lists;
    let
      lines = splitLines s;
    in
    join "\n" (
      [ (head lines) ]
      ++
      (map (x: if x == "" then x else "${prefix}${x}") (tail lines))
    );

  relativePath = path1': path2':
    let
      inherit (lib.path) subpath;
      inherit (lib) lists;

      path1 = subpath.components path1';
      path2 = subpath.components path2';

      commonPrefixLength = with lists;
        findFirstIndex (i: i.fst != i.snd)
          { fst = null; snd = null; }
          (zipLists path1 path2);

      relativeComponents = with lists;
        [ "." ] ++ (replicate (length path1 - commonPrefixLength - 1) "..") ++
        (drop commonPrefixLength path2);
    in
    join "/" relativeComponents;

  /**
    Recursively list all Nix files from a directory, except the top-level `default.nix`

    Useful for module system `imports` from a top-level module.
  **/
  nixFiles = dir: with lib.fileset;
    toList (difference
      (fileFilter ({ hasExt, ... }: hasExt "nix") dir)
      (dir + "/default.nix")
    );

  types = rec {
    collection = elemType:
      let
        unparenthesize = class: class == "noun";
        desc = type:
          types.optionDescriptionPhrase unparenthesize type;
        desc' = type:
          let
            typeDesc = lib.types.optionDescriptionPhrase unparenthesize type;
          in
          if type.descriptionClass == "noun"
          then
            typeDesc + "s"
          else
            "many instances of ${typeDesc}";
      in
      lib.types.mkOptionType {
        name = "collection";
        description = "separately specified ${desc elemType} for a collection of ${desc' elemType}";
        merge = loc: defs:
          map
            (def:
              let
                merged = lib.mergeDefinitions
                  (loc ++ [ "[definition ${toString def.file}]" ])
                  elemType
                  [{ inherit (def) file; value = def.value; }];
              in
              if merged ? mergedValue then merged.mergedValue else merged.value
            )
            defs;
        check = elemType.check;
        getSubOptions = elemType.getSubOptions;
        getSubModules = elemType.getSubModules;
        substSubModules = m: collection (elemType.substSubModules m);
        functor = (lib.defaultFunctor "collection") // {
          type = collection;
          wrapped = elemType;
          payload = { };
        };
        nestedTypes.elemType = elemType;
      };
  };
}