كيف تقرأ برنامج Rust بياناته الوصفية من Cargo.toml
\n\nهل سبق لك أن كتبت أداة CLI بلغة Rust وأردت أن تعرض عند تشغيلها مع العلم --version الإصدار الحقيقي الموجود في Cargo.toml بدلاً من "0.1.0"؟ أو ربما احتجت إلى تسجيل اسم الكريت ووصفه في السجلات — ولكن كان سحب هذه النصوص من الكود غير مريح وعرضة للأخطاء؟
هذا السؤال من أكثر الأسئلة شيوعاً على Stack Overflow (آلاف المشاهدات!). وليس من دون سبب: قد يبدو قراءة ملف الإعدادات الخاص بك أمراً بسيطاً، لكن في Rust هناك بعض التحديات، والأهم من ذلك، عدة طرق أنيقة للحل.
\n\nفي هذا المقال، سنستعرض ثلاث طرق رئيسية للحصول على البيانات الوصفية لحزمة Cargo مباشرة من الكود: من الماكرو البسيط env!() إلى الطريقة المرنة build.rs. ستتعلم كيفية قراءة الإصدار واسم الكريت والوصف بشكل صحيح، وكيفية تجنب الأخطاء الشائعة.
لماذا تحتاج هذا؟ ثلاثة سيناريوهات نموذجية
\n\nقبل الغوص في الكود، دعنا نفهم لماذا نريد الوصول إلى البيانات الوصفية للحزمة. إليك ثلاث حالات واقعية:
\n\n- \n
- أدوات CLI: الأمر
myapp --versionيجب أن يعرض الإصدار منCargo.toml، وليس قيمة ثابتة. \n - السجلات والقياس عن بعد: عند بدء التطبيق، من المفيد تسجيل اسم الكريت وإصداره في السجل — مما يسهل عملية التصحيح. \n
- التوليد التلقائي للتوثيق: يمكنك إدراج وصف الكريت في رسالة المساعدة أو ملف README الذي يتم إنشاؤه ديناميكياً. \n
الآن بعد أن فهمنا "لماذا"، ننتقل إلى "كيف".
\n\nالطريقة الأولى: ماكرو env!() — الأبسط والأكثر شيوعاً
\n\nالطريقة الأكثر مباشرة هي استخدام الماكرو المدمج env!(). يتيح لك قراءة متغيرات البيئة التي يضبطها Cargo تلقائياً أثناء التجميع. إليك قائمة بأكثرها فائدة:
- \n
CARGO_PKG_NAME— اسم الحزمة (مثال: "my_cool_app") \n CARGO_PKG_VERSION— الإصدار (مثال: "0.2.1") \n CARGO_PKG_DESCRIPTION— الوصف من حقلdescriptionفيCargo.toml\n CARGO_PKG_AUTHORS— قائمة المؤلفين مفصولة بنقطتين \n CARGO_PKG_LICENSE— الترخيص \n CARGO_PKG_REPOSITORY— رابط المستودع \n
مثال على الاستخدام:
\n\nfn main() {\n let name = env!("CARGO_PKG_NAME");\n let version = env!("CARGO_PKG_VERSION");\n\n println!("{} v{}", name, version);\n // المخرجات: my_cool_app v0.2.1\n}\n\n\nمهم: env!() هو ماكرو وقت التجميع. إذا لم يتم تعيين متغير البيئة، فلن يتم تجميع البرنامج! هذه ميزة (أمان الأنواع) وعيب (لا يمكنك عمل fallback إلى قيمة ثابتة بدون حيل إضافية).
إذا كنت بحاجة إلى الإصدار فقط لأغراض التصحيح، يمكنك استخدام option_env!() الذي يُرجع Option<&str>:
let version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");\nprintln!("Version: {}", version);\n\n\nهذه الطريقة مثالية لـ 90% من الحالات. لا تتطلب تبعيات إضافية وتعمل "مباشرة من الصندوق".
\n\nالطريقة الثانية: include_str!() + التحليل اليدوي — للتحكم الكامل
\n\nأحياناً لا تحتاج فقط إلى قراءة حقل واحد، بل إلى الحصول على بيانات منظمة من Cargo.toml. مثلاً، جميع التبعيات أو الكلمات المفتاحية. في هذه الحالة، يمكنك قراءة الملف نفسه كنص وتحليله.
fn main() {\n let cargo_toml = include_str!("../Cargo.toml");\n // هنا يمكنك استخدام محلل toml، لكن للمثال — بحث بسيط\n if let Some(line) = cargo_toml.lines().find(|l| l.starts_with("version =")) {\n let version = line.trim_start_matches("version = \\"").trim_end_matches('"');\n println!("Version from file: {}", version);\n }\n}\n\n\nهذه الطريقة تعطيك مرونة كاملة، لكنها تتطلب منك التعامل مع تحليل النص بنفسك. يمكنك استخدام مكتبة مثل toml لتحليل أكثر احترافية.