1use std::fmt;
2
3use crate::app::AppInfo;
4use crate::state::WindowInfo;
5
6pub type Result<T, E = Error> = std::result::Result<T, E>;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11#[non_exhaustive]
12pub enum Permission {
13 Accessibility,
15 ScreenRecording,
17}
18
19impl fmt::Display for Permission {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 f.write_str(match self {
22 Self::Accessibility => "accessibility",
23 Self::ScreenRecording => "screen recording",
24 })
25 }
26}
27
28#[derive(Debug, thiserror::Error)]
30#[non_exhaustive]
31pub enum Error {
32 #[error("app not found: {0}")]
34 AppNotFound(String),
35
36 #[error("{query:?} matches more than one running app: {}", list(candidates, |a| format!("{} (pid {})", a.name, a.pid.unwrap_or_default())))]
38 AmbiguousApp {
39 query: String,
41 candidates: Vec<AppInfo>,
43 },
44
45 #[error("{title:?} matches more than one window: {}", list(candidates, |w| format!("{:?} (id {})", w.title.as_deref().unwrap_or_default(), w.id.unwrap_or_default())))]
47 AmbiguousWindow {
48 title: String,
50 candidates: Vec<WindowInfo>,
52 },
53
54 #[error("no usable window for app {0}")]
56 WindowNotFound(String),
57
58 #[error("no app state for {0}; call get_app_state first")]
60 NoAppState(String),
61
62 #[error("element {index} not found in the latest state of {app}")]
64 ElementNotFound {
65 app: String,
67 index: usize,
69 },
70
71 #[error("invalid argument: {0}")]
73 InvalidArgument(String),
74
75 #[error("{0} permission has not been granted to this process")]
77 PermissionDenied(Permission),
78
79 #[error("unsupported: {0}")]
81 Unsupported(String),
82
83 #[error("action failed: {0}")]
85 ActionFailed(String),
86
87 #[error("state unavailable: {0}")]
89 StateUnavailable(String),
90
91 #[error("platform error: {0}")]
93 Platform(String),
94
95 #[error("image encoding failed: {0}")]
97 Image(#[from] image::ImageError),
98
99 #[error(transparent)]
101 Io(#[from] std::io::Error),
102}
103
104impl Error {
105 pub(crate) fn invalid(message: impl Into<String>) -> Self {
106 Self::InvalidArgument(message.into())
107 }
108
109 #[cfg_attr(not(target_os = "macos"), allow(dead_code))]
110 pub(crate) fn platform(message: impl Into<String>) -> Self {
111 Self::Platform(message.into())
112 }
113
114 pub(crate) fn action(message: impl Into<String>) -> Self {
115 Self::ActionFailed(message.into())
116 }
117}
118
119fn list<T>(items: &[T], describe: impl Fn(&T) -> String) -> String {
120 items.iter().map(describe).collect::<Vec<_>>().join(", ")
121}