1use serde::Serialize;
2use thiserror::Error;
3
4#[derive(Debug, Error, Serialize)]
5#[serde(tag = "kind", content = "message")]
6pub enum AppError {
7 #[error("not found: {0}")]
8 NotFound(String),
9
10 #[error("bad request: {0}")]
11 BadRequest(String),
12
13 #[error("validation error: {0}")]
14 ValidationError(String),
15
16 #[error("unauthorized: {0}")]
17 Unauthorized(String),
18
19 #[error("forbidden: {0}")]
20 Forbidden(String),
21
22 #[error("conflict: {0}")]
23 Conflict(String),
24
25 #[error("internal error: {0}")]
26 InternalError(String),
27
28 #[error("browser disconnected: {0}")]
29 BrowserDisconnected(String),
30
31 #[error("cdp timeout: {0}")]
32 CdpTimeout(String),
33
34 #[error("llm rate limited: {0}")]
35 LlmRateLimited(String),
36
37 #[error("llm auth expired: {0}")]
38 LlmAuthExpired(String),
39
40 #[error("llm provider error: {0}")]
41 LlmProviderError(String),
42
43 #[error("action failed: {0}")]
44 ActionFailed(String),
45
46 #[error("element not found: {0}")]
47 ElementNotFound(String),
48}
49
50impl AppError {
51 #[must_use]
52 pub fn cli_exit_code(&self) -> i32 {
53 match self {
54 Self::NotFound(_) | Self::ElementNotFound(_) => 4,
55 Self::BadRequest(_) | Self::ValidationError(_) => 2,
56 Self::Unauthorized(_) | Self::LlmAuthExpired(_) => 5,
57 Self::Forbidden(_) => 6,
58 Self::Conflict(_) => 7,
59 Self::BrowserDisconnected(_) | Self::CdpTimeout(_) => 8,
60 Self::LlmRateLimited(_) | Self::LlmProviderError(_) => 9,
61 Self::ActionFailed(_) | Self::InternalError(_) => 1,
62 }
63 }
64}
65
66pub type Result<T> = std::result::Result<T, AppError>;