1use std::io;
2use std::path::PathBuf;
3use std::process::ExitStatus;
4use std::string::FromUtf8Error;
5use std::time::Duration;
6
7#[derive(Debug, thiserror::Error)]
9#[error("{action} {}: {source}", path.display())]
10pub struct FileError {
11 pub action: &'static str,
12 pub path: PathBuf,
13 #[source]
14 pub source: io::Error,
15}
16
17#[derive(Debug, thiserror::Error)]
19pub enum PruneError {
20 #[error(transparent)]
21 File(#[from] FileError),
22 #[error(transparent)]
24 Git(#[from] GitCallError),
25}
26
27#[derive(Debug, thiserror::Error)]
29pub enum ResetError {
30 #[error(transparent)]
32 Git(#[from] GitCallError),
33 #[error(transparent)]
34 Symlinks(#[from] FileError),
35}
36
37#[derive(Debug, PartialEq, Eq, thiserror::Error)]
39pub enum HttpsAccessError {
40 #[error("Invalid credentials: a line break or a nul byte")]
42 CredentialLineBreak,
43 #[error("expected an http or https url")]
44 NotHttp,
45 #[error("Invalid url: its host or port could not be parsed")]
47 Unparsable,
48 #[error("expected an https credential")]
49 NotHttpsCredential,
50}
51
52#[derive(Debug, thiserror::Error)]
54pub enum CloneError {
55 #[error(transparent)]
56 Access(#[from] HttpsAccessError),
57 #[error(transparent)]
58 Git(#[from] GitCallError),
59 #[error(transparent)]
61 Info(#[from] FileError),
62 #[error(transparent)]
63 SshKey(#[from] SshKeyError),
64 #[error(transparent)]
65 Sts(#[from] StsError),
66}
67
68#[derive(Debug, thiserror::Error)]
70pub enum SshKeyError {
71 #[error("the SSH key is not base64: {0}")]
72 Decode(#[from] base64::DecodeError),
73 #[error("the SSH key is empty")]
74 Empty,
75 #[error("the SSH key could not be written: {0}")]
76 File(#[source] io::Error),
77 #[error("the SSH key path cannot be quoted for the shell: {0}")]
78 Path(#[from] shlex::QuoteError),
79}
80
81#[derive(Debug, thiserror::Error)]
83pub enum LsRemoteError {
84 #[error(transparent)]
85 SshKey(#[from] SshKeyError),
86 #[error(transparent)]
87 Sts(#[from] StsError),
88 #[error(transparent)]
89 Access(#[from] HttpsAccessError),
90 #[error(transparent)]
91 Git(#[from] GitCallError),
92}
93
94#[derive(Debug, thiserror::Error)]
96pub(crate) enum RemoteAccessError {
97 #[error(transparent)]
98 SshKey(#[from] SshKeyError),
99 #[error(transparent)]
100 Sts(#[from] StsError),
101 #[error(transparent)]
102 Access(#[from] HttpsAccessError),
103}
104
105impl From<RemoteAccessError> for LsRemoteError {
106 fn from(error: RemoteAccessError) -> Self {
107 match error {
108 RemoteAccessError::SshKey(error) => Self::SshKey(error),
109 RemoteAccessError::Sts(error) => Self::Sts(error),
110 RemoteAccessError::Access(error) => Self::Access(error),
111 }
112 }
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
117pub enum LocalAddress {
118 #[error("Requests to loopback addresses are not allowed")]
119 Loopback,
120 #[error("Requests to the local network are not allowed")]
121 LocalNetwork,
122}
123
124#[derive(Debug, thiserror::Error)]
126pub enum HttpsLsRemoteError {
127 #[error(transparent)]
129 Host(#[from] HostError),
130 #[error(transparent)]
131 Access(#[from] HttpsAccessError),
132 #[error(transparent)]
133 Git(#[from] GitCallError),
134}
135
136#[derive(Debug, thiserror::Error)]
138pub enum HostError {
139 #[error("Invalid url: its host could not be read")]
140 Unparsable,
141 #[error("could not resolve the hostname: {0}")]
142 Unresolved(#[source] io::Error),
143 #[error("no IP addresses resolved for the hostname")]
144 NoAddresses,
145 #[error(transparent)]
146 Local(LocalAddress),
147}
148
149#[derive(Debug, thiserror::Error)]
151pub enum RedirectError {
152 #[error("Invalid url: {0}")]
154 Unparsable(String),
155 #[error("expected an http or https url")]
156 NotHttp,
157 #[error("Hostname or IP address invalid")]
158 InvalidHost,
159 #[error("expected an https credential")]
160 NotHttpsCredential,
161 #[error("Invalid credentials: a line break or another character a header cannot carry")]
162 CredentialNotAHeader,
163 #[error(transparent)]
164 Local(LocalAddress),
165 #[error("no response in time: {0}")]
166 Timeout(#[source] reqwest::Error),
167 #[error("the request failed: {0}")]
169 Request(#[source] reqwest::Error),
170}
171
172#[derive(Debug, thiserror::Error)]
174pub enum StsError {
175 #[error("STS refused the role {arn}: {code}: {message}")]
177 Refused {
178 arn: String,
179 code: String,
180 message: String,
181 },
182 #[error("STS took too long to connect for the role {arn}: {reason}")]
183 ConnectTimeout { arn: String, reason: String },
184 #[error("STS took too long to answer the role {arn}: {reason}")]
186 ReadTimeout { arn: String, reason: String },
187 #[error("STS closed the connection for the role {arn}: {reason}")]
189 ConnectionClosed { arn: String, reason: String },
190 #[error("STS could not be connected to for the role {arn}: {reason}")]
192 NoConnection { arn: String, reason: String },
193 #[error("STS could not be asked for the role {arn}: {reason}")]
195 Unreachable { arn: String, reason: String },
196 #[error("STS answered the role {arn} without credentials")]
197 NoCredentials { arn: String },
198}
199
200#[derive(Debug, thiserror::Error)]
201pub enum GitCallError {
202 #[error("git {command} could not start: {source}")]
203 NotStarted {
204 command: &'static str,
205 source: io::Error,
206 },
207 #[error("git {command} did not finish within {after:?}")]
208 TimedOut {
209 command: &'static str,
210 after: Duration,
211 },
212 #[error("git {command} exited with {status}: {stderr}")]
214 Refused {
215 command: &'static str,
216 status: ExitStatus,
217 stderr: String,
218 },
219 #[error("git {command} got an argument with an embedded null byte")]
221 NullByte { command: &'static str },
222 #[error("git {command} printed something unreadable: {reason}")]
223 Malformed {
224 command: &'static str,
225 reason: String,
226 },
227}
228
229#[derive(Debug, thiserror::Error)]
231pub enum WarpCliError {
232 #[error("warp-cli could not start: {source}")]
233 NotStarted { source: io::Error },
234 #[error("warp-cli did not finish within {after:?}")]
236 TimedOut { after: Duration },
237 #[error("{stderr}")]
240 DaemonUnreachable { status: ExitStatus, stderr: String },
241 #[error("{stderr}")]
243 Refused { status: ExitStatus, stderr: String },
244 #[error("warp-cli printed something unreadable: {source}")]
245 Malformed { source: FromUtf8Error },
246}
247
248#[derive(Debug, thiserror::Error)]
250pub enum VnetLookupError {
251 #[error(transparent)]
252 Cli(#[from] WarpCliError),
253 #[error("Failed to find virtual network {name}")]
254 UnknownNetwork { name: String },
255 #[error("virtual network {name} has no id")]
257 Unidentified { name: String },
258}
259
260#[derive(Debug, thiserror::Error)]
262pub enum WarpConnectError {
263 #[error(transparent)]
264 Cli(#[from] WarpCliError),
265 #[error("Failed to resolve DNS")]
266 DnsUnresolved,
267}
268
269#[derive(Debug, thiserror::Error)]
271pub enum DownloadError {
272 #[error("writing the archive: {0}")]
275 Write(#[source] io::Error),
276 #[error(transparent)]
277 Fetch(#[from] FetchError),
278 #[error(transparent)]
279 Extract(#[from] ExtractError),
280 #[error(transparent)]
282 Reset(#[from] GitCallError),
283 #[error("pruning the working tree: {0}")]
284 Prune(#[from] PruneError),
285 #[error("placing the repository: {0}")]
288 Place(#[source] io::Error),
289}
290
291#[derive(Debug, thiserror::Error)]
293pub enum FetchError {
294 #[error("invalid url {url}: {reason}")]
295 InvalidUrl { url: String, reason: String },
296 #[error("not an http url: {0}")]
297 NonHttpUrl(String),
298 #[error("could not connect: {0}")]
300 Connect(#[source] reqwest::Error),
301 #[error("connecting timed out: {0}")]
302 ConnectTimeout(#[source] reqwest::Error),
303 #[error("no response in time: {0}")]
305 Timeout(#[source] reqwest::Error),
306 #[error("the server disconnected: {0}")]
308 Disconnected(#[source] reqwest::Error),
309 #[error("the socket failed: {source}")]
312 Socket {
313 reset: bool,
314 errno: Option<i32>,
315 source: reqwest::Error,
316 },
317 #[error("malformed response: {0}")]
318 Malformed(#[source] reqwest::Error),
319 #[error("the server answered {0}")]
320 Status(reqwest::StatusCode),
321 #[error("the body was cut: {0}")]
323 BodyCut(#[source] reqwest::Error),
324 #[error("the body did not arrive in time: {0}")]
326 BodyTimeout(#[source] reqwest::Error),
327 #[error("the request failed: {0}")]
328 Other(#[source] reqwest::Error),
329}
330
331#[derive(Debug, thiserror::Error)]
333pub enum ExtractError {
334 #[error("corrupt archive: {0}")]
337 Corrupt(#[source] io::Error),
338 #[error("truncated archive: {0}")]
340 Truncated(#[source] io::Error),
341 #[error("the archive holds no repository: {0}")]
343 NoRepository(String),
344 #[error("extracting the archive: {0}")]
345 Io(#[source] io::Error),
346}