1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
//! 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 |
pub use ErrorCode;
pub use Error;
pub use Result;
pub use TypedError;