expander

Expands a proc-macro into a file, and uses a include! directive in place.

Advantages

Usage

In your proc-macro, use it like:

```rust

[procmacroattribute]

pub fn baz(attr: procmacro::TokenStream, input: procmacro::TokenStream) -> procmacro::TokenStream { // wrap as per usual for proc-macro2::TokenStream, here dropping attr for simplicity baz2(input.into()).into() }

// or any other macro type fn baz2(input: procmacro2::TokenStream) -> procmacro2::TokenStream { let modified = quote::quote!{ #[derive(Debug, Clone, Copy)] #input };

let expanded = Expander::new("baz")
    .add_comment("This is generated code!".to_owned())
    .fmt(Edition::_2021)
    .verbose(true)
    // common way of gating this, by making it part of the default feature set
    .dry(cfg!(feature="no-file-expansion"))
    .write_to_out_dir(modified.clone()).unwrap_or_else(|e| {
        eprintln!("Failed to write to file: {:?}", e);
        modified
    });
expanded

} ```

will expand into

rust include!("/absolute/path/to/your/project/target/debug/build/expander-49db7ae3a501e9f4/out/baz-874698265c6c4afd1044a1ced12437c901a26034120b464626128281016424db.rs");

where the file content will be

```rust

[derive(Debug, Clone, Copy)]

struct X { y: [u8:32], } ```

Examplary output

An error in your proc-macro, i.e. an excess ;, is shown as


   Compiling expander v0.0.4-alpha.0 (/somewhere/expander)
error: macro expansion ignores token `;` and any following
 --> tests/multiple.rs:1:1
  |
1 | #[baz::baz]
  | ^^^^^^^^^^^ caused by the macro expansion here
  |
  = note: the usage of `baz::baz!` is likely invalid in item context

error: macro expansion ignores token `;` and any following
 --> tests/multiple.rs:4:1
  |
4 | #[baz::baz]
  | ^^^^^^^^^^^ caused by the macro expansion here
  |
  = note: the usage of `baz::baz!` is likely invalid in item context

error: could not compile `expander` due to 2 previous errors
warning: build failed, waiting for other jobs to finish...
error: build failed

becomes


   Compiling expander v0.0.4-alpha.0 (/somewhere/expander)
expander: writing /somewhere/expander/target/debug/build/expander-8cb9d7a52d4e83d1/out/baz-874698265c6c.rs
error: expected item, found `;`
 --> /somewhere/expander/target/debug/build/expander-8cb9d7a52d4e83d1/out/baz-874698265c6c.rs:2:42
  |
2 | #[derive(Debug, Clone, Copy)] struct A ; ;
  |                                          ^

expander: writing /somewhere/expander/target/debug/build/expander-8cb9d7a52d4e83d1/out/baz-73b3d5b9bc46.rs
error: expected item, found `;`
 --> /somewhere/expander/target/debug/build/expander-8cb9d7a52d4e83d1/out/baz-73b3d5b9bc46.rs:2:42
  |
2 | #[derive(Debug, Clone, Copy)] struct B ; ;
  |                                          ^

error: could not compile `expander` due to 2 previous errors
warning: build failed, waiting for other jobs to finish...
error: build failed

which tells you exactly where in the generated code of your proc-macro you generated that superfluous statement.

Now this was a simple example, doing this with macros that would expand to multiple tens of thousand lines of code with cargo-expand, but only in a few thousand that your particular one generates, it's a life saver to know what caused the issue rather than having to use eprintln! to print a unformated string to the terminal.

Hint: You can quickly toggle this by using .dry(true || false)