1. مقدمة
في تطوير البرمجيات الحديثة، تعتبر أدوات واجهة سطر الأوامر (CLI) ضرورية لتحسين إنتاجية المطورين بشكل كبير. في الماضي، كانت سكربتات الصدفة (Shell scripts) أو Python و Ruby هي السائدة، ولكن في السنوات الأخيرة، رسخت Rust مكانتها كمعيار فعلي لتطوير أدوات CLI.
في هذه المقالة، سنشرح بالتفصيل كيفية بناء أدوات CLI عملية “تعمل بسرعة فائقة ويمكن تطويرها بسرعة فائقة” باستخدام Rust، من الأساسيات إلى التطبيقات المتقدمة. لن نكتفي بصنع شيء يعمل فقط، بل سنغطي بشكل شامل معالجة الأخطاء القوية والمناسبة للمستوى التجاري، وطلبات API السريعة باستخدام المعالجة غير المتزامنة، وتنفيذ شريط تقدم (Progress bar) لتحسين تجربة المستخدم (UX).
بحلول نهاية هذه المقالة، ستكون قد أتقنت حزمة تقنيات Rust المتقدمة التالية، وستتمكن من نشر أدوات CLI القوية الخاصة بك للعالم.
2. لماذا نختار Rust لتطوير أدوات CLI؟
السبب وراء التقييم العالي لـ Rust في تطوير CLI ليس مجرد أنها “شائعة”. بل هناك مزايا تقنية ومعمارية واضحة.
2.1. ملفات تنفيذية أحادية والترجمة المتقاطعة
عند توزيع أداة مكتوبة بـ Python أو Node.js، يجب أن يكون لدى المستخدم بيئة تشغيل (مفسر Python أو Node.js) مثبتة في بيئته. بالإضافة إلى ذلك، ليس من النادر أن تعاني من تعارضات في إصدارات الحزم التابعة (ما يسمى “جحيم التبعيات”). من ناحية أخرى، تُترجم Rust مسبقًا إلى كود أصلي، مما ينتج ملفًا تنفيذيًا واحدًا يتضمن جميع التبعيات. يمكن للمستخدمين استخدام الأداة بمجرد تنزيل الملف الثنائي ووضعه، مما يقلل بشكل كبير من عقبات التثبيت. بالإضافة إلى ذلك، الترجمة المتقاطعة سهلة، ومن الممكن بناء ملفات ثنائية لأنظمة Windows، وmacOS، وLinux في بيئة CI واحدة.
2.2. سرعة تنفيذ هائلة واستهلاك منخفض للذاكرة
لا تمتلك Rust جامع قمامة (GC)، ومن خلال التجريد بدون تكلفة تقدم أداءً يعادل C/C++. في أدوات CLI، يرتبط وقت بدء التشغيل القصير بشكل مباشر بتجربة المستخدم. على عكس لغات JVM، لا يوجد وقت إحماء عند بدء التشغيل، وبدء المعالجة في اللحظة التي تضرب فيها الأمر هو ميزة كبيرة.
2.3. الأمان بفضل نظام الأنواع القوي ونموذج الملكية
بفضل نموذج الملكية - وهو أقوى سلاح في Rust - ونظام الأنواع القوي، يتم استبعاد الأخطاء مثل تسرب الذاكرة وتعارض البيانات في وقت الترجمة. تجربة “إذا نجحت الترجمة، فمن شبه المؤكد أنها ستعمل كما هو مقصود” تمنح المطورين شعورًا هائلاً بالأمان عند تطوير تطبيقات تتعامل مباشرة مع موارد النظام مثل أدوات CLI.
3. أقوى الحزم (Crates) المعتمدة في هذا البرنامج التعليمي
يوجد في نظام Rust البيئي العديد من الحزم (المكتبات) الممتازة التي تدعم بقوة تطوير CLI. في هذا البرنامج التعليمي، سنستخدم الحزم التالية، والتي يمكن أن نطلق عليها “الحزمة الذهبية” في تطوير Rust CLI الحديث.
clap: أقوى الحزم وأكثرها شيوعًا في تحليل وسائط سطر الأوامر. بدءًا من الإصدار 4 وما بعده، أصبح التعريف التصريحي باستخدام ماكرو Derive أكثر صقلاً، ويدعم التوليد التلقائي لرسائل المساعدة ونصوص الإكمال التلقائي.tokio: المعيار الفعلي لبيئة التشغيل غير المتزامنة في Rust. يعالج إدخال/إخراج غير متزامن متعدد الخيوط بكفاءة عالية جدًا.reqwest: عميل HTTP عالي الأداء يعمل علىtokio. يمتلك واجهة برمجة تطبيقات سهلة الاستخدام ويجعل من السهل تنفيذ طلبات API غير متزامنة.serde&serde_json: إطار عمل لإجراء عملية التسلسل وإلغاء التسلسل للبيانات. لا غنى عنه لتعيين استجابات JSON الخاصة بـ API إلى هياكل بيانات Rust الآمنة من حيث النوع.indicatif: يوفر شريط تقدم غنيًا وقابلًا للتخصيص. يعرض التقدم البصري للمعالجة غير المتزامنة، مما يحسن تجربة المستخدم الخاصة بـ CLI بشكل كبير.anyhow&thiserror: مزيج قوي لمعالجة الأخطاء. من أفضل الممارسات استخدامthiserrorلتعريف أخطاء المجال داخل المكتبة، وanyhowلتجميع الأخطاء في الطبقة العليا من التطبيق.
يوضح المخطط التالي بنية كيفية تفاعل هذه الحزم داخل التطبيق:
graph TD
A["تطبيق CLI (main)"] --> B["clap (تحليل الوسائط)"]
A --> C["tokio (بيئة التشغيل غير المتزامنة)"]
A --> D["anyhow / thiserror (معالجة الأخطاء)"]
C --> E["reqwest (عميل HTTP)"]
E --> F["serde (تسلسل JSON)"]
A --> G["indicatif (واجهة مستخدم شريط التقدم)"]
4. الخلفية الرياضية للمعالجة غير المتزامنة والأداء
ستقوم الأداة التي نطورها في هذا البرنامج التعليمي بإرسال طلبات بشكل متزامن إلى عدة نقاط نهاية في واجهة برمجة التطبيقات (API). دعونا نراجع الخلفية الرياضية لسبب استخدام بيئة تشغيل غير متزامنة مثل tokio يجعلها أسرع بشكل كبير.
4.1. قانون أمدال (Amdahl’s Law)
يُصاغ معدل تحسين الأداء العام عن طريق التوازي أو اللاتزامن لجزء من النظام بواسطة قانون أمدال كما يلي:
$$ S(N) = \frac{1}{(1 - P) + \frac{P}{N}} $$حيث:
- $S(N)$ هو الحد الأقصى النظري لنسبة زيادة السرعة.
- $P$ هي نسبة الجزء الذي يمكن موازنته (جعله غير متزامن) في البرنامج.
- $N$ هي درجة التوازي للمهام التي يمكن تنفيذها في نفس الوقت.
في حالة الأدوات التي تجلب البيانات من API، فإن معظم وقت التنفيذ يتمثل في انتظار استجابة الشبكة (I/O Bound). لذلك، ستكون قيمة $P$ كبيرة جدًا (على سبيل المثال، $0.95$ أو أكثر). في البرنامج المتزامن، $N = 1$، ولكن باستخدام إدخال/إخراج غير متزامن، يمكن رفع $N$ إلى آلاف المستويات، ومن الناحية النظرية، يزداد $S(N)$ بشكل هائل.
4.2. قانون ليتل (Little’s Law) والإنتاجية
عند معالجة طلبات الشبكة، تنطبق العلاقة التالية على متوسط عدد الطلبات المتزامنة في النظام $L$، ومتوسط الإنتاجية $\lambda$ (عدد العمليات المكتملة لكل وحدة زمنية)، ومتوسط وقت الاستجابة $W$:
$$ L = \lambda W \implies \lambda = \frac{L}{W} $$بمعنى آخر، في بيئة لا مفر فيها من تأخير الشبكة $W$، الطريقة الوحيدة لتحسين إنتاجية النظام $\lambda$ هي زيادة عدد الطلبات التي تتم معالجتها في نفس الوقت $L$. نظرًا لأن مهام Rust غير المتزامنة، على عكس خيوط نظام التشغيل، لها حمل ذاكرة صغير جدًا، فمن السهل توسيع $L$.
5. تصميم الأداة التي سيتم تطويرها: جالب مستودعات GitHub المجمع
كمثال عملي هذه المرة، سنقوم بتطوير أداة gh-stats-fetcher التي تجلب قائمة المستودعات العامة لمستخدم GitHub أو مؤسسة محددة، وتجلب إحصائيات كل منها (عدد النجوم، عدد التفرعات، اللغة، إلخ) بشكل متزامن، وتقوم بتنسيقها وعرضها في الطرفية.
تسلسل تنفيذ الأداة
sequenceDiagram
participant U as "المستخدم"
participant C as "نواة CLI"
participant T as "بيئة تشغيل Tokio"
participant A as "واجهة برمجة تطبيقات GitHub"
U->>C: "تشغيل: gh-stats-fetcher --user rust-lang"
C->>C: "تحليل الوسائط باستخدام clap"
C->>A: "جلب قائمة المستودعات"
A-->>C: "JSON للمستودعات"
C->>T: "توليد مهام غير متزامنة للتفاصيل"
loop "الجلب المتزامن"
T->>A: "جلب التفاصيل /repo/rust-lang/X"
A-->>T: "JSON التفاصيل"
T->>T: "التحليل باستخدام serde"
T->>U: "تحديث التقدم (indicatif)"
end
T-->>C: "إرجاع النتائج المجمعة"
C->>U: "طباعة جدول منسق إلى وحدة التحكم"
6. تهيئة المشروع وإعداد التبعيات
أولاً، سنستخدم Cargo لإنشاء مشروع جديد.
| |
بعد ذلك، أضف التبعيات الضرورية إلى Cargo.toml.
| |
نقطة مهمة: يستخدم
reqwestخيارrustls-tlsبدلاً من الخلفية الافتراضية لـ TLS. هذا يلغي الحاجة إلى المكتبات التي تعتمد على النظام مثل OpenSSL، ويسهل بناء ملف تنفيذي أحادي مرتبط بشكل ثابت بالكامل.
7. مرحلة التنفيذ 1: بناء أساس معالجة الأخطاء
لصنع أداة CLI قوية، يعد تصميم معالجة الأخطاء أمرًا حيويًا. هنا، سنمارس التمييز في استخدام thiserror و anyhow.
سنقوم بتعريف الأخطاء الخاصة بالمجال في src/error.rs.
| |
flowchart LR
E1["reqwest::Error"] --> EH["thiserror (FetcherError::ApiError)"]
E2["serde_json::Error"] --> EH
E3["حد المعدل / 404"] --> EH
EH --> AH["anyhow::Result (إرفاق السياق)"]
AH --> Out["طباعة رسالة خطأ سهلة الاستخدام"]
8. مرحلة التنفيذ 2: تحليل الوسائط بواسطة clap
بعد ذلك، سنقوم بتعريف وسائط CLI. قم بإنشاء src/cli.rs واستخدم ماكرو Derive الخاص بـ clap.
| |
وبهذا، يتم إنشاء رسالة مساعدة جميلة تلقائيًا كما يلي.
| |
9. مرحلة التنفيذ 3: عميل واجهة برمجة التطبيقات وتعيين البيانات
سنقوم بتعيين بيانات JSON المرجعة من واجهة برمجة تطبيقات GitHub إلى هياكل Rust. سنقوم بتنفيذ src/models.rs و src/api.rs.
| |
| |
10. مرحلة التنفيذ 4: المعالجة المتزامنة وشريط التقدم باستخدام tokio و indicatif
هذا هو أبرز جزء في هذه الأداة. سنقوم بتشغيل المعالجة المتزامنة على قائمة المستودعات التي تم الحصول عليها وعرض شريط تقدم جميل.
| |
في هذا الكود، يتم استخدام tokio::spawn لإرسال المهام إلى العمال في الخلفية، وفي نفس الوقت يتم استخدام tokio::sync::Semaphore للحد من عدد طلبات API المنفذة في نفس الوقت (التزامن الافتراضي هو 10). من خلال ذلك، نحن نقلل من مخاطر تجاوز حد معدل API بينما نحقق سرعة تفوق بكثير المعالجة المتزامنة.
11. مواضيع متقدمة: الاختبار والتحسين
11.1. اختبار التكامل لأدوات CLI
لاختبار سلوك أداة CLI نفسها، تعتبر حزمة assert_cmd مفيدة للغاية. قم بإنشاء tests/cli_test.rs وقم باستدعاء الملف التنفيذي الفعلي والتحقق من الإخراج القياسي.
| |
11.2. التحسين الأقصى لبناء الإصدار (Release Build)
على الرغم من أن بناء الإصدار الافتراضي سريع بما يكفي، لتقليل حجم الملف التنفيذي وتحقيق أقصى سرعة تنفيذ، قم بتكوين [profile.release] في Cargo.toml.
| |
بتطبيق هذه الإعدادات، سيكون حجم الملف التنفيذي المُنشأ أصغر بعدة ميغابايت، مما يجعل توزيعه على المستخدمين أسهل.
12. التكامل المستمر/النشر المستمر (CI/CD) والتوزيع (Publishing)
هذه هي الخطوات لتوزيع أداتك المبنية للعالم.
النشر على crates.io
باستخدام Cargo، مدير حزم Rust، يمكنك نشرها في السجل الرسمي ببضعة أوامر فقط.
| |
بعد النشر، سيتمكن المستخدمون حول العالم من تثبيت أداتك باستخدام أمر واحد cargo install gh-stats-fetcher.
الإصدار التلقائي عبر GitHub Actions
قم بإنشاء مسار CI/CD الذي يقوم بتحميل الملفات التنفيذية المترجمة تلقائيًا إلى إصدارات GitHub. اكتب إعدادات مثل الإعدادات التالية في .github/workflows/release.yml. سيؤدي هذا إلى بناء ملفات تنفيذية لأنظمة Linux و macOS و Windows تلقائيًا بمجرد دفع علامة (Tag)، وإرفاقها كأصول إصدار (نحن نحذف وصف YAML التفصيلي هنا لضيق المساحة، ولكن استخدام إجراء مثل taiki-e/upload-rust-binary-action هو أفضل الممارسات الحالية).
13. الخلاصة
في هذه المقالة، شرحنا بالتفصيل سلسلة تدفق تطوير أدوات CLI باستخدام Rust.
- نهج التصميم: أكدنا على أمان وسرعة Rust وميزة الملفات التنفيذية الأحادية.
- اختيار الحزم (Crates): اكتسبنا أسلحة قوية مثل
clapوtokioوserdeوindicatifوthiserrorوanyhow. - الميزة الرياضية للمعالجة المتزامنة: بناءً على قانون أمدال وقانون ليتل، فهمنا نظريًا قوة المعالجة غير المتزامنة.
- التنفيذ والتحسين: جمعنا المعرفة العملية، بدءًا من معالجة الأخطاء القوية ووصولاً إلى التحسين الأقصى للملفات التنفيذية.
تطوير CLI بواسطة Rust هو تجربة رائعة حيث يمكنك ضمان جودة البرنامج منذ مرحلة التصميم من خلال التفاعل مع المترجم. بناءً على الكود الأساسي الذي تم إنشاؤه هذه المرة، يرجى تطوير أداة CLI الأصلية الخاصة بك ونشرها للعالم! برمجة Rust سعيدة!
