Skip to content

A collection of Rust proc macros that allow the exporting and importing of TokenStream2s of items in foreign contexts and files

License

Notifications You must be signed in to change notification settings

gui1117/macro_magic

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Macro Magic 🪄

Crates.io docs.rs Build Status MIT License

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.

General Syntax

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.

Attribute Example

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