lxy 0.1.1

A convenient async http and RPC framework in Rust
Documentation
//! Error handling for the lxy framework.
//!
//! This module provides a unified error handling system that works seamlessly
//! across HTTP and gRPC protocols.
//!
//! # Features
//!
//! - **Type-safe error codes**: System-defined codes that map to HTTP and gRPC status codes
//! - **Error type binding**: Compile-time guarantee of code+type pairing via [`TypedError`]
//! - **Protocol conversion**: Automatic conversion to HTTP responses (with `http` feature) and gRPC Status (with `grpc` feature)
//! - **Debug support**: Backtrace capture in debug builds
//! - **Ergonomic API**: Minimal boilerplate with the [`define_error!`] macro
//!
//! # Quick Start
//!
//! ## Define Typed Errors
//!
//! ### Method 1: Using `define_errors!` (Recommended for Multiple Errors)
//!
//! Use the [`define_errors!`] macro to batch define multiple errors under the same code:
//!
//! ```
//! use lxy::{define_errors, error::ErrorCode};
//!
//! define_errors! {
//!     ErrorCode::ResourceNotFound => [
//!         UserNotFound,
//!         ProjectNotFound,
//!         FileNotFound,
//!     ],
//!     ErrorCode::InvalidInput => [
//!         InvalidEmail,
//!         InvalidPassword,
//!     ],
//! }
//! ```
//!
//! Each error type automatically implements `From<ErrorType> for Error`, allowing direct conversion:
//!
//! ```
//! # use lxy::{define_errors, error::{ErrorCode, Result}};
//! # define_errors! { ErrorCode::ResourceNotFound => [UserNotFound], }
//! fn find_user(id: u32) -> Result<String> {
//!     if id == 0 {
//!         return Err(UserNotFound.into());  // Direct conversion
//!     }
//!     Ok("found".to_string())
//! }
//! ```
//!
//! ### Method 2: Using `define_error!` (For Single Errors)
//!
//! Use the [`define_error!`] macro to define a single typed error:
//!
//! ```
//! use lxy::{define_error, error::ErrorCode};
//!
//! define_error!(ErrorCode::ResourceNotFound, UserNotFound);
//! define_error!(ErrorCode::InvalidInput, InvalidEmail);
//! ```
//!
//! ## Use in HTTP Handlers
//!
//! Errors automatically convert to JSON responses:
//!
//! ```
//! use lxy::{define_error, error::{Result, ErrorCode, TypedError}};
//! use axum::{Json, extract::Path};
//! use serde::Serialize;
//!
//! define_error!(ErrorCode::ResourceNotFound, UserNotFound);
//!
//! #[derive(Serialize)]
//! struct User { id: u32, name: String }
//!
//! async fn find_user(id: u32) -> Option<User> { None }
//!
//! async fn get_user(Path(id): Path<u32>) -> Result<Json<User>> {
//!     let user = find_user(id)
//!         .await
//!         .ok_or_else(|| UserNotFound::error(format!("User {} not found", id)))?;
//!     Ok(Json(user))
//! }
//! ```
//!
//! This returns an HTTP 404 response with JSON body:
//!
//! ```json
//! {
//!   "error": {
//!     "code": "ResourceNotFound",
//!     "type": "UserNotFound",
//!     "message": "User 123 not found",
//!     "data": null
//!   }
//! }
//! ```
//!
//! ## Use in gRPC Services
//!
//! Errors automatically convert to gRPC Status:
//!
//! ```
//! use lxy::{define_error, error::{ErrorCode, TypedError}};
//! use lxy::grpc::GrpcResult;
//! use tonic::{Request, Response};
//!
//! define_error!(ErrorCode::InvalidInput, InvalidRequest);
//!
//! struct HelloRequest { name: String }
//! struct HelloReply { message: String }
//!
//! async fn say_hello(request: Request<HelloRequest>) -> GrpcResult<Response<HelloReply>> {
//!     let name = request.into_inner().name;
//!     if name.is_empty() {
//!         return Err(InvalidRequest::error("Name is required").into());
//!     }
//!     Ok(Response::new(HelloReply {
//!         message: format!("Hello, {}", name),
//!     }))
//! }
//! ```
//!
//! ## Add Additional Data
//!
//! Attach structured data to errors:
//!
//! ```
//! use lxy::{define_error, error::{ErrorCode, TypedError}};
//! use serde_json::json;
//!
//! define_error!(ErrorCode::RateLimited, RateLimited);
//!
//! let error = RateLimited::error_with_data(
//!     "Too many requests",
//!     json!({
//!         "retry_after": 60,
//!         "limit": 100
//!     })
//! );
//! ```
//!
//! ## Converting from Standard Errors
//!
//! Use `Error::internal` to convert standard library errors:
//!
//! ```
//! use lxy::error::{Result, Error};
//! use std::fs;
//!
//! fn read_config() -> Result<String> {
//!     let content = fs::read_to_string("config.toml")
//!         .map_err(Error::internal)?;
//!     Ok(content)
//! }
//! ```
//!
//! ## Error Chaining with Source
//!
//! Preserve the original error when wrapping it in a higher-level error:
//!
//! ```
//! use lxy::{define_error, error::{ErrorCode, Result, TypedError}};
//! use std::fs;
//!
//! define_error!(ErrorCode::Internal, ConfigLoadFailed);
//!
//! fn load_config() -> Result<String> {
//!     fs::read_to_string("config.toml")
//!         .map_err(|e| ConfigLoadFailed::error_with_source(
//!             "Failed to load configuration file",
//!             e
//!         ))
//! }
//! ```
//!
//! The source error is preserved and can be accessed via `error.source()`.
//!
//! # Error Codes
//!
//! The framework provides these system-defined error codes:
//!
//! | Code | HTTP Status | gRPC Code | Usage |
//! |------|-------------|-----------|-------|
//! | [`ErrorCode::InvalidInput`] | 400 Bad Request | INVALID_ARGUMENT | Invalid or malformed input |
//! | [`ErrorCode::Unauthorized`] | 401 Unauthorized | UNAUTHENTICATED | Authentication required |
//! | [`ErrorCode::Forbidden`] | 403 Forbidden | PERMISSION_DENIED | Lacks permissions |
//! | [`ErrorCode::ResourceNotFound`] | 404 Not Found | NOT_FOUND | Resource not found |
//! | [`ErrorCode::Conflict`] | 409 Conflict | ALREADY_EXISTS | Resource conflict |
//! | [`ErrorCode::RateLimited`] | 429 Too Many Requests | RESOURCE_EXHAUSTED | Rate limit exceeded |
//! | [`ErrorCode::Internal`] | 500 Internal Server Error | INTERNAL | Internal error |
//! | [`ErrorCode::Unavailable`] | 503 Service Unavailable | UNAVAILABLE | Service unavailable |
//! | [`ErrorCode::Timeout`] | 504 Gateway Timeout | DEADLINE_EXCEEDED | Request timeout |

mod code;
mod core;
mod result;
mod typed;

pub use code::ErrorCode;
pub use core::Error;
pub use result::Result;
pub use typed::TypedError;

#[cfg(test)]
mod tests;