these_macros_should_be_illegal/lib.rs
1//! Experimental procedural macros for compact local DSLs, recursive token
2//! rewriting, and transformations of macro structure.
3//!
4//! The main entry points are [`enum_fn`] for per-variant methods,
5//! [`discriminated_str`] for reversible string discriminants, [`strutuct`] for
6//! nested algebraic declarations, and [`expand`] for applying recursive syntax
7//! extensions to an out-of-line module. Every macro is independent; import only
8//! the syntax used by the caller.
9//!
10//! ```
11//! use these_macros_should_be_illegal::enum_fn;
12//!
13//! #[enum_fn(code: usize)]
14//! enum Status {
15//! Ready = 200,
16//! Missing,
17//! }
18//!
19//! assert_eq!(Status::Ready.code(), Some(200));
20//! assert_eq!(Status::Missing.code(), None);
21//! ```
22//!
23//! The [crate repository](https://github.com/AlexanderGolys/these-macros-should-be-illegal)
24//! contains the complete book and runnable examples.
25
26/// Whole-stream grammar extensions applied recursively before parsing.
27mod flust;
28/// Reusable plumbing for composing and configuring macros.
29mod helpers;
30/// Shape-aware local syntax extensions and replacement grammars.
31mod local;
32/// Macros which transform macro invocations and token-stream structure.
33mod meta;
34/// Small conveniences over otherwise ordinary Rust syntax.
35mod rast;
36
37use flust::{literally_literal_string_impl, shared_match_arms_impl};
38use helpers::{excluded_macros_impl, expand_impl, forward_attributes_impl};
39use local::{
40 StringCase, callable_impl, discriminated_str_impl, enum_fn_impl, make_fn_impl, stringify_case,
41 stringify_type_impl, strutuct_impl,
42};
43use meta::{perm_impl, reflect_impl};
44use proc_macro::TokenStream;
45use rast::qualify_common_impl;
46
47/// Rewrites literal strings in exactly the supplied token stream.
48///
49/// ```
50/// use these_macros_should_be_illegal::literally_literal_string;
51///
52/// let owned: String = literally_literal_string!(@@"hello");
53/// assert_eq!(owned, "hello");
54/// ```
55#[proc_macro]
56pub fn literally_literal_string(input: TokenStream) -> TokenStream {
57 literally_literal_string_impl(input.into()).into()
58}
59
60/// Clones a shared match-arm RHS for alternatives separated by `||`.
61///
62/// Parenthesize `(pattern if guard)` when one alternative has its own guard.
63///
64/// ```
65/// use these_macros_should_be_illegal::shared_match_arms;
66///
67/// enum Value {
68/// Number(u32),
69/// Character(char),
70/// }
71///
72/// let value = Value::Number(3);
73/// let text = shared_match_arms! {
74/// match value {
75/// Value::Number(value) || Value::Character(value) => value.to_string(),
76/// }
77/// };
78///
79/// assert_eq!(text, "3");
80/// ```
81#[proc_macro]
82pub fn shared_match_arms(input: TokenStream) -> TokenStream {
83 shared_match_arms_impl(input.into()).into()
84}
85
86/// Qualifies common unqualified standard-library types inside one Rust type.
87///
88/// Nested common types are qualified recursively, while an already qualified
89/// path is preserved deliberately.
90///
91/// ```
92/// use these_macros_should_be_illegal::qf;
93///
94/// let value: qf!(Option<Vec<String>>) = Some(vec![String::from("less punctuation")]);
95/// assert!(value.is_some());
96/// ```
97#[proc_macro]
98pub fn qf(input: TokenStream) -> TokenStream {
99 qualify_common_impl(input.into()).into()
100}
101
102/// Converts an identifier or string literal to `camelCase`.
103///
104/// ```
105/// use these_macros_should_be_illegal::stringify_camel_case;
106/// assert_eq!(stringify_camel_case!(some_HTTP_server), "someHttpServer");
107/// ```
108#[proc_macro]
109pub fn stringify_camel_case(input: TokenStream) -> TokenStream {
110 stringify_case(input.into(), StringCase::Camel).into()
111}
112
113/// Converts an identifier or string literal to `PascalCase`.
114///
115/// This case is also commonly called `UpperCamelCase`.
116///
117/// ```
118/// use these_macros_should_be_illegal::stringify_pascal_case;
119/// assert_eq!(stringify_pascal_case!(some_HTTP_server), "SomeHttpServer");
120/// ```
121#[proc_macro]
122pub fn stringify_pascal_case(input: TokenStream) -> TokenStream {
123 stringify_case(input.into(), StringCase::Pascal).into()
124}
125
126/// Converts an identifier or string literal to `snake_case`.
127///
128/// ```
129/// use these_macros_should_be_illegal::stringify_snake_case;
130/// assert_eq!(stringify_snake_case!(SomeHTTPServer), "some_http_server");
131/// ```
132#[proc_macro]
133pub fn stringify_snake_case(input: TokenStream) -> TokenStream {
134 stringify_case(input.into(), StringCase::Snake).into()
135}
136
137/// Converts an identifier or string literal to `kebab-case`.
138///
139/// ```
140/// use these_macros_should_be_illegal::stringify_kebab_case;
141/// assert_eq!(stringify_kebab_case!(SomeHTTPServer), "some-http-server");
142/// ```
143#[proc_macro]
144pub fn stringify_kebab_case(input: TokenStream) -> TokenStream {
145 stringify_case(input.into(), StringCase::Kebab).into()
146}
147
148/// Converts an identifier or string literal to `SCREAMING_SNAKE_CASE`.
149///
150/// ```
151/// use these_macros_should_be_illegal::stringify_screaming_snake_case;
152/// assert_eq!(stringify_screaming_snake_case!(SomeHTTPServer), "SOME_HTTP_SERVER");
153/// ```
154#[proc_macro]
155pub fn stringify_screaming_snake_case(input: TokenStream) -> TokenStream {
156 stringify_case(input.into(), StringCase::ScreamingSnake).into()
157}
158
159/// Converts an identifier or string literal to `lowercase` without separators.
160///
161/// ```
162/// use these_macros_should_be_illegal::stringify_lowercase;
163/// assert_eq!(stringify_lowercase!(Some_HTTP_Server), "some_http_server");
164/// ```
165#[proc_macro]
166pub fn stringify_lowercase(input: TokenStream) -> TokenStream {
167 stringify_case(input.into(), StringCase::Lower).into()
168}
169
170/// Converts an identifier or string literal to `UPPERCASE` without separators.
171///
172/// ```
173/// use these_macros_should_be_illegal::stringify_uppercase;
174/// assert_eq!(stringify_uppercase!(Some_HTTP_Server), "SOME_HTTP_SERVER");
175/// ```
176#[proc_macro]
177pub fn stringify_uppercase(input: TokenStream) -> TokenStream {
178 stringify_case(input.into(), StringCase::Upper).into()
179}
180
181/// Produces a canonical, collision-safe string name for one Rust type.
182///
183/// The result begins with `type:`, which is deliberately illegal in Rust
184/// identifiers. Formatting differences are removed while type structure is
185/// preserved: `& 'a mut Vec < Option < T > >` becomes
186/// `"type:&'a mut Vec<Option<T>>"`.
187///
188/// ```
189/// use these_macros_should_be_illegal::stringify_type;
190/// assert_eq!(
191/// stringify_type!(fn((u8, u16), *const [u8; 4]) -> bool),
192/// "type:fn((u8,u16),*const[u8;4])->bool",
193/// );
194/// ```
195#[proc_macro]
196pub fn stringify_type(input: TokenStream) -> TokenStream {
197 stringify_type_impl(input.into()).into()
198}
199
200/// Permutes comma-separated token trees using standard cycle notation.
201///
202/// Cycles compose from right to left and positions are one-based. Token trees
203/// beyond the largest mentioned position remain fixed. Spaces are required
204/// between positions because `14` is one integer token while `1 4` is two.
205///
206/// ```text
207/// perm! { ((1 4 3)), a, b, c, d, e }
208/// // expands to the token stream: c, b, d, a, e
209/// ```
210///
211/// An empty cycle product demonstrates the identity in an ordinary expression
212/// context; nontrivial products deliberately return a raw comma-separated
213/// stream for structural consumers.
214///
215/// ```
216/// use these_macros_should_be_illegal::perm;
217/// let fixed: &str = perm!((), "fixed");
218/// assert_eq!(fixed.len(), 5);
219/// ```
220#[proc_macro]
221pub fn perm(input: TokenStream) -> TokenStream {
222 perm_impl(input.into()).into()
223}
224
225/// Reflects two macro invocation objects around an opaque token-stream body.
226///
227/// `reflect!(first, second; body)` constructs
228/// `second! { first! { body } }`. Consequently `second` expands before
229/// `first`, reversing the expansion order of `first! { second! { body } }`.
230///
231/// ```
232/// use these_macros_should_be_illegal::reflect;
233/// macro_rules! add_one { ($value:expr) => { 1 + $value }; }
234/// macro_rules! double { ($value:expr) => { 2 * $value }; }
235/// assert_eq!(reflect!(add_one, double; 3), 8);
236/// ```
237#[proc_macro]
238pub fn reflect(input: TokenStream) -> TokenStream {
239 reflect_impl(input.into()).into()
240}
241
242#[doc = include_str!("../docs/callable.md")]
243#[proc_macro_attribute]
244pub fn callable(arguments: TokenStream, item: TokenStream) -> TokenStream {
245 callable_impl(arguments.into(), item.into()).into()
246}
247
248/// Creates a local value and a same-name macro providing function-like calls.
249///
250/// The value's type must expose the method generated by [`callable`]. The
251/// accepted syntax is `[mut] name = expression`.
252///
253/// ```
254/// use these_macros_should_be_illegal::{callable, make_fn};
255///
256/// #[callable(apply)]
257/// trait Action {
258/// fn apply(&self, point: usize) -> usize;
259/// }
260///
261/// struct Shift(usize);
262///
263/// impl Action for Shift {
264/// fn apply(&self, point: usize) -> usize {
265/// point + self.0
266/// }
267/// }
268///
269/// make_fn!(sigma = Shift(3));
270/// assert_eq!(sigma!(2), 5);
271/// ```
272#[proc_macro]
273pub fn make_fn(input: TokenStream) -> TokenStream {
274 make_fn_impl(input.into()).into()
275}
276
277#[doc = include_str!("../docs/discriminated_str.md")]
278#[proc_macro_attribute]
279pub fn discriminated_str(arguments: TokenStream, item: TokenStream) -> TokenStream {
280 discriminated_str_impl(arguments.into(), item.into()).into()
281}
282
283#[doc = include_str!("../docs/enum_fn.md")]
284#[proc_macro_attribute]
285pub fn enum_fn(arguments: TokenStream, item: TokenStream) -> TokenStream {
286 enum_fn_impl(arguments.into(), item.into()).into()
287}
288
289/// Prevents transformations from descending into the listed macro invocations.
290///
291/// ```
292/// use these_macros_should_be_illegal::{excluded_macros, literally_literal_string};
293/// macro_rules! raw_tokens { ($($tokens:tt)*) => { "borrowed" }; }
294///
295/// #[excluded_macros(raw_tokens)]
296/// literally_literal_string! {
297/// fn value() -> &'static str { raw_tokens!(@@"untouched") }
298/// }
299///
300/// fn main() {
301/// assert_eq!(value(), "borrowed");
302/// }
303/// ```
304#[proc_macro_attribute]
305pub fn excluded_macros(arguments: TokenStream, item: TokenStream) -> TokenStream {
306 excluded_macros_impl(arguments.into(), item.into()).into()
307}
308
309/// Loads an out-of-line module and injects function-like macros around its body.
310///
311/// The declaration points to a real source file, so this outline is ignored in
312/// rustdoc and exercised by the integration fixtures instead.
313///
314/// ```rust,ignore
315/// use these_macros_should_be_illegal::{expand, literally_literal_string};
316///
317/// expand!(literally_literal_string; mod experiments);
318/// // `experiments.rs` may now contain the extended token syntax.
319/// ```
320#[proc_macro]
321pub fn expand(input: TokenStream) -> TokenStream {
322 expand_impl(input.into()).into()
323}
324
325#[doc = include_str!("../docs/forward-attributes.md")]
326#[proc_macro_attribute]
327pub fn forward_attributes(arguments: TokenStream, item: TokenStream) -> TokenStream {
328 forward_attributes_impl(arguments.into(), item.into()).into()
329}
330
331#[doc = include_str!("../docs/strutuct.md")]
332#[proc_macro]
333pub fn strutuct(input: TokenStream) -> TokenStream {
334 strutuct_impl(input.into()).into()
335}
336
337/// Expands exactly like [`strutuct!`](strutuct).
338///
339/// ```
340/// use these_macros_should_be_illegal::emmun;
341/// emmun! { State { Ready, Waiting } }
342/// assert!(matches!(State::Ready, State::Ready));
343/// ```
344#[proc_macro]
345pub fn emmun(input: TokenStream) -> TokenStream {
346 strutuct_impl(input.into()).into()
347}