#proc-macro #cargo-manifest #nightly #path #syn #cargo-toml #own

nightly cargo-manifest-proc-macros

Find the syn::Path to your own crate from proc-macros reliably

2 unstable releases

new 0.1.0 Dec 19, 2024
0.0.1 Dec 19, 2024

#6 in #cargo-manifest

MIT/Apache

27KB
263 lines

cargo-manifest-proc-macros

Daily-Nightly Rust-Main-CI docs.rs crates.io rustc

Requirements

cargo-manifest-proc-macros is a library for creating proc-macros. It provides a reliably way to compute the `syn::Path to other crates.

This crate requires a nightly compiler.

What can this crate do?

Proc-macro crates usually tightly coupled to other crates and need to know the module path to them.

Given the following common crate structure:

  • my-awesome-crate-proc-macros has the proc-macro implementations of my-awesome-crate
  • my-awesome-crate depends on my-awesome-crate-proc-macros and exports the macros in my-awesome-crate-proc-macros.
  • my-awesome-super-crate depends on and re-exports my-awesome-crate. This is called a KnownReExportingCrate.

There are multiple ways users can gain access to the proc-macros and your other crates:

[dependencies]
my-awesome-crate = "0.1.0" # The classic and easy way
my-awesome-crate-renamed = { version = "0.1.0", package = "my-awesome-crate" } # The renamed way
my-awesome-super-crate = "0.1.0" # The re-exporting way
my-awesome-super-crate-renamed = { version = "0.1.0", package = "my-awesome-super-crate" } # The renamed re-exporting way

The CargoManifest struct is capable of computing the syn::Path to the crates in the all of the above cases.

Example (non-re-exporting)

extern crate proc_macro;

use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};
use cargo_manifest_proc_macros::CargoManifest;

//#[proc_macro_derive(YourProcMacro, attributes(your_attributes))]
pub fn derive_your_proc_macro(input: TokenStream) -> TokenStream {
  let mut ast = parse_macro_input!(input as DeriveInput);
  // The [`CargoManifest::resolve_crate_path`] returns a global [`syn::Path`] to the crate no matter how it is depended on.
  let path_to_your_crate: syn::Path = CargoManifest::shared().resolve_crate_path("my-awesome-crate", &[]);
  TokenStream::default()
}

Example (re-exporting)

In this example the user may depend on either my-awesome-super-crate or my-awesome-crate to gain access to the proc-macros and features of my-awesome-crate.

extern crate proc_macro;

use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};
use cargo_manifest_proc_macros::{CargoManifest, CrateReExportingPolicy, KnownReExportingCrate, PathPiece};

struct SuperCrateReExportingPolicy {}

impl CrateReExportingPolicy for SuperCrateReExportingPolicy {
  fn get_re_exported_crate_path(
    &self,
    crate_name: &str,
  ) -> Option<PathPiece> {
    // Since crates can be re-exported with arbitrary names, we may need to transform the crate name to the re-exported name.
    let export_name = crate_name.strip_suffix("_crate"); // This would handle the case of `my-awesome-crate` being re-exported as just `my-awesome` by `my-awesome-super-crate`.
    export_name.map(|export_name| {
      let mut path_piece = PathPiece::new();
      path_piece.push(syn::parse_str::<syn::PathSegment>(export_name).unwrap());
      path_piece
    })
  }
}

/// We need to associate the re-exporting policy with the re-exporting crate name.
const SUPER_RE_EXPORTER_RESOLVER: KnownReExportingCrate<'_> = KnownReExportingCrate {
  re_exporting_crate_name: "my-awesome-super-crate",
  crate_re_exporting_policy: &SuperCrateReExportingPolicy {},
};

/// In case there are multiple re-exporting crates, we can list them here.
const KNOWN_RE_EXPORTING_CRATES: &[&KnownReExportingCrate<'_>] = &[
  &SUPER_RE_EXPORTER_RESOLVER,
];

//#[proc_macro_derive(YourProcMacro, attributes(your_attributes))]
pub fn derive_your_proc_macro(input: TokenStream) -> TokenStream {
  let mut ast = parse_macro_input!(input as DeriveInput);
  // The [`CargoManifest::resolve_crate_path`] returns a global [`syn::Path`] to the crate no matter how it is depended on.
  let path_to_your_crate: syn::Path = CargoManifest::shared().resolve_crate_path("my-awesome-crate", KNOWN_RE_EXPORTING_CRATES);
  TokenStream::default()
}

License

This project is released under either:

at your choosing.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

How it works

This crate works by parsing the $CARGO_MANIFEST_DIR/Cargo.toml file and extracting the dependencies from it. It then uses the package field in the Cargo.toml to resolve the path to the crate.

syn

Dependencies

~3MB
~61K SLoC