ides/lib/build.nix
2025-02-07 15:12:17 +11:00

189 lines
5.7 KiB
Nix

{
pkgs,
config,
...
}:
{
config =
let
# control flow monstrosity
branchOnConfig =
cfg:
{
text,
file,
content,
contentFmt,
}:
if (cfg.text != "") then
text
else if (cfg.file != null) then
file
else if (cfg.content != { }) then
if (cfg.format != null) then
content
else if (cfg.formatter != null) then
contentFmt
else
throw "`format` or `formatter` must be set for `content` value ${cfg.content}!"
else
"";
in
{
# validate and complete the service configurations
_buildIdes.finalServices = builtins.mapAttrs (
name:
{
pkg,
args ? "",
exec ? "",
config,
path,
socket,
timer,
}:
let
# make our best effort to use the correct binary
bin = if (exec == "") then pkgs.lib.getExe pkg else pkgs.lib.getExe' pkg exec;
# set file extension
ext =
if (config.ext != "") || (config.format != null) then "." + (config.ext or config.format) else "";
# config hash for unique service names
cfgHash =
let
# method to hash a set
hashContent = builtins.hashString "sha256" (builtins.toJSON config.content);
in
branchOnConfig config {
text = builtins.hashString "sha256" config.text;
file = builtins.hashFile "sha256" config.file;
content = hashContent;
contentFmt = hashContent;
};
confFile =
let
writers = {
java = pkgs.formats.javaProperties { };
json = pkgs.formats.json { };
yaml = pkgs.formats.yaml { };
ini = pkgs.formats.ini { };
toml = pkgs.formats.toml { };
xml = pkgs.formats.xml { };
php = pkgs.formats.php { finalVariable = null; };
};
# final config name
confPath = "config-${name}-${cfgHash}${ext}";
in
# write out config
branchOnConfig config {
text = pkgs.writeText confPath config.text;
inherit (config) file;
content = writers.${config.format}.generate confPath config.content;
contentFmt = config.formatter confPath config.content;
};
# template the config path into the launch command
cfgArgs = builtins.replaceStrings [ "%CFG%" ] [ "${confFile}" ] args;
# flatten unit options into cli args
sdArgs =
let
inherit (pkgs.lib) foldlAttrs;
inherit (builtins) concatStringsSep;
convertToArgList =
prefix: name: values:
(map (inner: "${prefix} ${name}=${inner}") values);
writeArgListFor =
attrs: prefix:
if (attrs != { }) then
concatStringsSep " " (
foldlAttrs (
acc: n: v:
acc + (convertToArgList prefix n v) + " "
) "" attrs
)
else
"";
in
concatStringsSep " " [
(writeArgListFor socket "--socket-property")
(writeArgListFor path "--path-property")
(writeArgListFor timer "--timer-property")
];
in
# transform into attrs that mkWorks expects to receive
{
inherit
bin
sdArgs
cfgArgs
;
unitName = "shell-${name}-${cfgHash}";
}
) config.serviceDefs;
# generate service scripts and create the shell
_buildIdes.shell =
let
# create commands to run and clean up services
mkWorks =
name:
{
unitName,
bin,
cfgArgs,
sdArgs,
}:
{
runner = pkgs.writeShellScriptBin "run" ''
echo "[ides]: starting ${name}.."
systemd-run --user -G -u ${unitName} ${sdArgs} ${bin} ${cfgArgs}
'';
cleaner = pkgs.writeShellScriptBin "clean" ''
echo "[ides]: stopping ${name}.."
systemctl --user stop ${unitName}
'';
};
works = pkgs.lib.mapAttrs (
name: serviceConf: mkWorks name serviceConf
) config._buildIdes.finalServices;
# create the ides cli
cli = import ./cli.nix {
inherit (pkgs) writeShellScriptBin;
inherit (pkgs.lib) foldlAttrs;
inherit works;
};
# create the ides shell
final =
let
inherit (config._buildIdes) shellArgs;
in
shellArgs
// {
nativeBuildInputs = (shellArgs.nativeBuildInputs or [ ]) ++ [
cli
];
shellHook =
let
autoRun =
if config.auto then
''
ides run
''
else
"";
in
(shellArgs.shellHook or "")
+ ''
printf '[ides]: use "ides [action] [target]" to control services. type "ides help" to find out more.\n'
''
+ autoRun;
};
in
config._buildIdes.shellFn final;
};
}