regy 0.1.1

Private-by-default desktop agent for the Regy web interface
use std::env;

use crate::domain::errors::{AgentError, AgentResult, ErrorCode};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Platform {
    pub os: OperatingSystem,
    pub arch: Architecture,
    pub libc: Libc,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum OperatingSystem {
    Linux,
    MacOs,
    Windows,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Architecture {
    X86_64,
    Aarch64,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Libc {
    Glibc,
    Musl,
    NotApplicable,
}

impl Platform {
    pub(crate) fn detect() -> AgentResult<Self> {
        Self::from_parts(env::consts::OS, env::consts::ARCH, detected_linux_libc())
    }

    pub(crate) fn from_parts(os: &str, arch: &str, libc: Option<&str>) -> AgentResult<Self> {
        let os = match os.to_ascii_lowercase().as_str() {
            "linux" => OperatingSystem::Linux,
            "macos" | "darwin" => OperatingSystem::MacOs,
            "windows" => OperatingSystem::Windows,
            _ => return Err(platform_error("unsupported operating system")),
        };
        let arch = match arch.to_ascii_lowercase().as_str() {
            "x86_64" | "amd64" | "x64" => Architecture::X86_64,
            "aarch64" | "arm64" => Architecture::Aarch64,
            _ => return Err(platform_error("unsupported architecture")),
        };
        let libc = if os == OperatingSystem::Linux {
            match libc.map(str::to_ascii_lowercase).as_deref() {
                Some("glibc" | "gnu") => Libc::Glibc,
                Some("musl") => Libc::Musl,
                _ => return Err(platform_error("unsupported Linux libc")),
            }
        } else {
            Libc::NotApplicable
        };

        Ok(Self { os, arch, libc })
    }

    pub(crate) fn supports_managed_install(self) -> bool {
        matches!(
            (self.os, self.arch, self.libc),
            (OperatingSystem::Linux, Architecture::X86_64, Libc::Glibc)
                | (OperatingSystem::Linux, Architecture::Aarch64, Libc::Glibc)
                | (
                    OperatingSystem::MacOs,
                    Architecture::X86_64,
                    Libc::NotApplicable
                )
                | (
                    OperatingSystem::MacOs,
                    Architecture::Aarch64,
                    Libc::NotApplicable
                )
        )
    }
}

fn detected_linux_libc() -> Option<&'static str> {
    if env::consts::OS != "linux" {
        return None;
    }

    if cfg!(target_env = "musl") {
        Some("musl")
    } else if cfg!(target_env = "gnu") {
        Some("glibc")
    } else {
        None
    }
}

fn platform_error(message: &'static str) -> AgentError {
    AgentError::new(ErrorCode::InvalidMessage, message)
}