use std::fmt::{Display, Formatter};
#[derive(Debug)]
pub enum GbizError {
Request(reqwest::Error),
Status {
status: u16,
body: String,
},
Decode {
error: serde_json::Error,
body: String,
},
}
impl GbizError {
pub fn status_code(&self) -> Option<u16> {
match self {
GbizError::Status { status, .. } => Some(*status),
_ => None,
}
}
pub fn is_not_found(&self) -> bool {
self.status_code() == Some(404)
}
}
impl Display for GbizError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
GbizError::Request(e) => write!(f, "リクエストに失敗しました: {e}"),
GbizError::Status { status, body } => {
write!(f, "API がエラーステータス {status} を返しました: {body}")
}
GbizError::Decode { error, body } => {
write!(f, "レスポンスのデコードに失敗しました: {error}: {body}")
}
}
}
}
impl std::error::Error for GbizError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
GbizError::Request(e) => Some(e),
GbizError::Decode { error, .. } => Some(error),
GbizError::Status { .. } => None,
}
}
}
impl From<reqwest::Error> for GbizError {
fn from(e: reqwest::Error) -> Self {
GbizError::Request(e)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn status_code_and_not_found() {
let err = GbizError::Status {
status: 404,
body: "{}".to_string(),
};
assert_eq!(err.status_code(), Some(404));
assert!(err.is_not_found());
let err = GbizError::Status {
status: 401,
body: String::new(),
};
assert_eq!(err.status_code(), Some(401));
assert!(!err.is_not_found());
let err = GbizError::Decode {
error: serde_json::from_str::<serde_json::Value>("x").unwrap_err(),
body: "x".to_string(),
};
assert_eq!(err.status_code(), None);
assert!(!err.is_not_found());
}
#[test]
fn display_contains_context() {
let err = GbizError::Status {
status: 401,
body: "unauthorized".to_string(),
};
let msg = err.to_string();
assert!(msg.contains("401"));
assert!(msg.contains("unauthorized"));
}
}