1use serde::{Deserialize, Serialize};
4use thiserror::Error;
5
6#[derive(Debug, Error)]
8pub enum ComputerError {
9 #[error("invalid arguments: {0}")]
11 InvalidArguments(String),
12
13 #[error("execution failed: {0}")]
15 ExecutionFailed(String),
16
17 #[error("io error: {0}")]
19 Io(#[from] std::io::Error),
20
21 #[error("unsupported: {0}")]
23 Unsupported(String),
24
25 #[error("app not found: {0}")]
27 AppNotFound(String),
28
29 #[error("window not found: {0}")]
31 WindowNotFound(String),
32
33 #[error("element not found: {0}")]
35 ElementNotFound(String),
36
37 #[error("stale element: {0}")]
39 StaleElement(String),
40
41 #[error("permission required: {0}")]
43 PermissionRequired(String),
44
45 #[error("policy denied: {0}")]
47 PolicyDenied(String),
48
49 #[error("confirm required: {0}")]
51 ConfirmRequired(String),
52
53 #[error("confirm denied: {0}")]
55 ConfirmDenied(String),
56
57 #[error("blocked url: {0}")]
59 BlockedUrl(String),
60
61 #[error("paused: {0}")]
63 Paused(String),
64
65 #[error("cdp unavailable: {0}")]
67 CdpUnavailable(String),
68
69 #[error("display unavailable: {0}")]
71 DisplayUnavailable(String),
72}
73
74impl ComputerError {
75 pub fn code(&self) -> &'static str {
77 match self {
78 Self::InvalidArguments(_) => "invalid_arguments",
79 Self::ExecutionFailed(_) => "execution_failed",
80 Self::Io(_) => "io_error",
81 Self::Unsupported(_) => "unsupported",
82 Self::AppNotFound(_) => "app_not_found",
83 Self::WindowNotFound(_) => "window_not_found",
84 Self::ElementNotFound(_) => "element_not_found",
85 Self::StaleElement(_) => "stale_element",
86 Self::PermissionRequired(_) => "permission_required",
87 Self::PolicyDenied(_) => "policy_denied",
88 Self::ConfirmRequired(_) => "confirm_required",
89 Self::ConfirmDenied(_) => "confirm_denied",
90 Self::BlockedUrl(_) => "blocked_url",
91 Self::Paused(_) => "paused",
92 Self::CdpUnavailable(_) => "cdp_unavailable",
93 Self::DisplayUnavailable(_) => "display_unavailable",
94 }
95 }
96}
97
98#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
100pub struct ErrorBody {
101 pub code: String,
103 pub message: String,
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn codes_and_display_match_the_variant() {
113 let err = ComputerError::InvalidArguments("missing text".into());
114 assert_eq!(err.code(), "invalid_arguments");
115 assert_eq!(err.to_string(), "invalid arguments: missing text");
116
117 let err = ComputerError::ExecutionFailed("click failed".into());
118 assert_eq!(err.code(), "execution_failed");
119 assert_eq!(err.to_string(), "execution failed: click failed");
120
121 let err = ComputerError::Unsupported("no at-spi".into());
122 assert_eq!(err.code(), "unsupported");
123 assert_eq!(err.to_string(), "unsupported: no at-spi");
124
125 let err = ComputerError::Io(std::io::Error::other("disk"));
126 assert_eq!(err.code(), "io_error");
127 assert!(err.to_string().contains("disk"));
128
129 assert_eq!(
130 ComputerError::AppNotFound("Notes".into()).code(),
131 "app_not_found"
132 );
133 assert_eq!(
134 ComputerError::WindowNotFound("0".into()).code(),
135 "window_not_found"
136 );
137 assert_eq!(
138 ComputerError::ElementNotFound("4".into()).code(),
139 "element_not_found"
140 );
141 assert_eq!(
142 ComputerError::StaleElement("role changed".into()).code(),
143 "stale_element"
144 );
145 assert_eq!(
146 ComputerError::PermissionRequired("Accessibility".into()).code(),
147 "permission_required"
148 );
149 assert_eq!(
150 ComputerError::PolicyDenied("Terminal".into()).code(),
151 "policy_denied"
152 );
153 assert_eq!(
154 ComputerError::ConfirmRequired("quit".into()).code(),
155 "confirm_required"
156 );
157 assert_eq!(
158 ComputerError::ConfirmDenied("timeout".into()).code(),
159 "confirm_denied"
160 );
161 assert_eq!(
162 ComputerError::BlockedUrl("https://accounts.example.com".into()).code(),
163 "blocked_url"
164 );
165 assert_eq!(ComputerError::Paused("user input".into()).code(), "paused");
166 assert_eq!(
167 ComputerError::CdpUnavailable("feature off".into()).code(),
168 "cdp_unavailable"
169 );
170 let err = ComputerError::DisplayUnavailable("the screen is locked".into());
171 assert_eq!(err.code(), "display_unavailable");
172 assert_eq!(err.to_string(), "display unavailable: the screen is locked");
173 }
174}