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
//! Procedural macros for `aerro`. See [`aerro`](https://docs.rs/aerro) for
//! the full usage guide.
/// Derive the [`aerro::Aerro`](https://docs.rs/aerro/latest/aerro/trait.Aerro.html)
/// trait for an error enum.
///
/// # Attributes on each variant — `#[aerro(...)]`
///
/// | Key | Type | Required | Description |
/// |-----|------|----------|-------------|
/// | `category` | ident | ✓ | Error bucket: `Business`, `System`, `Auth`, `NotFound`, `RateLimit`, `Validation`, `Unavailable`, `Unknown` |
/// | `code` | ident | ✓ | gRPC status code: `Ok`, `Cancelled`, `Unknown`, `InvalidArgument`, `NotFound`, `AlreadyExists`, `PermissionDenied`, `ResourceExhausted`, `FailedPrecondition`, `Aborted`, `OutOfRange`, `Unimplemented`, `Internal`, `Unavailable`, `DataLoss`, `Unauthenticated` |
/// | `error` | string literal | ✓ | Display format; `{field_name}` interpolates variant fields |
/// | `exposure` | ident | — | Override default exposure: `Internal`, `Trusted`, `Public` (default derived from `category`) |
///
/// # Field-level attributes
///
/// | Attribute | Description |
/// |-----------|-------------|
/// | `#[source]` | Marks this field as the `std::error::Error::source()` |
/// | `#[from]` | Generates a `From<FieldType>` impl (field must be the only field) |
///
/// # Example
///
/// ```rust,ignore
/// #[derive(Debug, aerro::Aerro)]
/// pub enum CreateUserError {
/// #[aerro(category = Business, code = AlreadyExists, error = "email already taken: {email}")]
/// EmailTaken { email: String },
///
/// #[aerro(category = System, code = Internal, error = "db.unavailable")]
/// DbUnavailable,
/// }
/// ```
/// Derive the `AerroHandler` metadata trait for a unit struct, and generate a
/// `call_tonic` method that encodes typed errors into `tonic::Status`.
///
/// The struct must also implement
/// [`aerro::Handler`](https://docs.rs/aerro/latest/aerro/handler/trait.Handler.html).
///
/// # Attributes on the struct — `#[aerro(...)]`
///
/// | Key | Type | Required | Description |
/// |-----|------|----------|-------------|
/// | `service` | string literal | ✓ | Service name injected into each call [`Frame`](https://docs.rs/aerro/latest/aerro/frame/struct.Frame.html) |
/// | `rpc` | string literal | ✓ | RPC name injected into each call `Frame` |
/// | `exposure` | ident | ✓ | Egress exposure tier: `Internal`, `Trusted`, or `Public` |
/// | `max_frames` | integer | — | Frame cap for this handler (default: 16) |
///
/// # Generated items
///
/// - Constants: `SERVICE`, `RPC`, `EXPOSURE`, `MAX_FRAMES`
/// - Method: `async fn call_tonic(&self, req: Request) -> Result<Response, tonic::Status>`
///
/// # Example
///
/// ```rust,ignore
/// use aerro::{AerroHandler, Handler};
///
/// #[derive(Debug, aerro::Aerro)]
/// pub enum CreateUserError {
/// #[aerro(category = Business, code = AlreadyExists, error = "email already taken: {email}")]
/// EmailTaken { email: String },
/// }
///
/// #[derive(aerro::AerroHandler)]
/// #[aerro(service = "users", rpc = "create_user", exposure = Public, max_frames = 4)]
/// struct CreateUserHandler;
///
/// impl Handler for CreateUserHandler {
/// type Request = String;
/// type Response = String;
/// type Error = CreateUserError;
///
/// async fn handle(&self, email: String) -> Result<String, CreateUserError> {
/// Ok(format!("created {email}"))
/// }
/// }
/// ```