use std::{fs, io::ErrorKind, path::Path, sync::Arc};
use sidoc::{Builder, RenderContext};
use super::Error;
pub fn install(ctx: super::RegSvc) -> Result<(), Error> {
let mut bldr = Builder::new();
bldr.line(r#"<?xml version="1.0" encoding="UTF-8"?>"#);
bldr.line(r#"<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">"#);
bldr.scope(r#"<plist version="1.0">"#, Some("</plist>"));
bldr.scope("<dict>", Some("</dict"));
bldr.line("<key>Label</key>");
bldr.line(format!("<string>{}</string>", ctx.svcname()));
let service_binary_path = ::std::env::current_exe()?
.to_str()
.ok_or_else(|| Error::bad_format("Executable pathname is not utf-8"))?
.to_string();
if let Some(ref username) = ctx.runas.user {
bldr.line("<key>UserName</key>");
bldr.line(format!("<string>{username}</string>"));
}
if let Some(ref groupname) = ctx.runas.group {
bldr.line("<key>GroupName</key>");
bldr.line(format!("<string>{groupname}</string>"));
}
if ctx.runas.initgroups {
bldr.line("<key>InitGroups</key>");
bldr.line("<true/>");
}
if let Some(ref umask) = ctx.runas.umask {
bldr.line("<key>Umask</key>");
bldr.line(format!("<integer>{umask}</integer>"));
}
if ctx.have_args() {
bldr.line("<key>ProgramArguments</key>");
bldr.scope("<array>", Some("</array"));
bldr.line(format!("<string>{service_binary_path}</string>"));
for arg in &ctx.args {
bldr.line(format!("<string>{arg}</string>"));
}
bldr.exit(); } else {
bldr.line("<key>Program</key>");
bldr.line(format!("<string>{service_binary_path}</string>"));
}
let mut envs = Vec::new();
if let Some(ll) = ctx.log_level {
envs.push((String::from("LOG_LEVEL"), ll.to_string()));
}
if let Some(ref lf) = ctx.log_filter {
envs.push((String::from("LOG_FILTER"), lf.clone()));
}
if let Some(ref tf) = ctx.trace_filter {
envs.push((String::from("TRACE_FILTER"), tf.clone()));
}
if let Some(ref fname) = ctx.trace_file {
envs.push((String::from("TRACE_FILE"), fname.clone()));
}
if ctx.have_envs() {
for (key, value) in &ctx.envs {
envs.push((key.clone(), value.clone()));
}
}
if !envs.is_empty() {
bldr.line("<key>EnvironmentVariables</key>");
bldr.scope("<dict>", Some("</dict"));
for (key, value) in envs {
bldr.line(format!("<key>{key}</key>"));
bldr.line(format!("<string>{value}</string>"));
}
bldr.exit(); }
if let Some(wd) = ctx.workdir {
bldr.line("<key>WorkingDirectory</key>");
bldr.line(format!("<string>{wd}</string>"));
}
if ctx.autostart {
bldr.line("<key>RunAtLoad</key>");
bldr.line("<true/>");
}
bldr.exit(); bldr.exit();
let doc = bldr.build()?;
let mut r = RenderContext::new();
r.doc("root", Arc::new(doc));
let buf = r.render("root")?;
let fname = format!("{}.plist", ctx.svcname);
let fname = Path::new(&fname);
if fname.exists() && !ctx.force {
Err(Error::IO(
std::io::Error::new(ErrorKind::AlreadyExists, "File already exists")
.to_string()
))?;
}
fs::write(fname, buf)?;
Ok(())
}
#[expect(clippy::missing_errors_doc)]
#[expect(clippy::missing_const_for_fn)]
pub fn uninstall(_svcname: &str) -> Result<(), Error> {
Ok(())
}