dtolnay / typetag

Serde serializable and deserializable trait objects
Apache License 2.0
1.19k stars 38 forks source link

Support renaming crate in Cargo.toml, re-exporting macros #50

Closed jess-sol closed 1 year ago

jess-sol commented 2 years ago

The proc macros in typetag explicitly use the typetag crate when referring to idents in the crate, this PR moves to using a crate_path variable instead. The variable is set by the proc-macro-crate package, which looks up the proper name of the crate in the Cargo.toml file. This allows the crate to be renamed in the Cargo.toml file, and still function.

This PR also breaks the macro definitions and their impl apart into separate crates, which allows other packages to easily re-export the macro without requiring users of their package to also depend on typetag directly.

This PR came about because I want to re-export the typetag macros from my crate, so my macros can use it, without the user of my crate needing to depend directly on typetag too.

I realize dtolnay has more context into these issues than I do hah, but for others here is a bit more context - https://internals.rust-lang.org/t/rfc-crate-for-proc-macro/16968 Also I very loosely based my solution on Bevy's, though using proc-macro-crate instead of rolling my own like they needed to. See the bevy-derive and bevy-macro-utils crates for reference.

Example renaming crate in Cargo.toml:

Cargo.toml

[package]
name = "example"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "1.0.140", features = ["derive"] }
serde_json = "1.0.82"
typetag_blah = { version = "0.2.1", package = "typetag" }

main.rs

[package]
name = "example"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "1.0.140", features = ["derive"] }
serde_json = "1.0.82"
typetag_blah = { version = "0.2.1", package = "typetag" }

Example re-exporting macros:

another-package/derive/src/lib.rs

use proc_macro::TokenStream;
use quote::format_ident;
use typetag_impl::{Mode, expand, get_crate_path};

#[proc_macro_attribute]
pub fn serde(args: TokenStream, input: TokenStream) -> TokenStream {
    let mut crate_path = get_crate_path("another_package");
    crate_path.segments.push(format_ident!("typetag").into());
    expand(args, input, Mode::new(true, true), &crate_path)
}

#[proc_macro_attribute]
pub fn serialize(args: TokenStream, input: TokenStream) -> TokenStream {
    let mut crate_path = get_crate_path("another_package");
    crate_path.segments.push(format_ident!("typetag").into());
    expand(args, input, Mode::new(true, false), &crate_path)
}

#[proc_macro_attribute]
pub fn deserialize(args: TokenStream, input: TokenStream) -> TokenStream {
    let mut crate_path = get_crate_path("another_package");
    crate_path.segments.push(format_ident!("typetag").into());
    expand(args, input, Mode::new(false, true), &crate_path)
}