use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use thiserror::Error;
use crate::config::ConfigError;
#[derive(Debug, Error)]
pub enum GotchaError {
#[error(transparent)]
Config(#[from] ConfigError),
#[error("invalid listen address: {0}")]
InvalidAddress(String),
#[error("failed to bind server to {addr}: {source}")]
Bind {
addr: String,
source: std::io::Error,
},
#[error(transparent)]
Io(std::io::Error),
#[error("{0}")]
Message(String),
#[error(transparent)]
Other(#[from] Box<dyn std::error::Error + Send + Sync>),
}
impl GotchaError {
pub fn message(msg: impl std::fmt::Display) -> Self {
Self::Message(msg.to_string())
}
}
pub type GotchaResult<T> = Result<T, GotchaError>;
impl IntoResponse for GotchaError {
fn into_response(self) -> Response {
tracing::error!(error = %self, "request failed with a gotcha error");
(StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn renders_as_internal_server_error() {
let response = GotchaError::message("boom").into_response();
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
#[test]
fn config_error_converts() {
let err: GotchaError = ConfigError::Error("bad".into()).into();
assert!(matches!(err, GotchaError::Config(_)));
}
}