1use anyhow::Error;
2use curl::easy::Easy;
3use http::Response;
4use std::fmt::{self, Write};
5use std::path::PathBuf;
6
7use crate::util::network::http_async::ResponsePartsExtensions;
8
9use super::truncate_with_ellipsis;
10
11pub type CargoResult<T> = anyhow::Result<T>;
12
13pub const DEBUG_HEADERS: &[&str] = &[
16 "x-amz-cf-id",
19 "x-amz-cf-pop",
23 "x-amz-request-id",
25 "x-amz-id-2",
27 "x-cache",
29 "x-served-by",
31];
32
33#[derive(Debug)]
34pub struct HttpNotSuccessful {
35 pub code: u32,
36 pub url: String,
37 pub ip: Option<String>,
38 pub body: Vec<u8>,
39 pub headers: Vec<String>,
40}
41
42impl HttpNotSuccessful {
43 pub fn new_from_handle(
44 handle: &mut Easy,
45 initial_url: &str,
46 body: Vec<u8>,
47 headers: Vec<String>,
48 ) -> HttpNotSuccessful {
49 let ip = handle.primary_ip().ok().flatten().map(|s| s.to_string());
50 let url = handle
51 .effective_url()
52 .ok()
53 .flatten()
54 .unwrap_or(initial_url)
55 .to_string();
56 HttpNotSuccessful {
57 code: handle.response_code().unwrap_or(0),
58 url,
59 ip,
60 body,
61 headers,
62 }
63 }
64
65 pub fn new_from_response(response: Response<Vec<u8>>, url: &str) -> HttpNotSuccessful {
66 let ip = response.client_ip().map(str::to_string);
67 let url = response.effective_url().unwrap_or(url).to_string();
68 let (head, body) = response.into_parts();
69 let headers = head
70 .headers
71 .into_iter()
72 .filter_map(|(k, v)| Some(format!("{}: {}", k?.as_str(), v.to_str().ok()?)))
73 .collect();
74 HttpNotSuccessful {
75 code: head.status.as_u16() as u32,
76 url,
77 ip,
78 body,
79 headers,
80 }
81 }
82
83 pub fn display_short(&self) -> String {
85 self.render(false)
86 }
87
88 fn render(&self, show_headers: bool) -> String {
89 let mut result = String::new();
90 let body = std::str::from_utf8(&self.body)
91 .map(|s| truncate_with_ellipsis(s, 512))
92 .unwrap_or_else(|_| format!("[{} non-utf8 bytes]", self.body.len()));
93
94 write!(
95 result,
96 "failed to get successful HTTP response from `{}`",
97 self.url
98 )
99 .unwrap();
100 if let Some(ip) = &self.ip {
101 write!(result, " ({ip})").unwrap();
102 }
103 write!(result, ", got {}\n", self.code).unwrap();
104 if show_headers {
105 let headers: Vec<_> = self
106 .headers
107 .iter()
108 .filter(|header| {
109 let Some((name, _)) = header.split_once(":") else {
110 return false;
111 };
112 DEBUG_HEADERS.contains(&name.to_ascii_lowercase().trim())
113 })
114 .collect();
115 if !headers.is_empty() {
116 writeln!(result, "debug headers:").unwrap();
117 for header in headers {
118 writeln!(result, "{header}").unwrap();
119 }
120 }
121 }
122 write!(result, "body:\n{body}").unwrap();
123 result
124 }
125}
126
127impl fmt::Display for HttpNotSuccessful {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 f.write_str(&self.render(true))
130 }
131}
132
133impl std::error::Error for HttpNotSuccessful {}
134
135pub struct VerboseError {
145 inner: Error,
146}
147
148impl VerboseError {
149 pub fn new(inner: Error) -> VerboseError {
150 VerboseError { inner }
151 }
152}
153
154impl std::error::Error for VerboseError {
155 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
156 self.inner.source()
157 }
158}
159
160impl fmt::Debug for VerboseError {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 self.inner.fmt(f)
163 }
164}
165
166impl fmt::Display for VerboseError {
167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168 self.inner.fmt(f)
169 }
170}
171
172pub struct InternalError {
180 inner: Error,
181}
182
183impl InternalError {
184 pub fn new(inner: Error) -> InternalError {
185 InternalError { inner }
186 }
187}
188
189impl std::error::Error for InternalError {
190 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
191 self.inner.source()
192 }
193}
194
195impl fmt::Debug for InternalError {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 self.inner.fmt(f)
198 }
199}
200
201impl fmt::Display for InternalError {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 self.inner.fmt(f)
204 }
205}
206
207pub struct AlreadyPrintedError {
213 inner: Error,
214}
215
216impl AlreadyPrintedError {
217 pub fn new(inner: Error) -> Self {
218 AlreadyPrintedError { inner }
219 }
220}
221
222impl std::error::Error for AlreadyPrintedError {
223 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
224 self.inner.source()
225 }
226}
227
228impl fmt::Debug for AlreadyPrintedError {
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 self.inner.fmt(f)
231 }
232}
233
234impl fmt::Display for AlreadyPrintedError {
235 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236 self.inner.fmt(f)
237 }
238}
239
240pub struct ManifestError {
247 cause: Error,
248 manifest: PathBuf,
249}
250
251impl ManifestError {
252 pub fn new<E: Into<Error>>(cause: E, manifest: PathBuf) -> Self {
253 Self {
254 cause: cause.into(),
255 manifest,
256 }
257 }
258
259 pub fn manifest_path(&self) -> &PathBuf {
260 &self.manifest
261 }
262
263 pub fn manifest_causes(&self) -> ManifestCauses<'_> {
267 ManifestCauses { current: self }
268 }
269}
270
271impl std::error::Error for ManifestError {
272 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
273 self.cause.source()
274 }
275}
276
277impl fmt::Debug for ManifestError {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 self.cause.fmt(f)
280 }
281}
282
283impl fmt::Display for ManifestError {
284 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285 self.cause.fmt(f)
286 }
287}
288
289pub struct ManifestCauses<'a> {
291 current: &'a ManifestError,
292}
293
294impl<'a> Iterator for ManifestCauses<'a> {
295 type Item = &'a ManifestError;
296
297 fn next(&mut self) -> Option<Self::Item> {
298 self.current = self.current.cause.downcast_ref()?;
299 Some(self.current)
300 }
301}
302
303impl<'a> ::std::iter::FusedIterator for ManifestCauses<'a> {}
304
305pub type CliResult = Result<(), CliError>;
309
310#[derive(Debug)]
311pub struct CliError {
317 pub error: Option<anyhow::Error>,
323 pub exit_code: i32,
325}
326
327impl CliError {
328 pub fn new(error: anyhow::Error, code: i32) -> CliError {
329 CliError {
330 error: Some(error),
331 exit_code: code,
332 }
333 }
334
335 pub fn code(code: i32) -> CliError {
336 CliError {
337 error: None,
338 exit_code: code,
339 }
340 }
341}
342
343impl From<anyhow::Error> for CliError {
344 fn from(err: anyhow::Error) -> CliError {
345 CliError::new(err, 101)
346 }
347}
348
349impl From<clap::Error> for CliError {
350 fn from(err: clap::Error) -> CliError {
351 let code = if err.use_stderr() { 1 } else { 0 };
352 CliError::new(err.into(), code)
353 }
354}
355
356impl From<std::io::Error> for CliError {
357 fn from(err: std::io::Error) -> CliError {
358 CliError::new(err.into(), 1)
359 }
360}
361
362pub type GitCliResult = Result<(), GitCliError>;
366
367#[derive(Debug)]
375pub struct GitCliError {
376 inner: Error,
377 is_spurious: bool,
378}
379
380impl GitCliError {
381 pub fn new(inner: Error, is_spurious: bool) -> GitCliError {
382 GitCliError { inner, is_spurious }
383 }
384
385 pub fn is_spurious(&self) -> bool {
386 self.is_spurious
387 }
388}
389
390impl std::error::Error for GitCliError {
391 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
392 self.inner.source()
393 }
394}
395
396impl fmt::Display for GitCliError {
397 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
398 self.inner.fmt(f)
399 }
400}
401
402pub fn internal<S: fmt::Display>(error: S) -> anyhow::Error {
406 InternalError::new(anyhow::format_err!("{}", error)).into()
407}