تكوين تطبيقات Rust: مكتبتا config و dotenv
أي تطبيق حديث يحتاج إلى نظام تكوين مرن: إعدادات قاعدة البيانات، مفاتيح API، منافذ الخادم، مستويات التسجيل وغيرها من المعلمات. في نظام Rust البيئي، توجد أدوات قوية لهذا الغرض — كريتات config و dotenv. تسمح بدمج الإعدادات من مصادر مختلفة (ملفات، متغيرات بيئة، وسائط سطر الأوامر) في بنية واحدة آمنة من حيث النوع.
لماذا نحتاج إلى مكتبة منفصلة؟
Rust هي لغة ذات كتابة ثابتة قوية. قراءة السلاسل النصية ببساطة من std::env::var() تؤدي إلى العديد من الفحوصات والتحويلات اليدوية. مكتبة config تؤتمت هذه العملية:
- دعم الصيغ: YAML، JSON، TOML، HJSON، INI، RON
- بنية هرمية للإعدادات مع إمكانية التجاوز
- إلغاء التسلسل تلقائياً إلى بنى مخصصة عبر
serde - دمج عدة مصادر مع أولويات (ملف < env < CLI)
التثبيت
أضف إلى Cargo.toml:
[dependencies]config = "0.14"serde = { version = "1.0", features = ["derive"] }dotenv = "0.15"مثال أساسي: القراءة من ملف و env
لننشئ بنية لتكوين التطبيق:
use serde::Deserialize;use config::Config;
#[derive(Debug, Deserialize)]struct AppConfig { server: ServerConfig, database: DatabaseConfig,}
#[derive(Debug, Deserialize)]struct ServerConfig { host: String, port: u16, workers: u8,}
#[derive(Debug, Deserialize)]struct DatabaseConfig { url: String, pool_size: u32,}
fn main() -> Result<(), config::ConfigError> { // تحميل التكوين let config = Config::builder() .add_source(config::File::with_name("config/default")) .add_source(config::Environment::with_prefix("APP")) .build()?;
let app_config: AppConfig = config.try_deserialize()?; println!("{:#?}", app_config); Ok(())}ملف config/default.toml:
[server]host = "127.0.0.1"port = 8080workers = 4
[database]url = "postgres://localhost/mydb"pool_size = 10متغيرات البيئة ذات البادئة APP_ تتجاوز القيم من الملف. على سبيل المثال، APP_DATABASE__URL سيغير رابط قاعدة البيانات.
العمل مع dotenv
لتسهيل التطوير، غالباً ما يُستخدم ملف .env. كريت dotenv يقوم بتحميله إلى متغيرات البيئة قبل تهيئة config:
use dotenv::dotenv;
fn main() { dotenv().ok(); // تحميل .env // ثم تهيئة config كما في المثال أعلاه}محتوى .env:
APP_SERVER__PORT=3000APP_DATABASE__POOL_SIZE=20سيناريو متقدم: عدة مصادر
المشاريع الحقيقية تتطلب دمج الإعدادات من أماكن مختلفة. config يسمح بتحديد سلسلة من المصادر ذات الأولوية:
let config = Config::builder() // الإعدادات الأساسية (أولوية منخفضة) .add_source(config::File::with_name("config/default")) // تجاوز لبيئة معينة .add_source(config::File::with_name(&format!("config/{}", env::var("APP_ENV").unwrap_or("development")))) // متغيرات البيئة (أولوية متوسطة) .add_source(config::Environment::with_prefix("APP")) // وسائط سطر الأوامر (أولوية عالية) .add_source(config::File::from(std::env::args().nth(1).unwrap_or_default())) .build()?;أفضل الممارسات
- استخدم البادئات لمتغيرات البيئة لتجنب التعارض مع المتغيرات النظامية