1use thiserror::Error;
2
3#[derive(Debug, Error)]
14pub enum Error {
15 #[error("{0}")]
17 Message(String),
18
19 #[error("not implemented")]
20 NotImplemented,
21
22 #[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 #[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}