compose_idents/
lib.rs

1#![allow(clippy::needless_doctest_main)]
2#![doc = include_str!("../snippets/docs.md")]
3
4mod ast;
5mod core;
6mod deprecation;
7mod error;
8mod eval;
9mod funcs;
10mod interpreter;
11mod parse;
12mod resolve;
13mod unique_id;
14
15use crate::ast::ComposeIdentsArgs;
16use crate::deprecation::DeprecationService;
17use crate::interpreter::Interpreter;
18use proc_macro::TokenStream;
19use std::convert::TryInto;
20use syn::parse_macro_input;
21
22/// Compose identifiers from the provided parts and replace their aliases in the code block.
23///
24/// In addition to replacing identifier aliases it replaces tokens like `%alias%` in string
25/// literals (including in doc-attributes).
26///
27/// # Syntax
28///
29/// ```rust,ignore
30/// use compose_idents::compose_idents;
31///
32/// compose_idents!(
33///     // Alias is defined as an expression that could be a value or a function call that evaluates
34///     // in a valid identifier.
35///     alias1 = concat(foo, _, bar),
36///     alias2 = foo,
37///     // Function calls could be nested.
38///     alias3 = upper(snake_case(fooBarBaz)),
39///     // Values could be idents, string or integer literals, underscores or free-form tokens.
40///     alias4 = concat(foo, _, "bar", _, 42),
41///     // Values could be converted to valid identifiers using `normalize()` function.
42///     alias5 = concat(my, _, fn, _, normalize(My::Enum)),
43///     // Bracket-based legacy syntax is still supported but deprecated.
44///     alias6 = [part3, _, "part4", _, 1],
45///     // ... more alias definitions
46///     {
47///         // Code block that uses aliases as identifiers
48///         // The aliases will be replaced with their replacements when the code is expanded
49///         let alias1 = 42;
50///
51///         fn alias2() -> u32 { 42 }
52///
53///         // Aliases could be also used for string-formatting using %alias% syntax
54///         #[doc = "Documentation for %alias3%"]
55///         fn alias3() -> u32 { 42 }
56///     },
57/// );
58/// ```
59///
60/// Semicolons could also be used as separators between the macro arguments for
61/// backwards-compatibility. Mixing separator styles in the same macro invocation is not allowed.
62///
63/// # Reference
64///
65#[doc = include_str!("../snippets/reference_h2.md")]
66#[proc_macro]
67pub fn compose_idents(input: TokenStream) -> TokenStream {
68    let deprecation_service = DeprecationService::scoped();
69    let args = parse_macro_input!(input as ComposeIdentsArgs);
70    let interpreter = Interpreter::new(args, deprecation_service);
71    match interpreter.execute() {
72        Ok(ts) => ts.into(),
73        Err(err) => {
74            let syn_err: syn::Error = err.try_into().unwrap_or_else(|_| {
75                syn::Error::new(proc_macro2::Span::call_site(), "Unknown error")
76            });
77            TokenStream::from(syn_err.into_compile_error())
78        }
79    }
80}