use std::path::{Path, PathBuf};
use super::ServiceError;
pub const DPKG_LIST_PATH: &str = "/var/lib/dpkg/info/all-smi.list";
pub const DPKG_BINARY_PATH: &str = "/usr/bin/all-smi";
pub const HOMEBREW_PREFIXES: &[&str] = &["/home/linuxbrew", "/opt/homebrew", "/usr/local/Cellar"];
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PackageOwner {
Unmanaged,
Dpkg,
Homebrew,
}
pub fn classify(exe: &Path, dpkg_list_present: bool) -> PackageOwner {
if dpkg_list_present && exe == Path::new(DPKG_BINARY_PATH) {
return PackageOwner::Dpkg;
}
for prefix in HOMEBREW_PREFIXES {
if exe.starts_with(prefix) {
return PackageOwner::Homebrew;
}
}
PackageOwner::Unmanaged
}
pub fn detect() -> PackageOwner {
let Ok(exe) = std::env::current_exe() else {
return PackageOwner::Unmanaged;
};
let exe: PathBuf = exe.canonicalize().unwrap_or(exe);
classify(&exe, Path::new(DPKG_LIST_PATH).exists())
}
pub fn guard(force: bool) -> Result<(), ServiceError> {
if force {
return Ok(());
}
match detect() {
PackageOwner::Unmanaged => Ok(()),
PackageOwner::Dpkg => Err(ServiceError::PackageManaged(
"The deb package already ships a systemd unit; use 'sudo systemctl enable --now \
all-smi' instead"
.to_string(),
)),
PackageOwner::Homebrew => Err(ServiceError::PackageManaged(format!(
"The Homebrew formula already ships a service definition; use 'brew services start \
all-smi' instead{HOMEBREW_BOOT_HINT}"
))),
}
}
#[cfg(target_os = "macos")]
const HOMEBREW_BOOT_HINT: &str =
" (or 'sudo brew services start all-smi' for a boot-time system daemon)";
#[cfg(not(target_os = "macos"))]
const HOMEBREW_BOOT_HINT: &str = "";
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dpkg_requires_both_the_file_list_and_the_packaged_path() {
let packaged = Path::new(DPKG_BINARY_PATH);
assert_eq!(classify(packaged, true), PackageOwner::Dpkg);
assert_eq!(classify(packaged, false), PackageOwner::Unmanaged);
}
#[test]
fn dpkg_does_not_fire_for_a_locally_built_binary() {
let local = Path::new("/home/dev/all-smi/target/release/all-smi");
assert_eq!(classify(local, true), PackageOwner::Unmanaged);
}
#[test]
fn homebrew_prefixes_are_recognised() {
for (path, label) in [
(
"/home/linuxbrew/.linuxbrew/Cellar/all-smi/0.25.0/bin/all-smi",
"linuxbrew",
),
(
"/opt/homebrew/Cellar/all-smi/0.25.0/bin/all-smi",
"apple silicon",
),
(
"/usr/local/Cellar/all-smi/0.25.0/bin/all-smi",
"intel macOS",
),
] {
assert_eq!(
classify(Path::new(path), false),
PackageOwner::Homebrew,
"{label} prefix must be recognised: {path}"
);
}
}
#[test]
fn homebrew_match_is_component_wise_not_substring() {
assert_eq!(
classify(Path::new("/opt/homebrewery/bin/all-smi"), false),
PackageOwner::Unmanaged
);
assert_eq!(
classify(Path::new("/usr/local/CellarX/bin/all-smi"), false),
PackageOwner::Unmanaged
);
}
#[test]
fn unmanaged_paths_stay_unmanaged() {
for path in [
"/usr/local/bin/all-smi",
"/opt/all-smi/bin/all-smi",
"/home/dev/.cargo/bin/all-smi",
] {
assert_eq!(
classify(Path::new(path), true),
PackageOwner::Unmanaged,
"{path} must not be treated as package-managed"
);
}
}
#[test]
fn force_bypasses_the_guard() {
assert!(guard(true).is_ok());
}
#[test]
fn the_homebrew_refusal_distinguishes_the_two_brew_services_domains() {
#[cfg(target_os = "macos")]
{
assert!(HOMEBREW_BOOT_HINT.contains("sudo brew services start all-smi"));
assert!(HOMEBREW_BOOT_HINT.contains("boot-time"));
}
#[cfg(not(target_os = "macos"))]
assert!(HOMEBREW_BOOT_HINT.is_empty());
}
#[test]
fn guard_messages_name_the_package_manager_command() {
let dpkg = ServiceError::PackageManaged(
"The deb package already ships a systemd unit; use 'sudo systemctl enable --now \
all-smi' instead"
.to_string(),
);
assert!(dpkg.to_string().contains("systemctl enable --now all-smi"));
let brew = ServiceError::PackageManaged(
"The Homebrew formula already ships a service definition; use 'brew services start \
all-smi' instead"
.to_string(),
);
assert!(brew.to_string().contains("brew services start all-smi"));
}
}