use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error("400 Bad Request: {body}")]
BadRequest { body: String },
#[error("401 Unauthorized: {body}")]
Unauthorized { body: String },
#[error("403 Forbidden: {body}")]
Forbidden { body: String },
#[error("429 Too Many Requests: {body}")]
TooManyRequests { body: String },
#[error("503 Service Unavailable: {body}")]
ServiceUnavailable { body: String },
#[error("HTTP {status}: {body}")]
UnexpectedStatus { status: u16, body: String },
#[error("transport error: {0}")]
Transport(#[from] reqwest::Error),
#[error("response body did not match expected schema: {source}; body was: {body}")]
Decode {
#[source]
source: serde_json::Error,
body: String,
},
#[error("authentication failure: {0}")]
Auth(#[from] nordnet_model::AuthError),
#[error("invalid header value: {0}")]
InvalidHeader(String),
#[error("form-urlencoded serialization failed: {0}")]
EncodeForm(String),
}
impl Error {
pub(crate) fn from_status(status: u16, body: String) -> Self {
match status {
400 => Error::BadRequest { body },
401 => Error::Unauthorized { body },
403 => Error::Forbidden { body },
429 => Error::TooManyRequests { body },
503 => Error::ServiceUnavailable { body },
other => Error::UnexpectedStatus {
status: other,
body,
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn maps_documented_statuses() {
assert!(matches!(
Error::from_status(400, "x".into()),
Error::BadRequest { .. }
));
assert!(matches!(
Error::from_status(401, "x".into()),
Error::Unauthorized { .. }
));
assert!(matches!(
Error::from_status(403, "x".into()),
Error::Forbidden { .. }
));
assert!(matches!(
Error::from_status(429, "x".into()),
Error::TooManyRequests { .. }
));
assert!(matches!(
Error::from_status(503, "x".into()),
Error::ServiceUnavailable { .. }
));
assert!(matches!(
Error::from_status(418, "x".into()),
Error::UnexpectedStatus { status: 418, .. }
));
}
}