5.3 Что несёт воздух
Хорошая инструкция описывает общее; детали - дело мастерской. Где хранить готовые изделия - не вопрос инструмента, а вопрос места. На одном станке это один угол, на другом - другой, на временном - нигде и ненадолго. Файл с настройкой не умеет говорить сам за себя. Его приходится менять перед переездом. Переезд случается чаще, чем планировали.
Что нужно
tq читает config.toml - берёт data_dir оттуда, или использует текущий каталог по умолчанию.
Это работает для одного места. Разработчик с данными в ~/tq-data и CI с данными в /tmp/ci-tq
должны либо менять файл перед каждым запуском, либо держать два файла и подкладывать нужный.
Оба варианта предполагают ручную работу.
Переменная среды решает вопрос иначе: не меняет файл, не требует версионирования, живёт ровно столько, сколько нужно - один запуск, один сеанс, один job в CI.
Сборка
std::env::var("TQ_DATA_DIR") возвращает Ok(String) если переменная установлена, и Err если
нет. if let Ok(dir) берёт значение только в успешном случае; если переменная не задана, блок
пропускается.
В src/config/mod.rs два изменения - config становится mut, и после загрузки файла
добавляется проверка переменной среды:
// src/config/mod.rs - CHANGED
pub fn load(path: &Path) -> TqResult<Config> {
let mut config = if !path.exists() { // CHANGED: let -> let mut
Config::default()
} else {
let content = fs::read_to_string(path).map_err(|e| TqError::Parse(e.to_string()))?;
toml::from_str(&content).map_err(|e| TqError::Parse(e.to_string()))?
};
if let Ok(dir) = std::env::var("TQ_DATA_DIR") { // NEW
config.data_dir = PathBuf::from(dir); // NEW
} // NEW
if !config.data_dir.exists() {
return Err(TqError::Parse(format!(
"data_dir '{}' does not exist; create it with: mkdir -p {}",
config.data_dir.display(),
config.data_dir.display()
)));
}
Ok(config)
}
Сначала загружаем файл (или берём default), потом - если переменная задана - перезаписываем
data_dir. Проверка существования директории стоит в конце, после всех источников. Приоритет:
TQ_DATA_DIR > config.toml > default.
Заглавные буквы и префикс TQ_ - соглашение Unix: переменные среды читает любой инструмент,
и имя должно быть достаточно уникальным, чтобы не пересечься с чужими.
Тест для нового пути - в src/config/tests.rs. В Rust 2024 set_var требует unsafe
(детали - в M6.x), и переменную убираем до unwrap() - если тест упадёт на проверке,
remove_var уже выполнен:
#[test]
fn env_var_overrides_data_dir() {
let tmp = std::env::temp_dir().join("tq-m53-env");
std::fs::create_dir_all(&tmp).unwrap();
unsafe { std::env::set_var("TQ_DATA_DIR", &tmp) };
let result = Config::load(Path::new("nonexistent.toml"));
unsafe { std::env::remove_var("TQ_DATA_DIR") };
assert_eq!(result.unwrap().data_dir, tmp);
}
Примечание.
env_var_overrides_data_dirменяетTQ_DATA_DIR- переменную окружения процесса, общую для всех потоков. При параллельном запуске тестов два теста могут одновременно устанавливать разные значения и мешать друг другу. В этой книге мы выбираем простой путь:make ciзапускает тесты с флагом--test-threads=1, что устраняет возможную проблему.
# Makefile - CHANGED
ci:
@cargo fmt --check && cargo clippy -- -D warnings && cargo test -- --test-threads=1
Результат
$ mkdir -p /tmp/tq-test
$ TQ_DATA_DIR=/tmp/tq-test cargo run -- list
Список пустой: /tmp/tq-test не содержит tasks.json. Это ожидаемо - переменная меняет
то, куда смотрит tq, а не то, что там лежит.
Неверный путь:
$ TQ_DATA_DIR=/nonexistent cargo run -- list
tq: parse error: data_dir '/nonexistent' does not exist; create it with: mkdir -p /nonexistent
make ci проходит.
Полный код
tqдля этой главы -5-interface/03-what-the-air-carries/.