compose_idents/
lib.rs

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