代码之家  ›  专栏  ›  技术社区  ›  Alex

如何使用include?str!对于多个文件还是整个目录?

  •  1
  • Alex  · 技术社区  · 7 年前

    我想将整个目录复制到用户的 $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. 为我要复制的每个文件复制第一个代码。

    最好的方法是什么?

    2 回复  |  直到 7 年前
        1
  •  6
  •   Shepmaster Tim Diekmann    7 年前

    我会创造 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<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!("{name}")),"#,
                name = f.path().display(),
            )?;
        }
    
        writeln!(&mut all_the_files, r#"];"#,)?;
    
        Ok(())
    }
    

    这有一些弱点,即它要求路径可以表示为 &str . 因为你已经在使用 include_string! 我不认为这是 额外的 要求。

    因为我们包括文件,所以我用 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());
        }
    }
    
        2
  •  0
  •   Alex    7 年前

    使用宏:

    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));
    }
    

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

    更新 使用 Vec 而不是 HashMap .

    推荐文章