Macro sbor_derive::eager_replace
source · eager_replace!() { /* proc-macro */ }Expand description
NOTE: This should probably be moved out of sbor to its own crate.
This macro is a powerful but simple general-purpose tool to ease building declarative macros which create new types.
§Motivation
Effectively it functions as a more powerful version of paste!, whilst bringing the power of quote!’s variable substitution to declarative macros.
This approach neatly solves the following cases:
- Wanting
paste!to output strings or work with attributes other than doc. - Avoiding defining internal
macro_rules!to handle instances where you need to do a procedural macro repeat across two conflicting expansions per this stack overflow post. - Improves readability of long procedural macros through substitution of repeated segments.
An example of case 1:
// Inside a macro_rules! expression:
eager_replace!{
#[sbor(as_type = [!stringify! $my_inner_type])]
$vis struct $my_type($my_inner_type)
}§Specific functions
[!stringify! X Y " " Z]gives"X Y \" \" Z"- IMPORTANT: This usestoken_stream.into_string()which is compiler-version dependent. Do not use if that is important. Instead, the output fromconcatshould be independent of compiler version.[!concat! X Y " " Z (Hello World)]gives"XY Z(HelloWorld)"by concatenating each argument without spaces, and recursing inside groups. String and char literals are first unquoted. Spaces can be added with “ “.[!ident! X Y "Z"]gives an identXYZ, using the same algorithm asconcat.[!literal! 31 u 32]gives31u32, using the same algorithm asconcat.[!raw! abc #abc [!ident! test]]outputs its contents without any nested expansion, givingabc #abc [!ident! test].
Note that all functions except raw resolve in a nested manner as you would expected, e.g.
[!concat! X Y [!ident! Hello World] Z] // "XYHelloWorldZ"§Variables for cleaner coding
You can define variables starting with # which can be used outside the set call.
All of the following calls don’t return anything, but create a variable, which can be embedded later in the macro.
See the Demonstration section for details
[!SET! #MyVar = ..]sets#MyVarto the given token stream.[!SET:stringify! #MyVar = ..]sets#MyVarto the result of applying thestringifyfunction to the token stream.[!SET:concat! #MyVar = ..]sets#MyVarto the result of applying theconcatfunction to the token stream.[!SET:ident! #MyVar = ..]sets#MyVarto the result of applying theidentfunction to the token stream.[!SET:literal! #MyVar = ..]sets#MyVarto the result of applying theliteralfunction to the token stream.
§Demonstration
macro_rules! impl_marker_traits {
{
$(#[$attributes:meta])*
$vis:vis $type_name_suffix:ident
// Arbitrary generics
$(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? $( = $deflt:tt)? ),+ >)?
[
$($trait:ident),*
$(,) // Optional trailing comma
]
} => {eager_replace!{
[!SET! #ImplGenerics = $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?]
[!SET! #TypeGenerics = $(< $( $lt ),+ >)?]
[!SET:ident! #MyType = Type $type_name_suffix #TypeGenerics]
// Output for each marker trait
$(
impl #ImplGenerics $trait for #MyType
{
// Empty trait body
}
)*
}}
}§Future extensions
§String case conversion
Could in future support case conversion like paste.