#proc-macro #script #write #source #inline #run-time #helper

script-macro

将过程宏与其他源代码内联编写

1 个不稳定版本

0.1.0 2023年4月23日

#533过程宏

MIT 许可证

12KB
141

script-macro

一种在与其他源代码内联编写简单过程宏的实验性方法。

在编写过程宏时,你是否曾经因为样板代码而感到沮丧,并希望能够直接编写Python或Bash脚本来生成代码呢?

pub fn add(left: usize, right: usize) -> usize {
    left + right
}

#[cfg(test)]
mod tests {
    use super::*;

    #[script_macro::run_script_on(r##"
        let output = item;

        for x in 0..10 {
            for y in 0..10 {
                output += `
                #[test]
                fn it_works_${x}_${y}() {
                    it_works(${x}, ${y}, ${x + y});
                }`;
            }
        }

        return output;
    "##)]
    fn it_works(x: usize, y: usize, out: usize) {
        assert_eq!(add(x, y), out);
    }
}

宏不是Rust源代码,而是用RHAI脚本语言编写的。这既有优点也有缺点

  • 缺点:无法访问Rust crate生态系统 -- RHAI是其自己的独立语言,因此你无法在其中使用Rust crate。RHAI 可以 通过自定义Rust函数进行扩展,但script-macro目前还不支持。目前,script-macro公开了一些在代码生成中常用的辅助工具。

  • 优点:可沙箱化 -- 使用script-macro执行的进程宏无法访问互联网或执行任意系统调用。进程宏 可以 通过rhai-fs中提供的函数完全访问文件系统,但未来版本中这可能可配置,例如只读访问或限制访问特定目录。

  • 缺点:依赖RHAI运行时 -- RHAI是一个完整的语言运行时,必须在运行任何进程宏之前编译一次。

  • 优点:编辑进程宏时无需重新编译。 -- 进程宏是解释型脚本。在编辑时,只需要重新编译包含的crate,而不需要重新编译script-macro本身。这 可能 在处理大量进程宏时更快。

    另请参阅watt,它似乎在编译速度(一次编译所有宏的运行时,运行所有宏而不需要编译)方面具有类似的权衡

真的吗?

我真心希望过程宏(proc_macros)更容易编写(与其它代码一起编写)并且不会对编译时间产生那么大的影响。对我来说,这种情况特别常见的一个领域是程序化测试生成(或,参数化)。

这是我今天能做的最好的尝试,但这并不意味着我确信最终结果适用于生产使用。我希望它能激励其他人构建更好的东西。

API

可以选择的两个主要宏:

  • script_macro::run_script_on -- 属性宏,执行给定脚本,同时将注解函数/模块的源代码作为全局字符串在 item 下提供。

    脚本的返回值是替换项目将使用的源代码。

    这是一个简单的脚本宏,它将 #[test] 添加到注解函数中。

    #[script_macro::run_script_on(r##"
        return "#[test]" + item;
    "##)]
    fn it_works(x: usize, y: usize, out: usize) {
        assert_eq!(add(x, y), out);
    }
    
  • script_macro::run_script -- 函数宏,执行给定脚本。没有输入。

    script_macro::run_script!(r##"
        return `fn main() { println!("hello world"); }`;
    "##);
    

脚本 API

在脚本内部,整个 RHAI stdlib 以及 rhai-fs 中的函数都可用。

此外,还定义了以下函数:

  • parse_yaml(String) -> Dynamic -- 将 YAML 有效负载作为字符串接收,并返回解析后的有效负载作为非结构化数据(例如,RHAI 对象映射或数组)。

  • parse_json(String) -> Dynamic -- 将 JSON 有效负载作为字符串接收,并返回解析后的有效负载作为非结构化数据(例如,RHAI 对象映射或数组)。

  • stringify_yaml(Dynamic) -> String -- 将 RHAI 对象转换为 YAML 字符串,与 parse_yaml 相反。

  • stringify_json(Dynamic) -> String -- 将 RHAI 对象转换为 YAML 字符串,与 parse_json 相反。

  • glob(String) -> Vec<PathBuf> -- 接收一个 glob 模式,并返回匹配该模式的路径列表。

  • basename(PathBuf) -> String -- 返回给定路径的 .file_name(),如果没有,则返回整个路径。

示例

查看 示例库,了解上述所有功能的应用。

许可证

在 MIT 许可下发布,请参阅 ./LICENSE

依赖项

~7MB
~133K SLoC