use thiserror::Error;
#[derive(Debug, Error)]
pub enum CarDesktopError {
#[error("platform `{platform}` is not supported in this build")]
PlatformUnsupported { platform: &'static str },
#[error("feature `{feature}` not yet implemented (scheduled: {task_id})")]
NotYetImplemented {
feature: &'static str,
task_id: &'static str,
},
#[error("permission denied: {permission:?} — user must grant in System Settings and relaunch")]
PermissionDenied { permission: Permission },
#[error("OS version too old: {detail}")]
OsTooOld { detail: String },
#[error("window not found: {detail}")]
WindowNotFound { detail: String },
#[error("click point {x},{y} lies outside target window frame {frame:?}")]
OutOfTargetWindow {
x: f64,
y: f64,
frame: crate::models::WindowFrame,
},
#[error("destructive action `{label}` requires explicit `unsafe_ok: true`")]
DestructiveActionGated { label: String },
#[error("mission aborted by kill switch")]
KillSwitchActivated,
#[error("rate limit: target window has exceeded 8 events/sec")]
RateLimited,
#[error("OS API failure: {detail}")]
OsApi {
detail: String,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
#[error("timed out after {elapsed_ms}ms waiting for {condition}")]
WaitTimeout { condition: String, elapsed_ms: u64 },
#[error("element id `{element_id}` not present in the last observation")]
UnknownElement { element_id: String },
#[error("unsupported character in typed text: U+{codepoint:04X}")]
UnsupportedCharacter { codepoint: u32 },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Permission {
ScreenRecording,
Accessibility,
}
impl Permission {
pub fn tcc_service_name(self) -> &'static str {
match self {
Self::ScreenRecording => "kTCCServiceScreenCapture",
Self::Accessibility => "kTCCServiceAccessibility",
}
}
}
pub type Result<T> = std::result::Result<T, CarDesktopError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn platform_unsupported_renders_helpful_message() {
let e = CarDesktopError::PlatformUnsupported { platform: "linux" };
let msg = e.to_string();
assert!(msg.contains("linux"));
}
#[test]
fn permission_maps_to_tcc_service_name() {
assert_eq!(
Permission::ScreenRecording.tcc_service_name(),
"kTCCServiceScreenCapture"
);
assert_eq!(
Permission::Accessibility.tcc_service_name(),
"kTCCServiceAccessibility"
);
}
#[test]
fn out_of_target_window_carries_coords_and_frame() {
let e = CarDesktopError::OutOfTargetWindow {
x: 100.0,
y: 200.0,
frame: crate::models::WindowFrame {
x: 0.0,
y: 0.0,
width: 50.0,
height: 50.0,
},
};
let msg = e.to_string();
assert!(msg.contains("100"));
assert!(msg.contains("200"));
}
}