axum-error-handler 0.2.0

A simple error handler for axum
Documentation
# Axum Error Handler

A simple parser that implemented Axum `IntoResponse` trait.

> Please notice that this is a experimental project.
> This proc-macros depends on `axum`, `thiserror` and `serde_json` crates. 

## Basic Usage

```rust
use axum_error_handler::AxumErrorResponse;
use thiserror::Error;

#[derive(Debug, Error, AxumErrorResponse)]
pub enum TestError {
    #[error("Bad request: {0}")]
    #[status_code("400")]
    #[code("BAD_REQUEST")]
    BadRequest(String),
    
    #[error("Internal server error {0}")]
    #[status_code("500")]
    #[code("INTERNAL_SERVER_ERROR")]
    InternalError(String),
}
```

### Basic Output

```json
{
  "result": null,
  "error": {
    "code": "BAD_REQUEST",
    "message": "Bad request: invalid input"
  }
}
```

## Nested Response Support

The library supports nested error handling through the `#[response(nested)]` attribute. This allows inner errors that also implement `AxumErrorResponse` to be properly forwarded with their own status codes and error details.

### Example

```rust
use axum_error_handler::AxumErrorResponse;
use thiserror::Error;

#[derive(Debug, Error, AxumErrorResponse)]
pub enum AppError {
    #[error("Bad request: {0}")]
    #[status_code("400")]
    #[code("BAD_REQUEST")]
    BadRequest(String),
    
    // Use nested response to delegate to inner error's response
    #[error("{0}")]
    #[response(nested)]
    ServiceError(#[from] ServiceError),
}

#[derive(Debug, Error, AxumErrorResponse)]
pub enum ServiceError {
    #[error("Authentication error: {0}")]
    #[status_code("401")]
    #[code("AUTHENTICATION_ERROR")]
    AuthenticationError(String),
    
    #[error("Database connection failed: {0}")]
    #[status_code("503")]
    #[code("DATABASE_ERROR")]
    DatabaseError(String),
    
    // Supports multiple levels of nesting
    #[error("{0}")]
    #[response(nested)]
    ValidationError(#[from] ValidationError),
}

#[derive(Debug, Error, AxumErrorResponse)]
pub enum ValidationError {
    #[error("Validation failed: {0}")]
    #[status_code("422")]
    #[code("VALIDATION_ERROR")]
    FieldValidation(String),
    
    #[error("Permission denied: {0}")]
    #[status_code("403")]
    #[code("PERMISSION_DENIED")]
    PermissionDenied(String),
}
```

### Usage

```rust
// Direct error
let err = AppError::BadRequest("missing field".to_string());

// Nested error - preserves inner error's status code and details
let service_err = ServiceError::AuthenticationError("invalid token".to_string());
let app_err = AppError::ServiceError(service_err);

// Multi-level nesting
let validation_err = ValidationError::FieldValidation("email format invalid".to_string());
let service_err = ServiceError::ValidationError(validation_err);
let app_err = AppError::ServiceError(service_err);
```

### Nested Response Output

When using nested responses, the inner error's status code, error code, and message are preserved:

```json
{
  "result": null,
  "error": {
    "code": "AUTHENTICATION_ERROR",
    "message": "Authentication error: invalid token"
  }
}
```

### Key Features

- **Status Code Preservation**: Nested errors maintain their original HTTP status codes
- **Error Code Forwarding**: Custom error codes from inner errors are preserved
- **Multiple Nesting Levels**: Supports arbitrarily deep error nesting
- **Automatic Conversion**: Use `#[from]` attribute for automatic error conversion
- **Consistent JSON Format**: All responses follow the same `{"result": null, "error": {...}}` structure