Конфигурация приложений Rust: библиотеки config, envy и dotenvy
При разработке приложений на Rust управление конфигурацией — одна из ключевых задач. От правильного подхода к загрузке настроек зависит гибкость, безопасность и удобство развертывания. В экосистеме Rust существует несколько зрелых решений, которые позволяют читать конфигурацию из файлов, переменных окружения и других источников. В этой статье мы рассмотрим три популярных инструмента: config, envy и dotenvy.
1. Библиотека config — универсальный менеджер конфигурации
config — это мощная библиотека для загрузки иерархических конфигураций. Она поддерживает несколько форматов (JSON, TOML, YAML, HJSON, INI), а также переменные окружения и удаленные источники. Основная идея — объединить все источники в единое дерево настроек.
Установка
[dependencies]
config = "0.14"
serde = { version = "1.0", features = ["derive"] }
Пример использования
use config::{Config, File, Environment};
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct AppConfig {
server: ServerConfig,
database: DatabaseConfig,
}
#[derive(Debug, Deserialize)]
struct ServerConfig {
host: String,
port: u16,
}
#[derive(Debug, Deserialize)]
struct DatabaseConfig {
url: String,
pool_size: u32,
}
fn load_config() -> Result<AppConfig, config::ConfigError> {
let cfg = Config::builder()
.add_source(File::with_name("config/default")) // Базовые настройки
.add_source(File::with_name("config/local").required(false)) // Локальные переопределения
.add_source(Environment::with_prefix("APP")) // Переменные окружения с префиксом APP_
.build()?;
cfg.try_deserialize()
}
fn main() {
match load_config() {
Ok(config) => println!("Loaded config: {:?}", config),
Err(e) => eprintln!("Config error: {}", e),
}
}
Библиотека автоматически объединяет источники: значения из последующих источников перезаписывают предыдущие. Это позволяет легко организовать иерархию: default → environment → local override.
2. Библиотека envy — конфигурация из переменных окружения
envy — минималистичная библиотека, которая загружает конфигурацию исключительно из переменных окружения. Идеально подходит для 12-factor приложений, где настройки должны быть отделены от кода.
Установка
[dependencies]
envy = "0.4"
serde = { version = "1.0", features = ["derive"] }
Пример использования
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct AppConfig {
#[serde(rename = "SERVER_HOST")]
host: String,
#[serde(rename = "SERVER_PORT")]
port: u16,
#[serde(rename = "DATABASE_URL")]
db_url: String,
}
fn load_config() -> Result<AppConfig, envy::Error> {
envy::from_env::<AppConfig>()
}
fn main() {
match load_config() {
Ok(config) => println!("Env config: {:?}", config),
Err(e) => eprintln!("Envy error: {}", e),
}
}
Для работы достаточно установить переменные окружения:
export SERVER_HOST=127.0.0.1
export SERVER_PORT=8080
export DATABASE_URL=postgres://user:pass@localhost/db
envy поддерживает вложенные структуры через разделители (например, DATABASE__HOST), а также опциональные поля с значением по умолчанию.
3. Библиотека dotenvy — загрузка .env файлов
dotenvy — это форк популярной библиотеки dotenv, предназначенный для загрузки переменных окружения из .env файлов. Особенно полезна при разработке, когда нужно быстро задать локальные настройки.
Установка
[dependencies]
dotenvy = "0.15"
Пример использования
use std::env;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Загружаем .env файл из текущей директории
dotenvy::dotenv()?;
// Теперь можно читать переменные стандартным способом
let host = env::var("SERVER_HOST").unwrap_or_else(|_| "localhost".to_string());
let port: u16 = env::var("SERVER_PORT")
.unwrap_or_else(|_| "3000".to_string())
.parse()?;
println!("Server running on {}:{}", host, port);
Ok(())
}
Содержимое .env файла:
SERVER_HOST=0.0.0.0
SERVER