1use std::time::Duration;
2
3#[derive(Debug, thiserror::Error)]
5pub enum Error {
6 #[error("cannot connect to incusd at {socket}: {source}")]
8 Connect {
9 socket: String,
10 #[source]
11 source: std::io::Error,
12 },
13
14 #[error("incusd did not answer {method} {path} within {timeout:?}")]
16 RequestTimeout {
17 method: String,
18 path: String,
19 timeout: Duration,
20 },
21
22 #[error("incusd: {method} {path}: {message} (HTTP {status})")]
24 Api {
25 method: String,
26 path: String,
27 status: u16,
28 message: String,
29 },
30
31 #[error(
35 "{step} stalled: operation {operation} still {status} after {waited:?}{}",
36 if *.cancelled { " (cancelled)" } else { " (not cancellable; left running on the server)" }
37 )]
38 OperationTimeout {
39 step: String,
40 operation: String,
41 status: String,
42 waited: Duration,
43 cancelled: bool,
44 },
45
46 #[error("{step} failed: {message}")]
48 OperationFailed { step: String, message: String },
49
50 #[error("sandbox {sandbox} not ready after {waited:?}: {check} ({detail})")]
52 NotReady {
53 sandbox: String,
54 check: String,
55 detail: String,
56 waited: Duration,
57 },
58
59 #[error("exec {argv} timed out after {timeout:?} (killed)")]
61 ExecTimeout { argv: String, timeout: Duration },
62
63 #[error("{0} not found")]
64 NotFound(String),
65
66 #[error("sandbox {0} already exists")]
67 AlreadyExists(String),
68
69 #[error("{0}")]
71 Invalid(String),
72
73 #[error("forbidden: {0}")]
76 Forbidden(String),
77
78 #[error("interpolation: {0}")]
80 Interpolation(String),
81
82 #[error("{path}: {message}")]
83 Parse { path: String, message: String },
84
85 #[error("websocket: {0}")]
86 WebSocket(String),
87
88 #[error("protocol: {0}")]
89 Protocol(String),
90
91 #[error("{message}")]
94 Remote {
95 code: String,
96 message: String,
97 data: serde_json::Value,
98 },
99
100 #[error(transparent)]
101 Io(#[from] std::io::Error),
102
103 #[error(transparent)]
104 Json(#[from] serde_json::Error),
105}
106
107impl Error {
108 #[doc(hidden)]
109 pub fn invalid(msg: impl Into<String>) -> Self {
110 Error::Invalid(msg.into())
111 }
112
113 pub fn is_not_found(&self) -> bool {
115 matches!(self, Error::Api { status: 404, .. } | Error::NotFound(_))
116 || matches!(self, Error::Remote { code, .. } if code == "not_found")
117 }
118
119 pub fn is_conflict(&self) -> bool {
121 matches!(
122 self,
123 Error::Api { status: 409, .. } | Error::AlreadyExists(_)
124 ) || matches!(self, Error::Api { message, .. } if message.contains("already exists"))
125 || matches!(self, Error::Remote { code, .. } if code == "already_exists")
126 }
127
128 pub fn is_timeout(&self) -> bool {
130 matches!(
131 self,
132 Error::RequestTimeout { .. } | Error::OperationTimeout { .. }
133 ) || matches!(self, Error::Remote { code, .. } if code == "request_timeout" || code == "operation_timeout")
134 }
135}
136
137pub type Result<T, E = Error> = std::result::Result<T, E>;