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}