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
207
208
209
//! Tower HTTP middleware for automatic error response rendering with content negotiation.
//!
//! This module provides a [Tower](https://github.com/tower-rs/tower) middleware layer that
//! automatically catches errors from your HTTP service and renders them in the format requested
//! by the client's `Accept` header.
//!
//! # Features
//!
//! - **Content Negotiation**: Automatically selects the best error format based on the `Accept` header
//! - **Multiple Formats**: Supports JSON, GraphQL, JSON-RPC, HTML, and plain text error responses
//! - **Quality Values**: Respects `q` values in Accept headers for format prioritization
//! - **Wildcard Matching**: Handles `*/*`, `text/*`, and `application/*` Accept patterns
//! - **Customizable**: Configure custom media type mappings and fallback renderers
//! - **Type-Safe**: Leverages the error registry for type-safe error rendering
//!
//! # Quick Start
//!
//! ```rust
//! use apollo_errors::tower_http::ErrorLayer;
//! use tower::ServiceBuilder;
//! # use tower::service_fn;
//! # use http::{Request, Response};
//!
//! let service = ServiceBuilder::new()
//! .layer(ErrorLayer::new())
//! .service(service_fn(|_req: Request<String>| async {
//! // Your handler that might return errors
//! Ok::<_, Box<dyn std::error::Error>>(Response::new("OK"))
//! }));
//! ```
//!
//! # Content Negotiation
//!
//! The middleware examines the `Accept` header to determine the best format for error responses:
//!
//! | Accept Header | Format | Content-Type |
//! |--------------|--------|--------------|
//! | `application/json` | JSON | `application/json` |
//! | `text/html` | HTML | `text/html` |
//! | `application/graphql-response+json` | GraphQL | `application/graphql-response+json` |
//! | `application/json-rpc` | JSON-RPC | `application/json-rpc` |
//! | `text/plain` | Plain text | `text/plain` |
//! | `*/*` or missing | JSON (default) | `application/json` |
//!
//! ## Quality Values
//!
//! The middleware respects quality values (`q` parameters) in Accept headers:
//!
//! ```text
//! Accept: text/html;q=0.9, application/json;q=0.8
//! → Returns HTML (higher quality)
//!
//! Accept: application/json, text/html;q=0.5
//! → Returns JSON (equal quality, JSON is more specific)
//! ```
//!
//! ## Wildcards
//!
//! Wildcard patterns are supported and follow HTTP content negotiation rules:
//!
//! ```text
//! Accept: text/*
//! → Matches text/html or text/plain
//!
//! Accept: application/*
//! → Matches application/json or application/graphql-response+json
//!
//! Accept: */*
//! → Matches any format (falls back to default)
//! ```
//!
//! # Custom Configuration
//!
//! You can customize the content negotiation behavior:
//!
//! ```rust
//! use apollo_errors::tower_http::{ErrorLayer, NegotiationConfig, Renderer};
//! use tower::ServiceBuilder;
//! # use tower::service_fn;
//! # use http::{Request, Response};
//!
//! let config = NegotiationConfig::new()
//! .with_mapping("application/vnd.api+json", Renderer::Json)
//! .with_fallback(Renderer::Text);
//!
//! let service = ServiceBuilder::new()
//! .layer(ErrorLayer::with_config(config))
//! .service(service_fn(|_req: Request<String>| async {
//! Ok::<_, Box<dyn std::error::Error>>(Response::new("OK"))
//! }));
//! ```
//!
//! # Integration Example
//!
//! Complete example showing error handling with custom error types:
//!
//! ```rust
//! use apollo_errors::{Error, miette::Diagnostic, tower_http::ErrorLayer};
//! use tower::ServiceBuilder;
//! use http::{Request, Response, StatusCode};
//! # use tower::service_fn;
//!
//! #[derive(Debug, Error, Diagnostic)]
//! pub enum ApiError {
//! #[error("Resource not found: {resource}")]
//! #[diagnostic(code(api::not_found))]
//! #[http_status(404)]
//! NotFound {
//! #[extension]
//! resource: String,
//! },
//!
//! #[error("Invalid request: {reason}")]
//! #[diagnostic(code(api::bad_request))]
//! #[http_status(400)]
//! BadRequest {
//! #[extension]
//! reason: String,
//! },
//! }
//!
//! async fn handler(_req: Request<String>) -> Result<Response<String>, ApiError> {
//! Err(ApiError::NotFound {
//! resource: "user/123".to_string(),
//! })
//! }
//!
//! # async fn example() {
//! let service = ServiceBuilder::new()
//! .layer(ErrorLayer::new())
//! .service_fn(handler);
//! # }
//! ```
//!
//! The middleware will:
//! 1. Catch the `ApiError::NotFound` error
//! 2. Check the `Accept` header from the request
//! 3. Render the error in the requested format (JSON/GraphQL/HTML/Text)
//! 4. Set the appropriate `Content-Type` header
//! 5. Set the HTTP status code from the error (404 in this case)
//!
//! # Error Response Format
//!
//! ## JSON Format
//!
//! ```json
//! {
//! "errors": [{
//! "message": "Resource not found: user/123",
//! "extensions": {
//! "code": "api::not_found",
//! "resource": "user/123"
//! }
//! }]
//! }
//! ```
//!
//! ## GraphQL Format
//!
//! ```json
//! {
//! "errors": [{
//! "message": "Resource not found: user/123",
//! "extensions": {
//! "code": "api::not_found",
//! "resource": "user/123"
//! }
//! }]
//! }
//! ```
//!
//! ## HTML Format
//!
//! ```html
//! <!DOCTYPE html>
//! <html>
//! <head><title>Error: api::not_found</title></head>
//! <body>
//! <h1>api::not_found</h1>
//! <p>Resource not found: user/123</p>
//! <h2>Details</h2>
//! <dl>
//! <dt>resource</dt>
//! <dd>user/123</dd>
//! </dl>
//! </body>
//! </html>
//! ```
//!
//! ## Text Format
//!
//! ```text
//! Error: api::not_found
//! Resource not found: user/123
//!
//! Details:
//! resource: user/123
//! ```
pub use ErrorBody;
pub use ErrorLayer;
pub use ;
pub use ErrorService;