Как мне использовать include_str!для нескольких файлов или всего каталога? - PullRequest
0 голосов
/ 27 мая 2018

Я хотел бы скопировать весь каталог в папку пользователя $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 ]

0 голосов
/ 27 мая 2018

Использование макроса:

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.

0 голосов
/ 27 мая 2018

Я бы создал сценарий сборки , который выполняет итерацию по каталогу, создавая массив кортежей, содержащих имя и еще один вызов макроса для включения необработанных данных:

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!, я не думаю, что это требование extra .

Поскольку мы включаем файлы, я использовал 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());
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...