Michael-F-Bryan / include_dir

The logical evolution of the include_str macro for embedding a directory tree into your binary.
https://michael-f-bryan.github.io/include_dir
MIT License
339 stars 38 forks source link

Allow macro calls in arguments #86

Closed jjant closed 1 year ago

jjant commented 1 year ago

I want to write a crate that interally uses include_dir, something like this:

// some_crate
macro_rules! include_assets {
  ($path: tt) => {{
    static DIR: Dir = include_dir!(concat!(env!("CARGO_MANIFEST_DIR"), "/", $path));
    ...
  }}
}

// use site
fn main() {
  let assets = some_crate::include_assets!("assets");
}

But that currently fails because include_dir only allows literal strings as arguments.

Michael-F-Bryan commented 1 year ago

The compiler doesn't provide a way for proc-macros to evaluate macros dynamically, so I don't think this is possible.

That said, the include_dir!() macro can interpolate environment variables, so could the user just run some_crate::include_assets!("$CARGO_MANIFEST_DIR/assets") where your macro passes "$CARGO_MANIFEST_DIR/assets" to include_dir!() as-is?

It's also possible to hack around the lack of string concatenation with something like paste.

extern crate paste;

macro_rules! include_dir {
    ($arg:literal) => {
        println!("Including {}", $arg);
    };
}

/// The paste crate will only concatenate strings when it looks like they are
/// used in a #[doc] attribute, so this tricks paste into doing the
/// string concatenation rather than creating an (invalid)
/// "$CARGO_MANIFEST_DIR/blah" identifier
macro_rules! include_dir_hack {
    (#[doc = $arg:literal]) => {
        include_dir!($arg);
    };
}

macro_rules! include_assets {
    ($arg:tt) => {
        paste::paste! {
            include_dir_hack!(#[doc = "$CARGO_MANIFEST_DIR/" $arg ]);
        }
    };
}

fn main() {
    include_assets!("assets"); // Outputs "Including $CARGO_MANIFEST_DIR/assets"
}

(playground)

There was an RFC for "eager macro expansion" which does exactly what you want, but that was postponed.

If the above workarounds aren't acceptable, you might need to look for another way to do the same thing.

Also, keep in mind that any macro-based solution will evaluate the $CARGO_MANIFEST_DIR to the caller's crate directory, not your include_assets!() crate's directory.