use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum HealthStatus {
Healthy,
Degraded,
Unhealthy,
}
impl HealthStatus {
pub fn to_http_status(&self) -> u16 {
match self {
HealthStatus::Healthy => 200, HealthStatus::Degraded => 206, HealthStatus::Unhealthy => 503, }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthCheckResult {
pub status: HealthStatus,
pub api_available: bool,
pub desktop_accessible: bool,
pub can_enumerate_elements: bool,
pub check_duration_ms: u64,
pub platform: String,
pub error_message: Option<String>,
pub diagnostics: HashMap<String, serde_json::Value>,
}
impl Default for HealthCheckResult {
fn default() -> Self {
Self {
status: HealthStatus::Unhealthy,
api_available: false,
desktop_accessible: false,
can_enumerate_elements: false,
check_duration_ms: 0,
platform: std::env::consts::OS.to_string(),
error_message: Some("Health check not performed".to_string()),
diagnostics: HashMap::new(),
}
}
}
impl HealthCheckResult {
pub fn healthy(platform: impl Into<String>) -> Self {
Self {
status: HealthStatus::Healthy,
api_available: true,
desktop_accessible: true,
can_enumerate_elements: true,
check_duration_ms: 0,
platform: platform.into(),
error_message: None,
diagnostics: HashMap::new(),
}
}
pub fn unhealthy(platform: impl Into<String>, error: impl Into<String>) -> Self {
Self {
status: HealthStatus::Unhealthy,
api_available: false,
desktop_accessible: false,
can_enumerate_elements: false,
check_duration_ms: 0,
platform: platform.into(),
error_message: Some(error.into()),
diagnostics: HashMap::new(),
}
}
pub fn update_status(&mut self) {
self.status =
if self.api_available && self.desktop_accessible && self.can_enumerate_elements {
HealthStatus::Healthy
} else if self.api_available {
HealthStatus::Degraded
} else {
HealthStatus::Unhealthy
};
}
pub fn add_diagnostic(&mut self, key: impl Into<String>, value: impl Serialize) {
if let Ok(json_value) = serde_json::to_value(value) {
self.diagnostics.insert(key.into(), json_value);
}
}
}
#[async_trait]
pub trait PlatformHealthCheck: Send + Sync {
async fn check_health(&self) -> HealthCheckResult;
async fn quick_check(&self) -> bool {
self.check_health().await.api_available
}
}
pub async fn get_platform_health_checker() -> Box<dyn PlatformHealthCheck> {
#[cfg(target_os = "windows")]
{
Box::new(super::platforms::windows::health::WindowsHealthChecker::new())
}
#[cfg(target_os = "macos")]
{
Box::new(MacOSHealthChecker)
}
#[cfg(target_os = "linux")]
{
Box::new(LinuxHealthChecker)
}
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
{
Box::new(UnsupportedPlatformHealthChecker)
}
}
pub async fn check_automation_health() -> HealthCheckResult {
let checker = get_platform_health_checker().await;
checker.check_health().await
}
#[cfg(target_os = "macos")]
struct MacOSHealthChecker;
#[cfg(target_os = "macos")]
#[async_trait]
impl PlatformHealthCheck for MacOSHealthChecker {
async fn check_health(&self) -> HealthCheckResult {
let mut result = HealthCheckResult::healthy("macos");
result.add_diagnostic(
"note",
"Accessibility API health checks not yet implemented",
);
result
}
}
#[cfg(target_os = "linux")]
struct LinuxHealthChecker;
#[cfg(target_os = "linux")]
#[async_trait]
impl PlatformHealthCheck for LinuxHealthChecker {
async fn check_health(&self) -> HealthCheckResult {
let mut result = HealthCheckResult::healthy("linux");
result.add_diagnostic("note", "AT-SPI health checks not yet implemented");
result
}
}
#[allow(dead_code)]
struct UnsupportedPlatformHealthChecker;
#[async_trait]
impl PlatformHealthCheck for UnsupportedPlatformHealthChecker {
async fn check_health(&self) -> HealthCheckResult {
HealthCheckResult {
status: HealthStatus::Healthy,
api_available: true,
desktop_accessible: true,
can_enumerate_elements: true,
check_duration_ms: 0,
platform: std::env::consts::OS.to_string(),
error_message: None,
diagnostics: {
let mut diag = HashMap::new();
diag.insert(
"note".to_string(),
serde_json::Value::String(
"Platform-specific health checks not implemented".to_string(),
),
);
diag
},
}
}
}