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
//! Thin proc-macro bridge for [`rorpc_parse`].
//!
//! This crate contains only proc-macro entry points. All parsing, validation,
//! and code generation logic lives in `rorpc-parse` where it can be tested
//! with normal `#[test]` functions.
use TokenStream;
use parse_macro_input;
/// Annotate a plain Axum handler to register its metadata with rorpc.
///
/// The function is left completely unchanged — it remains a valid Axum handler.
/// Two `inventory::submit!` calls are added alongside it:
/// one for [`rorpc::HandlerMetadata`] (used by contract generation) and one for
/// [`rorpc::HandlerRegistration`] (used by [`router!`]).
///
/// # Syntax
///
/// ```rust,ignore
/// #[orpc(method = "POST", path = "/planet/list")]
/// async fn list_planets(State(db): State<Db>) -> Json<Vec<Planet>> {
/// Json(db.list().await)
/// }
/// ```
///
/// # Arguments
///
/// - `method` — HTTP method string (`"GET"`, `"post"`, etc.), normalised to uppercase. Required.
/// - `path` — Route path string (e.g. `"/planet/list"`). Required.
/// - `stream_event` — Type path for the SSE event type for streaming handlers (e.g. `StreamEvent`). Optional.
/// Auto-discovery router macro with optional module path filtering.
///
/// Discovers all `#[rorpc]`-annotated handlers via the `inventory` crate and
/// builds an Axum `Router`. Accepts an optional state expression and/or a
/// module path pattern to restrict which handlers are included.
///
/// # Syntax
///
/// ```text
/// router!() // all handlers, no state
/// router!(state) // all handlers, with state
/// router!("pattern") // filtered, no state
/// router!("pattern", state) // filtered + state (any order)
/// router!(state, "pattern") // filtered + state (any order)
/// router!(["pat1", "pat2"]) // multiple patterns
/// router!("prefix::{a,b}") // brace expansion
/// router!("prefix::*") // wildcard
/// ```
///
/// # Pattern matching
///
/// Patterns match against the handler's `module_path!()` value:
/// - `"handlers::planet"` — exact module or any child
/// - `"handlers::*"` — all direct and nested children of `handlers::`
/// - `"handlers::{planet,user}"` — brace expansion
/// - `["handlers::planet", "api::v1"]` — explicit list
/// Derive macro that generates a `fn zod_ts() -> String` method on structs and enums.
///
/// The generated method returns a complete TypeScript block with a Zod schema
/// and a `z.infer` type alias. An `inventory::submit!` call registers the real
/// schema so contract generation prefers it over the `z.unknown()` fallback
/// emitted by `#[rorpc]`.
///
/// # Example
///
/// ```rust,ignore
/// #[derive(Serialize, Deserialize, ZodTs)]
/// pub struct Planet {
/// pub id: i32,
/// #[zod(min_length(1), max_length(100))]
/// pub name: String,
/// pub description: Option<String>,
/// }
/// ```
///
/// # Supported `#[zod(...)]` field attributes
///
/// **Strings:** `min_length(n)`, `max_length(n)`, `length(n)`, `email`, `url`,
/// `regex("pattern")`, `starts_with("s")`, `ends_with("s")`, `includes("s")`
///
/// **Numbers:** `min(n)`, `max(n)`, `int`, `positive`, `negative`,
/// `nonnegative`, `nonpositive`, `finite`
///
/// **Arrays (`Vec<T>`):** `min_length(n)`, `max_length(n)`, `length(n)`
/// Derive macro for registering error enum variants with rorpc.
///
/// Annotate an error enum so `generate_contract()` can emit TypeScript
/// `.errors({...})` entries. Variant names are converted to `SCREAMING_SNAKE_CASE`.
///
/// # Example
///
/// ```rust,ignore
/// #[derive(OrpcError)]
/// pub enum AppError {
/// NotFound,
/// Conflict { reason: String },
/// DatabaseError(String),
/// }
/// ```
///
/// # Variant mapping
///
/// - Unit variants: `NotFound` → `NOT_FOUND: {}`
/// - Struct variants: `Conflict { reason: String }` → `CONFLICT: { data: z.object({...}) }`
/// - Tuple variants: `DatabaseError(String)` → `DATABASE_ERROR: { data: z.string() }`