axum-config 0.1.0

Proc macros for axum-config
Documentation
use axum::{
    Json,
    http::StatusCode,
    response::{IntoResponse, Response},
};
use serde::Serialize;
use thiserror::Error;

#[derive(Debug, Error)]
pub enum ConfigError {
    #[error("Configuration file not found at {0}")]
    FileNotFound(String),

    #[error("Failed to read configuration file: {source}")]
    ReadError {
        #[from]
        source: std::io::Error,
    },

    #[error("Environment variable interpolation error: {message}")]
    InterpolationError { message: String },

    #[error("Configuration key '{key}' not found")]
    KeyNotFound { key: String },

    #[error("Deserialization error: {source}")]
    DeserializeError {
        #[from]
        source: toml::de::Error,
    },

    #[error("Current executable directory not found")]
    ExeDirNotFound,

    #[error("Missing application configuration in request extensions")]
    ExtensionError,
}

impl ConfigError {
    pub fn interpolation_error(message: String) -> Self {
        ConfigError::InterpolationError { message }
    }

    pub fn key_not_found(key: impl Into<String>) -> Self {
        ConfigError::KeyNotFound { key: key.into() }
    }
}

#[derive(Serialize)]
struct ErrorResponse {
    code: u16,
    success: bool,
    message: String,
}

impl IntoResponse for ConfigError {
    fn into_response(self) -> Response {
        tracing::error!("Configuration error: {}", self);

        let json = ErrorResponse {
            code: 500,
            success: false,
            message: "Internal Server Error".to_string(),
        };

        (StatusCode::INTERNAL_SERVER_ERROR, Json(json)).into_response()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_into_response_status() {
        let error = ConfigError::FileNotFound("/test".to_string());
        let response = error.into_response();
        assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
    }
}