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}