compose_idents/
lib.rs

1#![allow(clippy::needless_doctest_main)]
2#![doc = include_str!("../README.md")]
3
4mod core;
5mod eval;
6mod funcs;
7mod parse;
8
9use crate::core::{ComposeIdentsArgs, ComposeIdentsVisitor, State};
10use proc_macro::TokenStream;
11use quote::quote;
12use std::sync::Mutex;
13use syn::{parse_macro_input, visit_mut::VisitMut};
14
15static COUNTER: Mutex<u64> = Mutex::new(0);
16
17/// Compose identifiers from the provided parts and replace their aliases in the code block.
18///
19/// In addition to replacing identifier aliases it replaces tokens like `%alias%` in string
20/// literals (including in doc-attributes).
21///
22/// # Syntax
23///
24/// ```rust,ignore
25/// use compose_idents::compose_idents;
26///
27/// compose_idents!(
28///     // Alias is defined one or more arguments that are supposed to be concatenated
29///     alias1 = [part1, part2],
30///     // Multiple aliases could be defined
31///     // and they could be composed from arbitrary number of arguments
32///     // Which could be identifiers, strings, numbers or underscores
33///     alias2 = [part3, _, "part4", _, 1],
34///     // Functions could applied to the arguments, calls to functions could be nested
35///     alias3 = [some_func(part5), outer_func(inner_func(part6))],
36///     // ... more aliases
37///     {
38///         // Code block that uses aliases as identifiers
39///         // The aliases will be replaced with their replacements when the code is expanded
40///         let alias1 = 42;
41///
42///         fn alias2() -> u32 { 42 }
43///
44///         // Aliases could be also used for string-formatting using %alias% syntax
45///         #[doc = "Documentation for %alias3%"]
46///         fn alias3() -> u32 { 42 }
47///     },
48/// );
49/// ```
50///
51/// Semicolons could also be used as separators between the macro arguments for
52/// backwards-compatibility. Mixing separator styles in the same macro invocation is not allowed.
53///
54/// # Example
55///
56/// ```rust
57#[doc = include_str!("../snippets/usage.rs")]
58/// ```
59#[proc_macro]
60pub fn compose_idents(input: TokenStream) -> TokenStream {
61    let mut counter = COUNTER.lock().unwrap();
62    *counter += 1;
63    let state = State { seed: *counter };
64    let args = parse_macro_input!(input as ComposeIdentsArgs);
65    let mut visitor = ComposeIdentsVisitor {
66        replacements: args.spec.replacements(&state),
67    };
68    let mut block = args.block;
69    visitor.visit_block_mut(&mut block);
70
71    let block_content = block.stmts;
72
73    let expanded = quote! { #(#block_content)* };
74    TokenStream::from(expanded)
75}