use http::header::{HeaderName, HeaderValue};
use http::StatusCode;
use std::fmt;
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug)]
pub struct Error {
status: StatusCode,
message: String,
source: Option<Box<dyn std::error::Error + Send + Sync>>,
headers: Vec<(HeaderName, HeaderValue)>,
}
impl Error {
pub fn new(status: StatusCode, message: impl Into<String>) -> Self {
Self {
status,
message: message.into(),
source: None,
headers: Vec::new(),
}
}
pub fn bad_request(message: impl Into<String>) -> Self {
Self::new(StatusCode::BAD_REQUEST, message)
}
pub fn not_found(message: impl Into<String>) -> Self {
Self::new(StatusCode::NOT_FOUND, message)
}
pub fn internal(message: impl Into<String>) -> Self {
Self::new(StatusCode::INTERNAL_SERVER_ERROR, message)
}
pub fn with_source(mut self, source: impl std::error::Error + Send + Sync + 'static) -> Self {
self.source = Some(Box::new(source));
self
}
pub fn status(&self) -> StatusCode {
self.status
}
pub fn message(&self) -> &str {
&self.message
}
pub fn with_response_header(mut self, name: HeaderName, value: HeaderValue) -> Self {
self.headers.push((name, value));
self
}
pub fn response_headers(&self) -> &[(HeaderName, HeaderValue)] {
&self.headers
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.status, self.message)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source
.as_ref()
.map(|s| s.as_ref() as &(dyn std::error::Error + 'static))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn carries_status_and_message() {
let e = Error::not_found("nope");
assert_eq!(e.status(), StatusCode::NOT_FOUND);
assert_eq!(e.message(), "nope");
}
#[test]
fn display_includes_status() {
let e = Error::bad_request("bad");
assert!(format!("{e}").contains("400"));
}
#[test]
fn carries_response_headers() {
let e = Error::new(StatusCode::UNAUTHORIZED, "no").with_response_header(
http::header::WWW_AUTHENTICATE,
http::HeaderValue::from_static("Bearer"),
);
assert_eq!(e.response_headers().len(), 1);
}
}