about summary refs log tree commit diff
path: root/nix/writeTree/default.nix
{ depot, lib, pkgs, ... }:
let
  inherit (lib) fix pipe mapAttrsToList isAttrs concatLines isString isDerivation isPath;

  # TODO(sterni): move to //nix/utils with clearer naming and alternative similar to lib.types.path
  isPathLike = value:
    isPath value
    || isDerivation value
    || (isString value && builtins.hasContext value);

  esc = s: lib.escapeShellArg /* ensure paths import into store */ "${s}";

  writeTreeAtPath = path: tree:
    ''
      mkdir -p "$out/"${esc path}
    ''
    + pipe tree [
      (mapAttrsToList (k: v:
        if isPathLike v then
          "cp -R --reflink=auto ${v} \"$out/\"${esc path}/${esc k}"
        else if lib.isAttrs v then
          writeTreeAtPath (path + "/" + k) v
        else
          throw "invalid type (expected path, derivation, string with context, or attrs)"))
      concatLines
    ];

  /* Create a directory tree specified by a Nix attribute set structure.

     Each value in `tree` should either be a file, a directory, or another tree
     attribute set. Those paths will be written to a directory tree
     corresponding to the structure of the attribute set.

     Type: string -> attrSet -> derivation
  */
  writeTree = name: tree:
    pkgs.runCommandLocal name { } (writeTreeAtPath "" tree);
in

# __functor trick so readTree can add the tests attribute
{
  __functor = _: writeTree;
}