soaprs-http 0.2.0

Transport-neutral HTTP metadata for soaprs
Documentation
//! Default mapping from stable application errors to HTTP status codes.

use http::StatusCode;
use soaprs_core::{SoapError, SoapErrorKind};

/// Returns the default HTTP status for a stable soaprs error category.
///
/// Framework adapters may override this mapping at their composition root.
pub const fn default_error_status(error: &SoapError) -> StatusCode {
    match error.kind() {
        SoapErrorKind::NotFound => StatusCode::NOT_FOUND,
        SoapErrorKind::Validation | SoapErrorKind::Domain => StatusCode::UNPROCESSABLE_ENTITY,
        SoapErrorKind::Conflict => StatusCode::CONFLICT,
        SoapErrorKind::Unauthorized => StatusCode::UNAUTHORIZED,
        SoapErrorKind::Forbidden => StatusCode::FORBIDDEN,
        SoapErrorKind::Unsupported => StatusCode::NOT_IMPLEMENTED,
        SoapErrorKind::Timeout => StatusCode::GATEWAY_TIMEOUT,
        SoapErrorKind::Unavailable => StatusCode::SERVICE_UNAVAILABLE,
        SoapErrorKind::Infrastructure => StatusCode::INTERNAL_SERVER_ERROR,
    }
}

#[cfg(test)]
mod tests {
    use http::StatusCode;
    use soaprs_core::SoapError;

    use super::default_error_status;

    #[test]
    fn distinguishes_authentication_and_authorization() {
        assert_eq!(
            default_error_status(&SoapError::unauthorized()),
            StatusCode::UNAUTHORIZED
        );
        assert_eq!(
            default_error_status(&SoapError::forbidden()),
            StatusCode::FORBIDDEN
        );
    }

    #[test]
    fn maps_stable_error_categories_to_default_statuses() {
        let cases = [
            (SoapError::not_found("user"), StatusCode::NOT_FOUND),
            (
                SoapError::validation("invalid email"),
                StatusCode::UNPROCESSABLE_ENTITY,
            ),
            (
                SoapError::domain("account is closed"),
                StatusCode::UNPROCESSABLE_ENTITY,
            ),
            (SoapError::conflict("email exists"), StatusCode::CONFLICT),
            (
                SoapError::unsupported("full-text search"),
                StatusCode::NOT_IMPLEMENTED,
            ),
            (
                SoapError::timeout("database query"),
                StatusCode::GATEWAY_TIMEOUT,
            ),
            (
                SoapError::unavailable("database"),
                StatusCode::SERVICE_UNAVAILABLE,
            ),
            (
                SoapError::infrastructure("database operation"),
                StatusCode::INTERNAL_SERVER_ERROR,
            ),
        ];

        for (error, expected) in cases {
            assert_eq!(default_error_status(&error), expected);
        }
    }
}