rjapi 0.0.1

A framework-agnostic JSON:API 1.1 implementation for Rust
Documentation
//! JSON:API Error Handling
//!
//! This module provides error handling utilities for JSON:API responses.
//! It includes data structures for JSON:API errors and helper functions
//! for creating common error responses.

use http::StatusCode;

use crate::response::{JsonApiDocument, JsonApiError};
use std::fmt;

/// A comprehensive error type for JSON:API operations.
///
/// This struct integrates with Rust's standard error handling by implementing
/// the `std::error::Error` trait. It includes an HTTP status code and a list
/// of `JsonApiError` objects, making it suitable for direct use in API handlers.
#[derive(Debug)]
pub struct JsonApiErrorResponse {
    pub status: StatusCode,
    pub errors: Vec<JsonApiError>,
}

impl fmt::Display for JsonApiErrorResponse {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "JSON:API Error ({}): {:?}",
            self.status,
            self.errors
                .iter()
                .map(|e| e.title.as_deref().unwrap_or("Unknown Error"))
                .collect::<Vec<_>>()
        )
    }
}

impl std::error::Error for JsonApiErrorResponse {}

impl JsonApiErrorResponse {
    /// Constructs a `JsonApiErrorResponse` from a status code and a list of errors.
    pub fn new(status: StatusCode, errors: Vec<JsonApiError>) -> Self {
        Self { status, errors }
    }

    /// Converts the error response into a `JsonApiDocument` for serialization.
    pub fn to_document<T>(&self) -> JsonApiDocument<T> {
        JsonApiDocument {
            errors: Some(self.errors.clone()),
            data: None,
            included: None,
            links: None,
            meta: None,
            jsonapi: None,
        }
    }
}

/// Creates a `400 Bad Request` error.
pub fn bad_request_error(title: &str, detail: &str) -> JsonApiErrorResponse {
    let error = JsonApiError::builder()
        .status("400")
        .code("BAD_REQUEST")
        .title(title)
        .detail(detail)
        .build();
    JsonApiErrorResponse::new(StatusCode::BAD_REQUEST, vec![error])
}

/// Creates a `500 Internal Server Error`.
pub fn internal_server_error(detail: &str) -> JsonApiErrorResponse {
    let error = JsonApiError::builder()
        .status("500")
        .code("INTERNAL_SERVER_ERROR")
        .title("Internal Server Error")
        .detail(detail)
        .build();
    JsonApiErrorResponse::new(StatusCode::INTERNAL_SERVER_ERROR, vec![error])
}

/// Creates a `401 Unauthorized` error.
pub fn unauthorized_error(detail: &str) -> JsonApiErrorResponse {
    let error = JsonApiError::builder()
        .status("401")
        .code("UNAUTHORIZED")
        .title("Unauthorized")
        .detail(detail)
        .build();
    JsonApiErrorResponse::new(StatusCode::UNAUTHORIZED, vec![error])
}

/// Creates a `404 Not Found` error.
pub fn not_found_error(detail: &str) -> JsonApiErrorResponse {
    let error = JsonApiError::builder()
        .status("404")
        .code("NOT_FOUND")
        .title("Not Found")
        .detail(detail)
        .build();
    JsonApiErrorResponse::new(StatusCode::NOT_FOUND, vec![error])
}

/// Creates a `403 Forbidden` error.
pub fn forbidden_error(detail: &str) -> JsonApiErrorResponse {
    let error = JsonApiError::builder()
        .status("403")
        .code("FORBIDDEN")
        .title("Forbidden")
        .detail(detail)
        .build();
    JsonApiErrorResponse::new(StatusCode::FORBIDDEN, vec![error])
}

/// Creates a `409 Conflict` error.
pub fn conflict_error(detail: &str) -> JsonApiErrorResponse {
    let error = JsonApiError::builder()
        .status("409")
        .code("CONFLICT")
        .title("Conflict")
        .detail(detail)
        .build();
    JsonApiErrorResponse::new(StatusCode::CONFLICT, vec![error])
}