66![ MSRV] ( https://img.shields.io/badge/MSRV-1.89-blue )
77![ License] ( https://img.shields.io/badge/License-MIT%20or%20Apache--2.0-informational )
88
9- Small, pragmatic error model for API-heavy Rust services. Core is framework-agnostic; integrations are opt-in via feature flags. Stable categories, conservative HTTP mapping, no ` unsafe ` .
9+ Small, pragmatic error model for API-heavy Rust services.
10+ Core is framework-agnostic; integrations are opt-in via feature flags.
11+ Stable categories, conservative HTTP mapping, no ` unsafe ` .
1012
1113- Core types: ` AppError ` , ` AppErrorKind ` , ` AppResult ` , ` ErrorResponse `
12- - Optional Axum ` IntoResponse `
14+ - Optional Axum/Actix integration
1315- Optional OpenAPI schema (via ` utoipa ` )
1416- Conversions from ` sqlx ` , ` reqwest ` , ` redis ` , ` validator ` , ` config ` , ` tokio `
1517
@@ -21,6 +23,7 @@ Small, pragmatic error model for API-heavy Rust services. Core is framework-agno
2123- ** Framework-agnostic core.** No web framework assumptions. No ` unsafe ` . MSRV pinned. Works in libraries and binaries alike.
2224- ** Opt-in integrations.** Zero default features. You pull only what you need:
2325 - ` axum ` (HTTP ` IntoResponse ` )
26+ - ` actix ` (ready-to-use integration)
2427 - ` serde_json ` (JSON details)
2528 - ` openapi ` (schemas via ` utoipa ` )
2629 - ` sqlx ` , ` reqwest ` , ` redis ` , ` validator ` , ` config ` , ` tokio ` , ` multipart ` (error conversions)
@@ -36,12 +39,12 @@ Small, pragmatic error model for API-heavy Rust services. Core is framework-agno
3639``` toml
3740[dependencies ]
3841# lean core, no extra deps
39- masterror = { version = " 0.1 " , default-features = false }
42+ masterror = { version = " 0.2 " , default-features = false }
4043
4144# Or with features:
42- # JSON + Axum + common integrations
43- # masterror = { version = "0.1 ", features = [
44- # "axum", "serde_json", "openapi",
45+ # JSON + Axum/Actix + common integrations
46+ # masterror = { version = "0.2 ", features = [
47+ # "axum", "actix", " serde_json", "openapi",
4548# "sqlx", "reqwest", "redis", "validator", "config", "tokio"
4649# ] }
4750```
@@ -87,7 +90,11 @@ let resp: ErrorResponse = (&app_err).into();
8790assert_eq! (resp . status, 404 );
8891```
8992
90- ### Axum integration
93+ ---
94+
95+ ## Web framework integrations
96+
97+ ### Axum
9198
9299Enable ` axum ` (and usually ` serde_json ` ) to return errors directly from handlers:
93100
@@ -103,43 +110,35 @@ async fn handler() -> AppResult<&'static str> {
103110let app = Router :: new (). route (" /demo" , get (handler ));
104111```
105112
106- > Note: for JSON bodies you typically need both ` axum ` and ` serde_json ` features enabled.
113+ ### Actix
107114
108- ### Actix integration
109-
110- Enable ` actix ` (и обычно ` serde_json ` ) чтобы возвращать ошибки и готовый ` ErrorResponse ` :
115+ Enable ` actix ` (and usually ` serde_json ` ) to return errors directly from handlers:
111116
112117``` rust
113118// requires: features = ["actix", "serde_json"]
114119use actix_web :: {get, App , HttpServer , Responder };
115- use masterror :: { AppError , AppErrorKind , ErrorResponse } ;
120+ use masterror :: prelude :: * ;
116121
117122#[get(" /err" )]
118- async fn err () -> Result <& 'static str , AppError > {
119- Err (AppError :: new ( AppErrorKind :: Forbidden , " No access" ))
123+ async fn err () -> AppResult <& 'static str > {
124+ Err (AppError :: forbidden ( " No access" ))
120125}
121126
122127#[get(" /payload" )]
123128async fn payload () -> impl Responder {
124129 ErrorResponse :: new (422 , " Validation failed" )
125130}
126-
127- # #[actix_web:: main]
128- # async fn main () -> std :: io :: Result <()> {
129- # HttpServer :: new (|| App :: new (). service (err ). service (payload ))
130- # . bind ((" 127.0.0.1" , 8080 ))?
131- # . run ()
132- # . await
133- # }
134131```
135132
136- ### OpenAPI
133+ ---
134+
135+ ## OpenAPI
137136
138137Enable ` openapi ` to derive an OpenAPI schema for ` ErrorResponse ` (via ` utoipa ` ).
139138
140139``` toml
141140[dependencies ]
142- masterror = { version = " 0.1 " , features = [" openapi" , " serde_json" ] }
141+ masterror = { version = " 0.2 " , features = [" openapi" , " serde_json" ] }
143142utoipa = " 5"
144143```
145144
@@ -148,6 +147,7 @@ utoipa = "5"
148147## Feature flags
149148
150149- ` axum ` — ` IntoResponse ` for ` AppError ` and JSON responses
150+ - ` actix ` — ` ResponseError ` /` Responder ` integration
151151- ` openapi ` — schema for ` ErrorResponse ` via ` utoipa `
152152- ` serde_json ` — JSON details support
153153- ` sqlx ` — ` From<sqlx::Error> `
@@ -156,62 +156,72 @@ utoipa = "5"
156156- ` config ` — ` From<config::ConfigError> `
157157- ` tokio ` — ` From<tokio::time::error::Elapsed> `
158158- ` reqwest ` — ` From<reqwest::Error> `
159- - ` multipart ` — compatibility flag for projects using multipart in Axum
159+ - ` multipart ` — compatibility flag for projects using multipart in Axum
160160
161161---
162162
163163## Conversions
164164
165165All mappings are conservative and avoid leaking internals:
166166
167- - ` std::io::Error ` → ` Internal `
168- - ` String ` → ` BadRequest `
169- - ` sqlx::Error ` → ` NotFound ` (for ` RowNotFound ` ) or ` Database `
170- - ` redis::RedisError ` → ` Service `
171- - ` reqwest::Error ` → ` Timeout ` / ` Network ` / ` ExternalApi `
172- - ` validator::ValidationErrors ` → ` Validation `
173- - ` config::ConfigError ` → ` Config `
174- - ` tokio::time::error::Elapsed ` → ` Timeout `
167+ - ` std::io::Error ` → ` Internal `
168+ - ` String ` → ` BadRequest `
169+ - ` sqlx::Error ` → ` NotFound ` (for ` RowNotFound ` ) or ` Database `
170+ - ` redis::RedisError ` → ` Service `
171+ - ` reqwest::Error ` → ` Timeout ` / ` Network ` / ` ExternalApi `
172+ - ` validator::ValidationErrors ` → ` Validation `
173+ - ` config::ConfigError ` → ` Config `
174+ - ` tokio::time::error::Elapsed ` → ` Timeout `
175175
176176---
177177
178178## Typical setups
179179
180180Minimal core:
181+
181182``` toml
182- masterror = { version = " 0.1 " , default-features = false }
183+ masterror = { version = " 0.2 " , default-features = false }
183184```
184185
185186API service (Axum + JSON + common deps):
187+
186188``` toml
187- masterror = { version = " 0.1 " , features = [
189+ masterror = { version = " 0.2 " , features = [
188190 " axum" , " serde_json" , " openapi" ,
189191 " sqlx" , " reqwest" , " redis" , " validator" , " config" , " tokio"
190192] }
191193```
192194
195+ API service (Actix + JSON + common deps):
196+
197+ ``` toml
198+ masterror = { version = " 0.2" , features = [
199+ " actix" , " serde_json" , " openapi" ,
200+ " sqlx" , " reqwest" , " redis" , " validator" , " config" , " tokio"
201+ ] }
202+ ```
203+
193204---
194205
195206## Versioning and MSRV
196207
197208- Semantic versioning. Breaking API or wire-contract changes bump the major version.
198- - MSRV: 1.89 (may be raised in a ** minor** release with a changelog note, never in a patch).
209+ - MSRV: 1.89 (may be raised in a ** minor** release with a changelog note, never in a patch).
199210
200211---
201212
202213## Non-goals
203214
204215- Not a general-purpose error aggregator like ` anyhow ` for CLIs.
205- - Not a replacement for your domain errors. Use it as the public API surface and transport mapping.
216+ - Not a replacement for your domain errors. Use it as the public API surface and transport mapping.
206217
207218---
208219
209220## License
210221
211222Licensed under either of
212223
213- - Apache License, Version 2.0
214- - MIT license
224+ - Apache License, Version 2.0
225+ - MIT license
215226
216227at your option.
217-
0 commit comments