use std::fmt;
use std::path::{Path, PathBuf};
use crate::platform::autostart::{AutostartError, AutostartProgram};
const IDENTIFIER: &str = "runpm-daemon";
const LABEL: &str = "com.zackees.runpm-daemon";
const DESCRIPTION: &str = "runpm process supervisor (running-process daemon)";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UnitPath(pub PathBuf);
impl UnitPath {
pub fn as_path(&self) -> &Path {
&self.0
}
pub fn into_inner(self) -> PathBuf {
self.0
}
}
impl fmt::Display for UnitPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0.display())
}
}
#[derive(Debug)]
pub enum BootAutostartError {
Resolve(String),
Io(std::io::Error),
InitSystem(String),
Unsupported(String),
}
impl fmt::Display for BootAutostartError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Resolve(detail) => write!(f, "could not resolve autostart location: {detail}"),
Self::Io(error) => write!(f, "autostart file operation failed: {error}"),
Self::InitSystem(detail) => write!(f, "init system rejected autostart: {detail}"),
Self::Unsupported(os) => write!(f, "boot autostart is not supported on {os}"),
}
}
}
impl std::error::Error for BootAutostartError {}
impl From<std::io::Error> for BootAutostartError {
fn from(error: std::io::Error) -> Self {
Self::Io(error)
}
}
impl From<AutostartError> for BootAutostartError {
fn from(error: AutostartError) -> Self {
match error {
AutostartError::Resolve(detail) => Self::Resolve(detail),
AutostartError::Io(io) => Self::Io(io),
AutostartError::InitSystem(detail) => Self::InitSystem(detail),
}
}
}
fn runpm_daemon(daemon_binary: &Path) -> AutostartProgram<'_> {
AutostartProgram {
identifier: IDENTIFIER,
label: LABEL,
description: DESCRIPTION,
program: daemon_binary,
start_argument: "start",
stop_argument: "stop",
}
}
pub fn install(daemon_binary: &Path) -> Result<UnitPath, BootAutostartError> {
let written = crate::platform::autostart::register(&runpm_daemon(daemon_binary))?;
Ok(UnitPath(written))
}
pub fn uninstall() -> Result<(), BootAutostartError> {
Ok(crate::platform::autostart::unregister(&runpm_daemon(
Path::new(""),
))?)
}
pub fn render_unit(daemon_binary: &Path) -> String {
crate::platform::autostart::render_registration(&runpm_daemon(daemon_binary))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_rendered_registration_is_for_the_runpm_daemon() {
let rendered = render_unit(Path::new("/usr/local/bin/running-process-daemon"));
assert!(
rendered.contains("running-process-daemon"),
"must name the daemon binary: {rendered}"
);
assert!(
rendered.contains("runpm"),
"an operator must be able to tell whose registration this is: {rendered}"
);
}
#[test]
fn facade_errors_keep_their_kind() {
assert!(matches!(
BootAutostartError::from(AutostartError::Resolve("no HOME".into())),
BootAutostartError::Resolve(_)
));
assert!(matches!(
BootAutostartError::from(AutostartError::InitSystem("schtasks".into())),
BootAutostartError::InitSystem(_)
));
assert!(matches!(
BootAutostartError::from(AutostartError::Io(std::io::Error::other("disk"))),
BootAutostartError::Io(_)
));
}
}