【问题标题】:How do I use include_str! for multiple files or an entire directory?如何使用 include_str!对于多个文件或整个目录?
【发布时间】:2021-10-16 17:22:33
【问题描述】:

我想将整个目录复制到用户$HOME 中的某个位置。将文件单独复制到该目录很简单:

let contents = include_str!("resources/profiles/default.json");
let fpath = dpath.join(&fname);
fs::write(fpath, contents).expect(&format!("failed to create profile: {}", n));

我还没有找到一种方法来适应多个文件:

for n in ["default"] {
    let fname = format!("{}{}", n, ".json");
    let x = format!("resources/profiles/{}", fname).as_str();
    let contents = include_str!(x);
    let fpath = dpath.join(&fname);
    fs::write(fpath, contents).expect(&format!("failed to create profile: {}", n));
}

...编译器抱怨x 必须是字符串文字。

据我所知,有两种选择:

  1. 编写自定义宏。
  2. 为我要复制的每个文件复制第一个代码。

最好的方法是什么?

【问题讨论】:

    标签: rust


    【解决方案1】:

    我会创建a build script,它遍历一个目录,构建一个包含名称和另一个宏调用以包含原始数据的元组数组:

    use std::{
        env,
        error::Error,
        fs::{self, File},
        io::Write,
        path::Path,
    };
    
    const SOURCE_DIR: &str = "some/path/to/include";
    
    fn main() -> Result<(), Box<dyn Error>> {
        let out_dir = env::var("OUT_DIR")?;
        let dest_path = Path::new(&out_dir).join("all_the_files.rs");
        let mut all_the_files = File::create(&dest_path)?;
    
        writeln!(&mut all_the_files, r##"["##,)?;
    
        for f in fs::read_dir(SOURCE_DIR)? {
            let f = f?;
    
            if !f.file_type()?.is_file() {
                continue;
            }
    
            writeln!(
                &mut all_the_files,
                r##"("{name}", include_bytes!(r#"{name}"#)),"##,
                name = f.path().display(),
            )?;
        }
    
        writeln!(&mut all_the_files, r##"]"##,)?;
    
        Ok(())
    }
    

    这有一些弱点,即它要求路径可以表达为&amp;str。由于您已经在使用include_string!,我不认为这是一个额外要求。这也意味着生成的字符串必须是有效的 Rust 字符串。我们在生成的文件中使用原始字符串,但如果文件名包含字符串"#,这仍然会失败。更好的解决方案可能会使用str::escape_default

    由于我们包含文件,我使用include_bytes! 而不是include_str!,但如果你真的需要,你可以切换回来。原始字节在编译时跳过了执行 UTF-8 验证,所以这是一个小小的胜利。

    使用它涉及到导入生成的值:

    const ALL_THE_FILES: &[(&str, &[u8])] = &include!(concat!(env!("OUT_DIR"), "/all_the_files.rs"));
    
    fn main() {
        for (name, data) in ALL_THE_FILES {
            println!("File {} is {} bytes", name, data.len());
        }
    }
    

    另见:

    【讨论】:

    • 嗨,我不知道这是否仍然是首选方式,但是当我尝试这个时({projectRoot}/build.rs 中的第一个 sn-p,{projectRoot}/src/main.rs 中的第二个)cargo build 抱怨它找不到文件。我猜如果const SOURCE_DIR: &amp;str = "some/path/to/include"; 是绝对路径,它会起作用,但使用绝对路径并不适合分发。我还缺少其他东西吗?
    • @DavSanchez 是否向您展示了如何将 How can I locate resources for testing with Cargo? 替换为与您的项目相关的内容?
    • 是的,我试过了,它还通过从 build.rs 的最后一个 writeln!() 的原始字符串中删除 ; 来工作。非常感谢!
    【解决方案2】:

    使用宏:

    macro_rules! incl_profiles {
        ( $( $x:expr ),* ) => {
            {
                let mut profs = Vec::new();
                $(
                    profs.push(($x, include_str!(concat!("resources/profiles/", $x, ".json"))));
                )*
    
                profs
            }
        };
    }
    

    ...

    let prof_tups: Vec<(&str, &str)> = incl_profiles!("default", "python");
    
    for (prof_name, prof_str) in prof_tups {
        let fname = format!("{}{}", prof_name, ".json");
        let fpath = dpath.join(&fname);
        fs::write(fpath, prof_str).expect(&format!("failed to create profile: {}", prof_name));
    }
    

    注意:这不是动态的。文件(“default”和“python”)在对宏的调用中指定。

    更新:使用Vec 代替HashMap

    【讨论】:

    • 如果您只打算迭代,使用HashMap 是不必要的开销。
    • @Shepmaster 正在使用它,所以我不需要"default""python" 的另一行......我想我可以使用一个数组并在之后拆分它,但这似乎更简单。你会推荐什么?
    • 我的回答 ([(name, value)]) 中看到的元组数组。
    【解决方案3】:

    您可以使用include_dir 宏。

    use include_dir::{include_dir, Dir};
    use std::path::Path;
    
    const PROJECT_DIR: Dir = include_dir!(".");
    
    // of course, you can retrieve a file by its full path
    let lib_rs = PROJECT_DIR.get_file("src/lib.rs").unwrap();
    
    // you can also inspect the file's contents
    let body = lib_rs.contents_utf8().unwrap();
    assert!(body.contains("SOME_INTERESTING_STRING"));
    

    【讨论】:

      猜你喜欢
      • 2016-05-08
      • 1970-01-01
      • 2022-07-02
      • 1970-01-01
      • 1970-01-01
      • 2021-11-13
      • 1970-01-01
      • 1970-01-01
      • 2021-03-28
      相关资源
      最近更新 更多