# Library for creating script modules { lib, pkgs, }: let shellArgs = { zsh = name: file: "--zsh --name _${name} ${file}"; bash = name: file: "--bash --name ${name}.bash ${file}"; }; mkScript = name: path: description: env: deps: completion: let descLines = lib.filter (s: s != "") (lib.splitString "\n" description); descComment = if descLines == [ ] then "" else lib.concatMapStringsSep "\n" (line: "# ${line}") descLines + "\n"; envVars = lib.concatStringsSep "\n" ( lib.mapAttrsToList (n: v: "export ${n}=${lib.escapeShellArg (toString v)}") env ); base = pkgs.writeShellApplication { inherit name; runtimeInputs = deps; text = '' ${descComment}${envVars} ${builtins.readFile path} ''; }; completionDrv = pkgs.runCommand "${name}-completion" { nativeBuildInputs = [ pkgs.installShellFiles ]; } ( "mkdir -p $out\n" + lib.concatStringsSep "\n" ( lib.mapAttrsToList (shell: content: '' installShellCompletion ${shellArgs.${shell} name (pkgs.writeText "_${name}.${shell}" content)} '') completion ) ); in if completion == { } then base else pkgs.symlinkJoin { name = name; paths = [ base completionDrv ]; meta = base.meta or { }; }; mkScriptModule = { scope ? [ ], name, path, env ? { }, deps ? [ ], desktop ? null, extraLinks ? [ ], config, description ? "", completion ? { }, }: let scriptAttrs = scope ++ [ name ]; dottedName = lib.concatStringsSep "." scriptAttrs; c = lib.getAttrFromPath scriptAttrs config; scriptDrv = mkScript name path description env deps completion; in { options = lib.setAttrByPath scriptAttrs { enable = lib.mkOption { type = lib.types.bool; default = true; description = "Whether to enable the ${name} script."; }; package = lib.mkOption { type = lib.types.nullOr lib.types.package; readOnly = true; description = "The derivation for the ${name} script."; }; completion.enable = lib.mkOption { type = lib.types.bool; default = completion != { }; description = "Whether to install completions for the ${name} script."; }; desktop = { enable = lib.mkOption { type = lib.types.bool; default = false; description = "Whether to create a .desktop entry for this script."; }; displayName = lib.mkOption { type = lib.types.str; default = name; description = "Display name for the .desktop entry."; }; icon = lib.mkOption { type = lib.types.str; default = "kitty"; description = "Icon for the .desktop entry."; }; }; }; config = lib.setAttrByPath scriptAttrs ( lib.mkIf c.enable ( { package = scriptDrv; } // lib.optionalAttrs (desktop != null) { inherit desktop; } ) ) // { home.file = lib.mkIf c.enable ( builtins.listToAttrs ( map (linkPath: { name = lib.removePrefix "~/" linkPath; value = { source = "${scriptDrv}/bin/${name}"; }; }) extraLinks ) ); assertions = [ { assertion = !c.enable || !c.desktop.enable || config.terminal.kitty.enable; message = "${dottedName}.desktop.enable requires terminal.kitty.enable because desktop entries launch scripts in kitty."; } { assertion = !c.completion.enable || completion != { }; message = "${dottedName}.completion.enable is true but no completion text was provided to mkScriptModule."; } ]; scriptPaths = [ scriptAttrs ]; }; }; in { inherit mkScript mkScriptModule; }