use std::fmt;
use crate::app::AppInfo;
use crate::state::WindowInfo;
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Permission {
Accessibility,
ScreenRecording,
}
impl fmt::Display for Permission {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Accessibility => "accessibility",
Self::ScreenRecording => "screen recording",
})
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("app not found: {0}")]
AppNotFound(String),
#[error("{query:?} matches more than one running app: {}", list(candidates, |a| format!("{} (pid {})", a.name, a.pid.unwrap_or_default())))]
AmbiguousApp {
query: String,
candidates: Vec<AppInfo>,
},
#[error("{title:?} matches more than one window: {}", list(candidates, |w| format!("{:?} (id {})", w.title.as_deref().unwrap_or_default(), w.id.unwrap_or_default())))]
AmbiguousWindow {
title: String,
candidates: Vec<WindowInfo>,
},
#[error("no usable window for app {0}")]
WindowNotFound(String),
#[error("no app state for {0}; call get_app_state first")]
NoAppState(String),
#[error("element {index} not found in the latest state of {app}")]
ElementNotFound {
app: String,
index: usize,
},
#[error("invalid argument: {0}")]
InvalidArgument(String),
#[error("{0} permission has not been granted to this process")]
PermissionDenied(Permission),
#[error("unsupported: {0}")]
Unsupported(String),
#[error("action failed: {0}")]
ActionFailed(String),
#[error("state unavailable: {0}")]
StateUnavailable(String),
#[error("platform error: {0}")]
Platform(String),
#[error("image encoding failed: {0}")]
Image(#[from] image::ImageError),
#[error(transparent)]
Io(#[from] std::io::Error),
}
impl Error {
pub(crate) fn invalid(message: impl Into<String>) -> Self {
Self::InvalidArgument(message.into())
}
#[cfg_attr(not(target_os = "macos"), allow(dead_code))]
pub(crate) fn platform(message: impl Into<String>) -> Self {
Self::Platform(message.into())
}
pub(crate) fn action(message: impl Into<String>) -> Self {
Self::ActionFailed(message.into())
}
}
fn list<T>(items: &[T], describe: impl Fn(&T) -> String) -> String {
items.iter().map(describe).collect::<Vec<_>>().join(", ")
}