This crate provides the #[export_tokens]
macro and a number of companion macros, including
the #[import_tokens_proc]
and #[import_tokens_attr]
macros. When used in tandem with
#[export_tokens]
, these macros allow you to create regular and attribute proc macros in which
you can import and make use of the tokens of external/foreign items marked with
#[export_tokens]
in other modules, files, and even in other crates merely by referring to
them by name/path.
Among other things, the patterns introduced by macro_magic
can be used to implement safe and
efficient exportation and importation of item tokens within the same file, and even across file
and crate boundaries.
macro_magic
is designed to work with stable Rust, and is fully no_std
compatible (in fact,
there is a unit test to ensure everything is no_std
safe). The main crate and all sub-crates
are no_std
.
You can use macro_magic
to build regular and attribute proc macros that look like this:
#[my_attribute(path::to::MyItem)]
trait SomeTrait {
// ..
}
this:
do_something!(path::to::MyItem);
or even this:
let foreign_tokens = my_macro!(path::to::MyItem);
assert_eq!(foreign_tokens.to_string(), "struct MyItem {...}");
where path::to::MyItem
is the path to an item that has been marked with #[export_tokens]
.
All of this behavior is accomplished under the hood using proc macros that create
macro_rules
-based callbacks, but as a programmer this complexity is completely hidden from
you via simple attribute macros you can apply to your proc macros to imbue them with the power
of importing the tokens for external items based on their path.
You could write an attribute macro to "inject" the fields of one struct into another as follows:
#[import_tokens_attr]
#[proc_macro_attribute]
pub fn combine_structs(attr: TokenStream, tokens: TokenStream) -> TokenStream {
let foreign_struct = parse_macro_input!(attr as ItemStruct);
let local_struct = parse_macro_input!(tokens as ItemStruct);
let Fields::Named(local_fields) = local_struct.fields else {
return Error::new(
local_struct.fields.span(),
"unnamed fields are not supported"
).to_compile_error().into()
};
let Fields::Named(foreign_fields) = foreign_struct.fields else {
return Error::new