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}