#[cfg(any(target_os = "macos", test))]
pub mod plist;
#[cfg(any(target_os = "linux", test))]
pub mod unit;
#[cfg_attr(target_os = "macos", path = "macos.rs")]
#[cfg_attr(target_os = "linux", path = "linux.rs")]
#[cfg_attr(
not(any(target_os = "macos", target_os = "linux")),
path = "unsupported.rs"
)]
mod platform;
use std::path::PathBuf;
use anyhow::Result;
#[cfg(any(target_os = "macos", target_os = "linux"))]
use serde::Serialize;
use crate::home::Home;
#[cfg(all(any(target_os = "macos", target_os = "linux"), debug_assertions))]
pub(crate) fn skip_exec() -> bool {
std::env::var_os("MOAT_SERVICE_SKIP_EXEC").is_some_and(|v| v == "1")
}
#[cfg(all(any(target_os = "macos", target_os = "linux"), not(debug_assertions)))]
pub(crate) fn skip_exec() -> bool {
false
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(tag = "state", rename_all = "kebab-case")]
pub enum State {
NotInstalled,
Stopped,
Running,
DriftBlocked,
Crashlooping,
Unknown { details: String },
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
impl State {
pub fn describe(&self, path: &std::path::Path) -> String {
match self {
Self::NotInstalled => {
"not installed; `moat proxy install` keeps the proxy running".to_owned()
}
Self::Stopped => format!("installed but stopped ({})", path.display()),
Self::Running => format!("installed and running ({})", path.display()),
Self::DriftBlocked => format!(
"installed; the proxy exited because of policy-lock drift. Fix the drift \
(`moat doctor`), then `moat proxy install` restarts it ({})",
path.display()
),
Self::Crashlooping => format!(
"installed; restart budget exhausted. Check the service log, then \
`moat proxy install` to retry ({})",
path.display()
),
Self::Unknown { details } => format!("installed; state unknown: {details}"),
}
}
}
pub trait Manager {
fn install(&self, binary: &std::path::Path, moat_home: &std::path::Path) -> Result<PathBuf>;
fn uninstall(&self) -> Result<Option<PathBuf>>;
#[cfg(any(target_os = "macos", target_os = "linux"))]
fn file_path(&self) -> Result<PathBuf>;
#[cfg(any(target_os = "macos", target_os = "linux"))]
fn state(&self) -> Result<State>;
#[cfg(any(target_os = "macos", target_os = "linux"))]
fn restart(&self) -> Result<()>;
}
pub fn manager(home: &Home) -> Result<Box<dyn Manager>> {
platform::manager(home)
}
pub fn display_name() -> &'static str {
platform::DISPLAY
}
#[cfg(all(test, any(target_os = "macos", target_os = "linux")))]
mod tests {
use super::*;
#[test]
fn state_describe_names_the_fix_for_each_variant() {
let path = std::path::Path::new("/x/plist");
assert!(State::NotInstalled.describe(path).contains("not installed"));
assert!(State::Stopped.describe(path).contains("stopped"));
assert!(State::Running.describe(path).contains("running"));
assert!(State::DriftBlocked.describe(path).contains("drift"));
assert!(State::Crashlooping.describe(path).contains("restart"));
assert!(
State::Unknown {
details: "no bus".into(),
}
.describe(path)
.contains("no bus")
);
}
}