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
//! Procedural macros for declaring and defunctionalizing DD programs.
//!
//! This crate is the shared syntax layer for DD extension, HTTP, and JSON-RPC programs.
//! Its public surface stays small: parsing, validation, and code generation
//! live in private modules, while this facade contains only procedural-macro
//! entry points. Product crates re-export the macros they own.
//!
//! Every example below is illustrative rather than tested. Expansion names `::alux_ext`,
//! `::alux_http`, and `::alux_jsonrpc`, and a procedural-macro crate cannot depend on the crates its
//! own expansion targets. The executed examples live in those crates' documentation.
use ext_internal;
use http_program_defunc_internal;
use jsonrpc_program_defunc_internal;
use TokenStream;
/// Declares extension methods and optionally gives each method a first-order operation type.
///
/// Arguments unrelated to defunctionalization are forwarded to `extend::ext`.
/// Plain `defunc` produces a hidden `*Operation<Context>` type implementing
/// `OperationAlg` and `ApplyAlg`. When every method already builds first-order
/// route syntax from `routes()`, plain `defunc` names those inferred programs
/// directly. A backend such as `defunc(via = http)` or `defunc(via = jsonrpc)`
/// additionally lifts convenient method references while producing named programs
/// and their interpreter evidence.
///
/// ```ignore
/// use alux_ext::ext;
///
/// #[ext(name = CounterExt, defunc)]
/// impl<This> This
/// where
/// This: CounterAlg,
/// {
/// async fn incremented(&self, by: u32) -> u32 {
/// self.increment(by).await
/// }
/// }
///
/// // The expansion also defines `IncrementedOperation<This>`.
/// ```
///
/// ```ignore
/// use alux_ext::ext;
/// use alux_http::http;
///
/// #[ext(name = StatusRoutesExt, defunc(via = http))]
/// impl<This> This
/// where
/// This: HttpApiAlg + JsonOutAlg,
/// {
/// fn status_routes<Alg>(&self)
/// where
/// Alg: StatusAlg,
/// {
/// self.routes().get("/status", self.op(Alg::status_current).json())
/// }
/// }
///
/// // The expansion also defines `StatusRoutesProgram<Alg>`.
/// ```
/// Lowers extension methods into named, composable HTTP programs.
///
/// This is the HTTP backend that `alux_ext::ext(..., defunc(via = http))` selects. `via = http`
/// resolves the name in the authoring scope, so a declaration imports it as `use alux_http::http`.
/// Applying the attribute directly means the same thing.
///
/// Each method becomes a first-order HTTP program. A route handler written with `op(...)` is replaced
/// by the operation type generated by `alux_ext::ext(defunc)`, the declared input roles and output
/// kind become bounds on the interpreter, and a call to another method of the same extension becomes
/// a nested program.
///
/// ```ignore
/// use alux_ext::ext;
/// use alux_http::{HttpApiAlg, JsonOutAlg, http};
///
/// #[ext(name = StatusApiExt, defunc(via = http))]
/// impl<This> This
/// where
/// This: HttpApiAlg + JsonOutAlg,
/// {
/// fn status_api<Alg>(&self)
/// where
/// Alg: StatusAlg,
/// {
/// self.routes().get("/status", self.op(Alg::status_current).json())
/// }
/// }
///
/// // The expansion also defines `StatusApiProgram<Alg>`.
/// ```
/// Lowers extension methods into named, composable JSON-RPC programs.
///
/// This is the JSON-RPC backend that `alux_ext::ext(..., defunc(via = jsonrpc))` selects. `via = jsonrpc`
/// resolves the name in the authoring scope, so a declaration imports it as `use alux_jsonrpc::jsonrpc`.
/// Applying the attribute directly means the same thing.
///
/// Each method becomes a first-order JSON-RPC program. A method handler written with `op(...)` is
/// replaced by the operation type generated by `alux_ext::ext(defunc)`, the operation's argument
/// product and output become bounds on the interpreter, `.named()` selects object decoding using the
/// authored argument names, and a call to another method of the same extension becomes a nested
/// program.
///
/// ```ignore
/// use alux_ext::ext;
/// use alux_jsonrpc::{JsonRpcApiAlg, jsonrpc};
///
/// #[ext(name = StatusRpcExt, defunc(via = jsonrpc))]
/// impl<This> This
/// where
/// This: JsonRpcApiAlg,
/// {
/// fn status_rpc<Alg>(&self)
/// where
/// Alg: StatusAlg,
/// {
/// self.methods().method("status_current", self.op(Alg::status_current))
/// }
/// }
///
/// // The expansion also defines `StatusRpcProgram<Alg>`.
/// ```