1use std::io;
2use std::path::PathBuf;
3use std::process::ExitStatus;
4use std::time::Duration;
5
6#[derive(Debug, thiserror::Error)]
8#[error("{action} {}: {source}", path.display())]
9pub struct FileError {
10 pub action: &'static str,
11 pub path: PathBuf,
12 #[source]
13 pub source: io::Error,
14}
15
16#[derive(Debug, thiserror::Error)]
18pub enum PruneError {
19 #[error(transparent)]
20 File(#[from] FileError),
21 #[error(transparent)]
23 Git(#[from] GitCallError),
24}
25
26#[derive(Debug, thiserror::Error)]
28pub enum ResetError {
29 #[error(transparent)]
31 Git(#[from] GitCallError),
32 #[error(transparent)]
33 Symlinks(#[from] FileError),
34}
35
36#[derive(Debug, PartialEq, Eq, thiserror::Error)]
38pub enum HttpsAccessError {
39 #[error("Invalid credentials: a line break or a nul byte")]
41 CredentialLineBreak,
42 #[error("expected an http or https url")]
43 NotHttp,
44 #[error("Invalid url: its host or port could not be parsed")]
46 Unparsable,
47 #[error("expected an https credential")]
48 NotHttpsCredential,
49}
50
51#[derive(Debug, thiserror::Error)]
53pub enum CloneError {
54 #[error(transparent)]
55 Access(#[from] HttpsAccessError),
56 #[error(transparent)]
57 Git(#[from] GitCallError),
58 #[error(transparent)]
60 Info(#[from] FileError),
61 #[error(transparent)]
62 SshKey(#[from] SshKeyError),
63 #[error(transparent)]
64 Sts(#[from] StsError),
65}
66
67#[derive(Debug, thiserror::Error)]
69pub enum SshKeyError {
70 #[error("the SSH key is not base64: {0}")]
71 Decode(#[from] base64::DecodeError),
72 #[error("the SSH key is empty")]
73 Empty,
74 #[error("the SSH key could not be written: {0}")]
75 File(#[source] io::Error),
76 #[error("the SSH key path cannot be quoted for the shell: {0}")]
77 Path(#[from] shlex::QuoteError),
78}
79
80#[derive(Debug, thiserror::Error)]
82pub enum LsRemoteError {
83 #[error(transparent)]
84 SshKey(#[from] SshKeyError),
85 #[error(transparent)]
86 Sts(#[from] StsError),
87 #[error(transparent)]
88 Access(#[from] HttpsAccessError),
89 #[error(transparent)]
90 Git(#[from] GitCallError),
91}
92
93#[derive(Debug, thiserror::Error)]
95pub enum HostError {
96 #[error("could not resolve the hostname: {0}")]
97 Unresolved(#[source] io::Error),
98 #[error("no IP addresses resolved for the hostname")]
99 NoAddresses,
100 #[error(transparent)]
101 Local(crate::local_address::LocalAddress),
102}
103
104#[derive(Debug, thiserror::Error)]
106pub enum SshUrlError {
107 #[error("Invalid ssh url: expected user@host:path or ssh://user@host[:port]/path")]
109 Malformed,
110 #[error("SSH host validation failed for {host}: {source}")]
111 Refused {
112 host: String,
113 #[source]
114 source: HostError,
115 },
116}
117
118#[derive(Debug, thiserror::Error)]
120pub enum RedirectError {
121 #[error("Invalid url: {0}")]
123 Unparsable(String),
124 #[error("expected an http or https url")]
125 NotHttp,
126 #[error("Hostname or IP address invalid")]
127 InvalidHost,
128 #[error("expected an https credential")]
129 NotHttpsCredential,
130 #[error("Invalid credentials: a line break or another character a header cannot carry")]
131 CredentialNotAHeader,
132 #[error(transparent)]
133 Local(crate::local_address::LocalAddress),
134 #[error("no response in time: {0}")]
135 Timeout(#[source] reqwest::Error),
136 #[error("the request failed: {0}")]
138 Request(#[source] reqwest::Error),
139}
140
141#[derive(Debug, thiserror::Error)]
143pub enum StsError {
144 #[error("STS refused the role {arn}: {code}: {message}")]
146 Refused {
147 arn: String,
148 code: String,
149 message: String,
150 },
151 #[error("STS took too long to connect for the role {arn}: {reason}")]
152 ConnectTimeout { arn: String, reason: String },
153 #[error("STS took too long to answer the role {arn}: {reason}")]
155 ReadTimeout { arn: String, reason: String },
156 #[error("STS closed the connection for the role {arn}: {reason}")]
158 ConnectionClosed { arn: String, reason: String },
159 #[error("STS could not be connected to for the role {arn}: {reason}")]
161 NoConnection { arn: String, reason: String },
162 #[error("STS could not be asked for the role {arn}: {reason}")]
164 Unreachable { arn: String, reason: String },
165 #[error("STS answered the role {arn} without credentials")]
166 NoCredentials { arn: String },
167}
168
169#[derive(Debug, thiserror::Error)]
170pub enum GitCallError {
171 #[error("git {command} could not start: {source}")]
172 NotStarted {
173 command: &'static str,
174 source: io::Error,
175 },
176 #[error("git {command} did not finish within {after:?}")]
177 TimedOut {
178 command: &'static str,
179 after: Duration,
180 },
181 #[error("git {command} exited with {status}: {stderr}")]
183 Refused {
184 command: &'static str,
185 status: ExitStatus,
186 stderr: String,
187 },
188 #[error("git {command} got an argument with an embedded null byte")]
190 NullByte { command: &'static str },
191 #[error("git {command} printed something unreadable: {reason}")]
192 Malformed {
193 command: &'static str,
194 reason: String,
195 },
196}
197
198#[derive(Debug, thiserror::Error)]
200pub enum DownloadError {
201 #[error("writing the archive: {0}")]
204 Write(#[source] io::Error),
205 #[error(transparent)]
206 Fetch(#[from] FetchError),
207 #[error(transparent)]
208 Extract(#[from] ExtractError),
209 #[error(transparent)]
211 Reset(#[from] GitCallError),
212 #[error("pruning the working tree: {0}")]
213 Prune(#[from] PruneError),
214 #[error("placing the repository: {0}")]
217 Place(#[source] io::Error),
218}
219
220#[derive(Debug, thiserror::Error)]
222pub enum FetchError {
223 #[error("invalid url {url}: {reason}")]
224 InvalidUrl { url: String, reason: String },
225 #[error("not an http url: {0}")]
226 NonHttpUrl(String),
227 #[error("could not connect: {0}")]
229 Connect(#[source] reqwest::Error),
230 #[error("connecting timed out: {0}")]
231 ConnectTimeout(#[source] reqwest::Error),
232 #[error("no response in time: {0}")]
234 Timeout(#[source] reqwest::Error),
235 #[error("the server disconnected: {0}")]
237 Disconnected(#[source] reqwest::Error),
238 #[error("the socket failed: {source}")]
241 Socket {
242 reset: bool,
243 errno: Option<i32>,
244 source: reqwest::Error,
245 },
246 #[error("malformed response: {0}")]
247 Malformed(#[source] reqwest::Error),
248 #[error("the server answered {0}")]
249 Status(reqwest::StatusCode),
250 #[error("the body was cut: {0}")]
252 BodyCut(#[source] reqwest::Error),
253 #[error("the body did not arrive in time: {0}")]
255 BodyTimeout(#[source] reqwest::Error),
256 #[error("the request failed: {0}")]
257 Other(#[source] reqwest::Error),
258}
259
260#[derive(Debug, thiserror::Error)]
262pub enum ExtractError {
263 #[error("corrupt archive: {0}")]
266 Corrupt(#[source] io::Error),
267 #[error("truncated archive: {0}")]
269 Truncated(#[source] io::Error),
270 #[error("the archive holds no repository: {0}")]
272 NoRepository(String),
273 #[error("extracting the archive: {0}")]
274 Io(#[source] io::Error),
275}