Skip to main content

linkprobe_core/
error.rs

1use thiserror::Error;
2
3/// Failure from discovery, measurement, or export helpers.
4///
5/// Match on this instead of string-matching `Display` output:
6///
7/// - [`Error::Iperf3Missing`](Self::Iperf3Missing) — `iperf3` binary not found when using
8///   [`Iperf3Engine`](crate::backends::Iperf3Engine)
9/// - [`Error::Probe`](Self::Probe) — a named measurement phase failed (for example `"download"`);
10///   the `source` chain holds the underlying error
11/// - [`Error::Http`](Self::Http), [`Error::Io`](Self::Io), [`Error::Json`](Self::Json) —
12///   transparent wrappers for reqwest, I/O, and JSON errors
13#[derive(Debug, Error)]
14pub enum Error {
15    /// General failure with a message (unknown server id, invalid CLI combination in the binary crate, etc.).
16    #[error("{0}")]
17    Message(String),
18
19    #[error("not implemented")]
20    NotImplemented,
21
22    /// A measurement phase failed; inspect `phase` and `source`.
23    #[error("{phase} failed: {source}")]
24    Probe {
25        phase: &'static str,
26        #[source]
27        source: Box<Error>,
28    },
29
30    #[error("iperf3 not found on PATH (install iperf3 to use --backend iperf3)")]
31    Iperf3Missing,
32
33    /// MQTT publish failure (CLI crate).
34    #[error("mqtt: {0}")]
35    Mqtt(String),
36
37    #[error(transparent)]
38    Http(#[from] reqwest::Error),
39
40    #[error(transparent)]
41    Url(#[from] url::ParseError),
42
43    #[error(transparent)]
44    Io(#[from] std::io::Error),
45
46    #[error(transparent)]
47    Json(#[from] serde_json::Error),
48}
49
50impl Error {
51    pub fn probe(phase: &'static str, source: impl Into<Error>) -> Self {
52        Error::Probe {
53            phase,
54            source: Box::new(source.into()),
55        }
56    }
57
58    pub fn from_reqwest(phase: &'static str, err: reqwest::Error) -> Self {
59        let hint = if err.is_timeout() {
60            format!("{err} (timed out)")
61        } else if err.is_connect() {
62            format!("{err} (connection failed)")
63        } else if err.is_decode() || err.is_body() {
64            format!(
65                "{err} (connection closed before the response finished; retry or pick another server)"
66            )
67        } else {
68            err.to_string()
69        };
70        Error::Probe {
71            phase,
72            source: Box::new(Error::Message(hint)),
73        }
74    }
75}