Skip to content

Serde -> TokenStream serializer #220

@matrach

Description

@matrach

Hi!

Have you considered implementing a reverse operation: serialization to TokenStream as part of this library?

Having a round-trip operation could be very useful in some contexts: one is a wrapper around macros using deserialization with serde_tokenstream. Because Rust macros are expanded recursively, one could write a macro such as:

#[simpler_api(param = "value")]
struct S(...);

that could expand to:

#[complex_api(
    param1 = 42,
    param2 = ["v", "a", "l", "u", "e"],
)]
struct S(...);

Of course, generating the parameters could be done manually, but having a complete round-trip operation as part of a single library could enable complex macro shenanigans. For instance, the macro_magic crate enables exporting tokens at one location, and then receiving them by a proc macro at completely different place (even across file and crate boundary). This is done with generation of decl macros doing callbacks to proc macros with a token tree. Combining this idea with de/serialization to valid token trees, we could have macros emit data-objects to be consumed by other proc-macros, effectively enabling sound communication across proc-macros invocation.

Collapsed: example data-macros expansion.

This is how one may implement data passing across various proc macros by using serialization to a TokenStream. In this example we do just a simple analysis that could be done using existing macro_magic: dump_analysis proc macro generates some data that is consumed by another proc-macro handler.

// A call such as
#[dump_analysis]
pub struct ApiCall {
    field: u64,
}

// would generate a decl-macro like:
macro_rules! __api_call__data {
 ($callback:path, $extra:tt) => {
    $callback!( {$extra},
        // Data serialized with serde_tokenstream::serialize
        { n_fields = 1, field_names = [ field ] }
    }
}}
}

// A user writes a handler like
#[handler(path::to::ApiCall)]
pub fn handle(request: path::to::ApiCall) { ... }

// which, first, expands to:
path::to::__api_call__data!{
   handler_inner,
   {pub fn handle(request: path::to::ApiCall) { ... }}
}

// this expands again to
handler_inner! {
   {pub fn handle(request: path::to::ApiCall) { ... }},
   { n_fields = 1, field_names = [ field ] }
}

// At which point, implementation of handler_inner uses serde_tokenstream::deserialize to get the data back.

Have you thought about implementing this? Alternatively, would you accept such a contribution?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions