rust 推断调用crate的名称以在过程宏中填充doctest

eqoofvh9  于 2023-10-20  发布在  其他
关注(0)|答案(1)|浏览(101)

我正在创建一个过程宏,它从一些配置文件自动生成一个库(这是一个寄存器布局,但这对这个问题并不重要)。
我想库自动生成的文档伴随着自动库,并包括应与cargo test运行的文档测试。现在,我已经实现了其中的大部分,但有一个问题我看不到解决方案。
假设我们有一个名为my_lib的库,我们调用宏来填充它:

use my_macro_lib::hello;

hello!();

它可以扩展为:

/// `foo` will always return `true`
/// ```
/// use my_lib;
/// assert!(my_lib::foo());
/// ```
pub fn foo() -> bool {
    true
}

这将按预期运行-cargo doc将做正确的事情,cargo test将按预期运行doctests。
问题是在这个例子中,use my_lib被硬编码到my_macro_lib中,这显然是不可取的。
我怎样才能创建一个宏来推断正在调用的crate的名称?
我尝试在过程宏中使用macro_rules!来扩展$crate,但这违反了卫生规则。

watbbzwu

watbbzwu1#

您可以通过阅读CARGO_PKG_NAME环境变量来获取使用宏的crate的名称。请注意,你必须通过std::env(在宏的“运行时”)读取它,而 * 不是 * 通过env!(在编译proc宏crate时)。

#[proc_macro]
pub fn hello(input: TokenStream) -> TokenStream {
    let crate_name = std::env::var("CARGO_PKG_NAME").unwrap();
    let use_statement = format!("use {}::foo;", crate_name);

    let output = quote! {
        /// `foo` will always return `true`
        /// ```
        #[doc = #use_statement]
        /// assert!(foo());
        /// ```
        pub fn foo() -> bool {
            true
        }
    };

    output.into()
}

这里有一些关于在文档注解中插入内容的复杂性。像/// #an_ident这样的插值方法不起作用,因为文档注解是以特殊的方式解析的。唯一的方法是创建一个字符串并使用#[doc = ...]语法。这有点烦人,因为您必须在quote!调用之前创建字符串,但它可以工作。

    • 目前proc宏可以访问所有的环境(包括文件系统,网络等)。据我所知,proc宏不能保证这种访问,proc宏将来可能会被沙箱化。因此,这个解决方案还不完美,但它现在有效(可能还需要相当长的一段时间)。

另一种方法是让用户将crate名称传递给宏:

hello!(my_lib);

如果你的宏在每个crate中只被调用一次,这可能是首选的解决方案。如果你的宏被调用了很多次,那么重复crate的名字可能会很烦人。

相关问题