use thiserror::Error;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum Error {
#[error("HTTP request failed: {0}")]
Request(#[from] reqwest::Error),
#[error("API error {status}: {message}")]
Api {
status: u16,
message: String,
body: Vec<u8>,
},
#[error("unknown API error {status}: {body}")]
UnknownApi {
status: u16,
body: String,
},
#[error("validation error: {0}")]
Validation(String),
#[error("version error: {0}")]
Version(String),
#[error("unknown version: {0}")]
UnknownVersion(String),
#[error("SSH signing error: {0}")]
SshSign(String),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("URL parse error: {0}")]
Url(#[from] url::ParseError),
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn error_implements_std_error() {
fn assert_error<E: std::error::Error>(_: &E) {}
assert_error(&Error::Validation("test".to_string()));
}
#[test]
fn error_is_send_sync() {
fn assert_send_sync<T: Send + Sync>(_: &T) {}
assert_send_sync(&Error::Validation("test".to_string()));
}
#[test]
fn result_alias_works() {
fn returns_result() -> Result<i32> {
Ok(42)
}
assert_eq!(returns_result().unwrap(), 42);
fn returns_error() -> Result<i32> {
Err(Error::Validation("bad".to_string()))
}
assert_eq!(
returns_error().unwrap_err().to_string(),
"validation error: bad"
);
}
#[test]
fn display_api() {
let err = Error::Api {
status: 404,
message: "not found".to_string(),
body: b"error details".to_vec(),
};
assert_eq!(err.to_string(), "API error 404: not found");
}
#[test]
fn display_unknown_api() {
let err = Error::UnknownApi {
status: 500,
body: "internal error".to_string(),
};
assert_eq!(err.to_string(), "unknown API error 500: internal error");
}
#[test]
fn display_validation() {
let err = Error::Validation("owner is empty".to_string());
assert_eq!(err.to_string(), "validation error: owner is empty");
}
#[test]
fn display_version() {
let err = Error::Version("unsupported version".to_string());
assert_eq!(err.to_string(), "version error: unsupported version");
}
#[test]
fn display_unknown_version() {
let err = Error::UnknownVersion("1.99.0".to_string());
assert_eq!(err.to_string(), "unknown version: 1.99.0");
}
#[test]
fn display_ssh_sign() {
let err = Error::SshSign("signing failed".to_string());
assert_eq!(err.to_string(), "SSH signing error: signing failed");
}
#[test]
fn display_json() {
let err: Error = serde_json::from_str::<serde_json::Value>("invalid json")
.unwrap_err()
.into();
assert!(err.to_string().contains("JSON error"));
}
#[tokio::test]
async fn display_request() {
let req_err = reqwest::Client::builder()
.build()
.unwrap()
.get("http://0.0.0.0:1")
.send()
.await
.expect_err("should fail to connect");
let err = Error::from(req_err);
assert!(err.to_string().starts_with("HTTP request failed"));
}
}