How to set dependencies for a macro in Rust?

Viewed 1034

I wrote a macro in a crate and import it in the main function of a main project.

The macro depends on:

use chrono::{Utc, Local, DateTime, Date};

Currently I include the dependency in the Cargo.toml of the main project.

Will I need to do the same for all main projects? Or Can I declare the dependency in the Cargo.toml of the macro crate?

//lib.rs //Mymacro 
#[macro_export]
macro_rules! time {
    () => {
        use chrono::{Utc, Local, DateTime, Date};
        let local_datetime: DateTime<Local> = Local::now();
        println!("{:?}",local_datetime);
    }
}
//main.rs // main project
extern crate Mymacro;

#[macro_use]
fn main() {
    time!();
}

// Cargo.html 
[dependencies] 
chrono=""
1 Answers

Will I need to do the same for all main projects? Or Can I declare the dependency in the Cargo.toml of the macro crate?

You can, by either (1) having your macro call a function in your library or (2) by re-exporting chrono (partially or completely).


Let's consider that we have a workspace, with a lib and bin crate.

[workspace]
members = [
    "bin",
    "lib",
]

Now in your Cargo.toml for the lib crate you'd include chrono as a dependency, like you otherwise would.

[dependencies]
chrono = "0.4"

While the Cargo.toml for the bin crate only has the dependency of lib.

[dependencies]
lib = { path = "../lib" }

Now, let's consider that main.rs in the bin crate looks like this:

use lib::time;

fn main() {
    time!();
}

It will remain the same, regardless of which option you go with.


The following snippets represent lib.rs.


Library Function

If you never actually return any chrono specific types, then this might be the easiest. As you avoid re-exporting chrono completely.

use chrono::{DateTime, Local};

pub fn _time() {
    let local_datetime: DateTime<Local> = Local::now();
    println!("{:?}", local_datetime);
}

#[macro_export]
macro_rules! time {
    () => {
        $crate::_time();
    };
}

If you don't want _time to appear in the documentation of your library, then you can use the #[doc(hidden)] attribute.

Re-export some items

Alternatively, if you want everything to remain within the macro, then you can re-export the chrono types it uses, and prepend the types in your macro with $crate::.

The downside to this, is that if your bin crate needs a chrono type that is not re-exported, then that can be an "annoyance" and thus bin would need to depend on chrono = "0.4" anyways.

pub use chrono::{DateTime, Local};

#[macro_export]
macro_rules! time {
    () => {
        let local_datetime: $crate::DateTime<$crate::Local> = $crate::Local::now();
        println!("{:?}", local_datetime);
    };
}

In main.rs if you instead do use lib::*;, then you'd of course not need to prepend $crate::, but it's best to avoid doing use lib::*;.

Re-export Chrono

Lastly, if you need the chrono types in you bin crate. Then you can also re-export the whole chrono crate with pub extern crate chrono;.

Note that this time, you need to prepend the types in your macro with $crate::chrono::.

pub extern crate chrono;

#[macro_export]
macro_rules! time {
    () => {
        let local_datetime: $crate::chrono::DateTime<$crate::chrono::Local> =
            $crate::chrono::Local::now();
        println!("{:?}", local_datetime);
    };
}
Related