From 3516efa6f2b78a494ba776ee4a832f9d4002ff72 Mon Sep 17 00:00:00 2001
From: Gabriel Arazas <foodogsquared@foodogsquared.one>
Date: Tue, 17 Sep 2024 18:18:59 +0800
Subject: [PATCH] wrapper-manager-fds/modules: init files option

---
 modules/wrapper-manager/default.nix           |  1 +
 modules/wrapper-manager/files.nix             | 98 +++++++++++++++++++
 tests/configs/default.nix                     |  1 +
 .../neofetch-with-additional-files.nix        | 55 +++++++++++
 4 files changed, 155 insertions(+)
 create mode 100644 modules/wrapper-manager/files.nix
 create mode 100644 tests/configs/neofetch-with-additional-files.nix

diff --git a/modules/wrapper-manager/default.nix b/modules/wrapper-manager/default.nix
index 3df876a..b462b82 100644
--- a/modules/wrapper-manager/default.nix
+++ b/modules/wrapper-manager/default.nix
@@ -1,6 +1,7 @@
 {
   imports = [
     ./base.nix
+    ./files.nix
     ./xdg-desktop-entries.nix
     ./xdg-dirs.nix
     ./locale.nix
diff --git a/modules/wrapper-manager/files.nix b/modules/wrapper-manager/files.nix
new file mode 100644
index 0000000..f2beb15
--- /dev/null
+++ b/modules/wrapper-manager/files.nix
@@ -0,0 +1,98 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.files;
+
+  filesModule = { name, lib, config, options, ... }: {
+    options = {
+      target = lib.mkOption {
+        type = lib.types.nonEmptyStr;
+        description = ''
+          Path of the file relative to the derivation output path.
+        '';
+        default = name;
+        example = "share/applications/org.example.App1.desktop";
+      };
+
+      source = lib.mkOption {
+        type = lib.types.path;
+        description = "Path of the file to be linked.";
+      };
+
+      text = lib.mkOption {
+        type = with lib.types; nullOr lines;
+        description = ''
+          Text content of the given filesystem path.
+        '';
+        default = null;
+        example = ''
+          key=value
+          hello=world
+        '';
+      };
+
+      mode = lib.mkOption {
+        type = lib.types.strMatching "[0-7]{0,4}";
+        default = "0644";
+        example = "0600";
+        description = ''
+          Permissions to be given to the file. By default, it is given with a
+          symlink.
+        '';
+      };
+    };
+
+    config = {
+      source = lib.mkIf (config.text != null) (
+        let
+          name' = "wrapper-manager-filesystem-${lib.replaceStrings ["/"] ["-"] name}";
+        in lib.modules.mkDerivedConfig options.text (pkgs.writeText name')
+      );
+    };
+  };
+in
+{
+  options.files = lib.mkOption {
+    type = with lib.types; attrsOf (submodule filesModule);
+    description = ''
+      A set of files to be exported within the derivation.
+    '';
+    default = { };
+    example = lib.literalExpression ''
+      {
+        "share/example-app/docs".source = ./docs;
+        "etc/xdg".source = ./config;
+
+        "share/example-app/example-config".text = ''''''
+          hello=world
+          location=your-home
+        '''''';
+      }
+    '';
+  };
+
+  config = lib.mkIf (cfg != { }) {
+    build.extraSetup = let
+      installFiles = acc: n: v: let
+        source = lib.escapeShellArg v.source;
+        target = lib.escapeShellArg v.target;
+        target' = "$out/${target}";
+        installFile = let
+          type = lib.filesystem.pathType v.source;
+          in
+          if type == "directory" then ''
+            mkdir -p $(basename ${target'}) && cp --recursive ${source} ${target'}
+          '' else if type == "symlink" then ''
+            ln --symbolic --force ${source} ${target'}
+          '' else ''
+            install -D --mode=${v.mode} ${source} ${target'}
+          '';
+      in ''
+        ${acc}
+        ${installFile}
+      '';
+    in lib.mkBefore ''
+      ${lib.foldlAttrs installFiles "" cfg}
+    '';
+  };
+}
diff --git a/tests/configs/default.nix b/tests/configs/default.nix
index 8877bb1..951c416 100644
--- a/tests/configs/default.nix
+++ b/tests/configs/default.nix
@@ -13,4 +13,5 @@ in
   fastfetch = build [ ./wrapper-fastfetch.nix ];
   neofetch = build [ ./wrapper-neofetch.nix ];
   single-basepackage = build [ ./single-basepackage.nix ];
+  neofetch-with-additional-files = build [ ./neofetch-with-additional-files.nix ];
 }
diff --git a/tests/configs/neofetch-with-additional-files.nix b/tests/configs/neofetch-with-additional-files.nix
new file mode 100644
index 0000000..4186ddc
--- /dev/null
+++ b/tests/configs/neofetch-with-additional-files.nix
@@ -0,0 +1,55 @@
+{ config, lib, pkgs, ... }:
+
+{
+  wrappers.neofetch = {
+    arg0 = lib.getExe' pkgs.neofetch "neofetch";
+    appendArgs = [
+      "--ascii_distro"
+      "guix"
+      "--title_fqdn"
+      "off"
+      "--os_arch"
+      "off"
+    ];
+  };
+
+  # Testing out a simple file.
+  files."share/nix/hello".text = ''
+    WHOA THERE!
+  '';
+
+  # A file target with an "absolute" path.
+  files."/absolute/path".text = ''
+    WHAAAAAAAT!
+  '';
+
+  # Testing out source.
+  files."share/nix/aloha".source = config.files."share/nix/hello".source;
+
+  # Testing out an executable file.
+  files."share/nix/example" = {
+    text = "WHOA";
+    mode = "0755";
+  };
+
+  # Testing out a directory.
+  files."share/whoa".source = pkgs.writeTextDir "/what/is/this.txt" ''
+    WHAT
+  '';
+
+  build.extraPassthru.tests = {
+    actuallyBuilt =
+      let
+        wrapper = config.build.toplevel;
+      in
+      pkgs.runCommand "wrapper-manager-neofetch-actually-built" { } ''
+        [ -x "${wrapper}/bin/${config.wrappers.neofetch.executableName}" ] \
+        && [ -f "${wrapper}/share/nix/hello" ] \
+        && [ -f "${wrapper}/share/nix/aloha" ] \
+        && [ -x "${wrapper}/share/nix/example" ] \
+        && [ -d "${wrapper}/share/whoa" ] \
+        && [ -f "${wrapper}/absolute/path" ] \
+        && touch $out
+      '';
+  };
+}