From 8e805836c69b882645014622aa3806a3c6aa5ef1 Mon Sep 17 00:00:00 2001 From: Dinu Blanovschi Date: Wed, 17 Sep 2025 11:54:15 +0200 Subject: [PATCH] fix: allow include_dir! to be called from within another macro --- include_dir/src/lib.rs | 2 +- include_dir/tests/integration_test.rs | 21 +++++++++++++++++++++ macros/src/lib.rs | 10 +++++++++- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/include_dir/src/lib.rs b/include_dir/src/lib.rs index b9380b931c..b13a9661f3 100644 --- a/include_dir/src/lib.rs +++ b/include_dir/src/lib.rs @@ -106,6 +106,6 @@ pub use crate::metadata::Metadata; pub use crate::{dir::Dir, dir_entry::DirEntry, file::File}; pub use include_dir_macros::include_dir; -#[doc = include_str!("../README.md")] +#[doc = include_str!("../../README.md")] #[allow(dead_code)] fn check_readme_examples() {} diff --git a/include_dir/tests/integration_test.rs b/include_dir/tests/integration_test.rs index bf95ba59df..6d888d36a0 100644 --- a/include_dir/tests/integration_test.rs +++ b/include_dir/tests/integration_test.rs @@ -94,3 +94,24 @@ fn msrv_is_in_sync() { .unwrap(); assert_eq!(workflow_msrv, msrv); } + +#[test] +fn call_from_other_macro() { + macro_rules! call_include_dir { + ($path:literal) => { + include_dir!($path) + }; + } + + macro_rules! call_include_dir_2 { + ($path:literal) => { + call_include_dir!($path) + }; + } + + static DIR: Dir<'_> = call_include_dir!("$CARGO_MANIFEST_DIR"); + static DIR2: Dir<'_> = call_include_dir_2!("$CARGO_MANIFEST_DIR"); + + assert!(DIR.contains("src/lib.rs")); + assert!(DIR2.contains("src/lib.rs")); +} diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 4a881f029b..6f3b770d96 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -3,7 +3,7 @@ //! You probably don't want to use this crate directly. #![cfg_attr(feature = "nightly", feature(track_path, proc_macro_tracked_env))] -use proc_macro::{TokenStream, TokenTree}; +use proc_macro::{Delimiter, TokenStream, TokenTree}; use proc_macro2::Literal; use quote::quote; use std::{ @@ -20,6 +20,14 @@ pub fn include_dir(input: TokenStream) -> TokenStream { let path = match tokens.as_slice() { [TokenTree::Literal(lit)] => unwrap_string_literal(lit), + // Delimiter::None is used when the macro is invoked from another macro + [TokenTree::Group(g)] if g.delimiter() == Delimiter::None => { + let stream: Vec<_> = g.stream().into_iter().collect(); + match stream.as_slice() { + [TokenTree::Literal(lit)] => unwrap_string_literal(lit), + _ => panic!("This macro only accepts a single, non-empty string argument"), + } + } _ => panic!("This macro only accepts a single, non-empty string argument"), };