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, underscores or just arbitrary token
33///     // sequences
34///     alias2 = [part3, _, "part4", _, 1],
35///     // Functions could applied to the arguments, calls to functions could be nested
36///     alias3 = [some_func(part5), outer_func(inner_func(part6))],
37///     // ... more aliases
38///     {
39///         // Code block that uses aliases as identifiers
40///         // The aliases will be replaced with their replacements when the code is expanded
41///         let alias1 = 42;
42///
43///         fn alias2() -> u32 { 42 }
44///
45///         // Aliases could be also used for string-formatting using %alias% syntax
46///         #[doc = "Documentation for %alias3%"]
47///         fn alias3() -> u32 { 42 }
48///     },
49/// );
50/// ```
51///
52/// Semicolons could also be used as separators between the macro arguments for
53/// backwards-compatibility. Mixing separator styles in the same macro invocation is not allowed.
54///
55/// # Example
56///
57/// ```rust
58#[doc = include_str!("../snippets/usage.rs")]
59/// ```
60#[proc_macro]
61pub fn compose_idents(input: TokenStream) -> TokenStream {
62    let mut counter = COUNTER.lock().unwrap();
63    *counter += 1;
64    let state = State { seed: *counter };
65    let args = parse_macro_input!(input as ComposeIdentsArgs);
66    let mut visitor = ComposeIdentsVisitor {
67        replacements: args.spec.replacements(&state),
68    };
69    let mut block = args.block;
70    visitor.visit_block_mut(&mut block);
71
72    let block_content = block.stmts;
73
74    let expanded = quote! { #(#block_content)* };
75    TokenStream::from(expanded)
76}