use std::path::{Path, PathBuf};
use std::process::Command;
use anyhow::Result;
use framework_system::CommandExt;
use sysinfo::{ProcessRefreshKind, RefreshKind, System, UpdateKind};
use crate::config::StartupConfig;
use crate::platform::Platform;
use crate::scope::Scope;
pub(crate) struct WindowsStartup;
impl Platform for WindowsStartup {
fn exists(&self, config: &StartupConfig, _scope: Scope) -> Result<bool> {
Ok(Command::new("schtasks")
.args(["/query", "/tn", &config.name])
.command_stdout()
.is_some())
}
fn is_run(&self, config: &StartupConfig, _scope: Scope) -> Result<bool> {
Ok(program_running(&config.program))
}
fn install(&self, config: &StartupConfig, scope: Scope) -> Result<()> {
let mut args = vec![
"/create".to_string(),
"/tn".to_string(),
config.name.clone(),
"/tr".to_string(),
config.command_line(),
"/sc".to_string(),
];
match scope {
Scope::Root => {
args.push("onstart".to_string());
args.push("/ru".to_string());
args.push("SYSTEM".to_string());
args.push("/rl".to_string());
args.push("highest".to_string());
}
Scope::User => {
args.push("onlogon".to_string());
if config.highest {
args.push("/rl".to_string());
args.push("highest".to_string());
}
}
}
args.push("/f".to_string());
Command::new("schtasks").args(&args).command_run()
}
fn uninstall(&self, config: &StartupConfig, _scope: Scope) -> Result<()> {
Command::new("schtasks")
.args(["/delete", "/tn", &config.name, "/f"])
.command_run()
}
fn start(&self, config: &StartupConfig, _scope: Scope) -> Result<()> {
Command::new("schtasks")
.args(["/run", "/tn", &config.name])
.command_run()
}
fn stop(&self, config: &StartupConfig, _scope: Scope) -> Result<()> {
Command::new("schtasks")
.args(["/end", "/tn", &config.name])
.command_run()
}
}
fn program_running(program: &Path) -> bool {
let Some(program) = normalize(program) else {
return false;
};
let system = System::new_with_specifics(
RefreshKind::nothing()
.with_processes(ProcessRefreshKind::nothing().with_exe(UpdateKind::OnlyIfNotSet)),
);
system.processes().values().any(|process| {
process
.exe()
.and_then(normalize)
.is_some_and(|candidate| candidate == program)
})
}
fn normalize(path: &Path) -> Option<PathBuf> {
std::fs::canonicalize(path).ok()
}