Что такое Clap и зачем он нужен?
Clap (Command Line Argument Parser) — это мощная, быстрая и удобная библиотека для парсинга аргументов командной строки в языке Rust. Она позволяет разработчикам легко определять, какие аргументы, флаги, опции и подкоманды должно принимать их приложение, автоматически генерировать справку (--help), проверять типы данных и обрабатывать ошибки ввода.
Зачем нужен Clap? Вместо того чтобы вручную разбирать std::env::args() и писать десятки строк кода для валидации, Clap делает всю работу за вас. Вы просто описываете структуру вашего CLI, а библиотека берёт на себя парсинг, генерацию сообщений об ошибках и форматирование подсказок. Clap поддерживает:
- Позиционные аргументы (обязательные и опциональные).
- Именованные опции (флаги) с короткими и длинными именами.
- Подкоманды (как в
git commit). - Автодополнение для оболочек (bash, zsh, fish, PowerShell).
- Валидацию значений, значения по умолчанию, множественные значения и многое другое.
Clap — это стандарт де-факто для создания CLI-приложений в экосистеме Rust. Он используется в таких известных проектах, как cargo, ripgrep (rg), fd и многих других.
Установка
Clap — это библиотека для Rust, поэтому она устанавливается через менеджер пакетов cargo. Добавьте следующую строку в ваш файл Cargo.toml:
[dependencies]
clap = { version = "4.5", features = ["derive"] }
Мы рекомендуем использовать derive-макросы (фича derive), так как это самый современный и удобный способ работы с Clap. Если вам нужен только базовый функционал без derive, можно написать:
[dependencies]
clap = "4.5"
После добавления зависимости выполните команду cargo build, чтобы загрузить и скомпилировать библиотеку.
Быстрый старт с примером кода
Давайте создадим простое приложение, которое принимает имя пользователя и опциональный флаг приветствия. Создайте новый проект:
cargo new hello_clap
cd hello_clap
Замените содержимое src/main.rs на следующий код:
use clap::Parser;
/// Простая программа для приветствия
#[derive(Parser)]
#[command(name = "greeter")]
#[command(about = "Приветствует пользователя", long_about = None)]
struct Cli {
/// Имя пользователя
name: String,
/// Количество раз, которое нужно повторить приветствие
#[arg(short = 'c', long = "count", default_value_t = 1)]
count: u8,
}
fn main() {
let cli = Cli::parse();
for _ in 0..cli.count {
println!("Привет, {}!", cli.name);
}
}
Теперь запустите программу:
cargo run -- Мир
# Вывод: Привет, Мир!
cargo run -- Мир -c 3
# Вывод:
# Привет, Мир!
# Привет, Мир!
# Привет, Мир!
cargo run -- --help
# Вывод справки
Как видите, Clap автоматически сгенерировал справку, обработал аргументы и выполнил проверку типов (если передать -c не число, будет ошибка).
Основные методы и функции Clap
Рассмотрим ключевые методы и атрибуты, которые предоставляет Clap. Мы будем использовать подход с derive-макросами, так как он наиболее популярен.
1. #[derive(Parser)]
Что делает: Превращает вашу структуру в парсер командной строки. Это основной макрос, с которого всё начинается. Он автоматически реализует трейт Parser для вашей структуры.
Параметры: Атрибуты структуры (#[command(...)]) и полей (#[arg(...)]).
Пример:
use clap::Parser;
#[derive(Parser)]
#[command(name = "myapp")]
struct Cli {
#[arg(short, long)]
verbose: bool,
}
2. Cli::parse()
Что делает: Запускает процесс парсинга аргументов командной строки (из std::env::args()). Если парсинг не удался (например, не хватает обязательного аргумента или передан неверный тип), программа завершится с сообщением об ошибке и подсказкой.
Параметры: Нет (использует глобальные аргументы).
Пример:
fn main() {
let cli = Cli::parse();
// ... используем cli
}
3. #[arg(short, long)]
Что делает: Атрибут для поля структуры. Делает поле опцией командной строки с коротким (-s) и/или длинным (--long) флагом. Имя флага автоматически генерируется из имени поля (например, поле verbose станет --verbose и -v).
Параметры: short (символ или true для авто), long (строка или true для авто), default_value_t, required, help и другие.
Пример:
struct Cli {
#[arg(short = 'o', long = "output", default_value = "out.txt")]
output_file: String,
#[arg(short, long)]
debug: bool,
}
4. #[arg(default_value_t = ...)]
Что делает: Устанавливает значение по умолчанию для опции, если она не была указана пользователем. default_value_t принимает литерал или выражение, которое вычисляется во время компиляции.
Параметры: Значение того же типа, что и поле.
Пример:
struct Cli {
#[arg(short, long, default_value_t = 8080)]
port: u16,
}
5. #[arg(required = true)]
Что делает: Делает опцию обязательной. Если пользователь не укажет её, Clap выдаст ошибку. Для позиционных аргументов (без short/long) это поведение по умолчанию.
Параметры: true или false.
Пример:
struct Cli {
#[arg(short, long, required = true)]
config: String,
}
6. #[arg(value_parser = ...)]
Что делает: Позволяет задать кастомный парсер для значения. Это может быть встроенный парсер (например, для перечислений) или ваша собственная функция. Очень полезно для валидации.
Параметры: Функция или замыкание, которое принимает &str и возвращает Result<T, String>.
Пример:
fn parse_size(s: &str) -> Result<u64, String> {
let s = s.trim().to_lowercase();
if let Some(num) = s.strip_suffix("kb") {
num.parse::<u64>().map(|n| n * 1024).map_err(|e| e.to_string())
} else if let Some(num) = s.strip_suffix("mb") {
num.parse::<u64>().map(|n| n * 1024 * 1024).map_err(|e| e.to_string())
} else {
s.parse::<u64>().map_err(|_| "Invalid size".to_string())
}
}
struct Cli {
#[arg(short, long, value_parser = parse_size)]
size: u64,
}
7. #[command(subcommand)]
Что делает: Определяет вложенную команду (подкоманду). Поле должно быть перечислением (enum), которое также реализует Subcommand. Позволяет создавать сложные CLI, как у Git или Docker.
Параметры: Атрибут для поля структуры.
Пример:
use clap::{Parser, Subcommand};
#[derive(Parser)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Добавить элемент
Add { name: String },
/// Удалить элемент
Remove { id: u32 },
/// Список всех элементов
List,
}
fn main() {
let cli = Cli::parse();
match cli.command {
Commands::Add { name } => println!("Добавлено: {}", name),
Commands::Remove { id } => println!("Удалено: {}", id),
Commands::List => println!("Список..."),
}
}
8. #[arg(short, long, action = clap::ArgAction::Count)]
Что делает: Используется для подсчёта количества повторений флага. Например, -vvv установит значение 3. Тип поля должен быть u8 или другой целочисленный.
Параметры: clap::ArgAction::Count.
Пример:
struct Cli {
#[arg(short, long, action = clap::ArgAction::Count)]
verbose: u8,
}
fn main() {
let cli = Cli::parse();
println!("Уровень детализации: {}", cli.verbose);
}
9. #[arg(num_args = 1..)]
Что делает: Позволяет указать, сколько значений может принимать опция. Можно задать диапазон, например, 1..=3 (от 1 до 3) или .. (любое количество). Поле должно быть коллекцией (Vec<T>).
Параметры: Диапазон или число.
Пример:
struct Cli {
#[arg(short, long, num_args = 1..)]
files: Vec<String>,
}
// Использование: program --files a.txt b.txt c.txt
10. #[arg(env = "ENV_VAR_NAME")]
Что делает: Позволяет получать значение аргумента из переменной окружения, если он не был указан в командной строке. Это удобно для конфиденциальных данных (пароли, токены).
Параметры: Имя переменной окружения.
Пример:
struct Cli {
#[arg(short, long, env = "DATABASE_URL")]
database_url: String,
}
// Можно передать: program --database-url postgres://...
// Или установить переменную: DATABASE_URL=postgres://... program
Реальный практический пример использования
Давайте создадим утилиту для работы с задачами (task manager). Она будет поддерживать добавление, удаление, отметку выполнения и список задач. Данные будем хранить в простом JSON-файле.
Полный код (src/main.rs):
use clap::{Parser, Subcommand};
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
#[derive(Debug, Serialize, Deserialize)]
struct Task {
id: u32,
description: String,
done: bool,
}
#[derive(Debug, Serialize, Deserialize)]
struct TaskList {
tasks: Vec<Task>,
next_id: u32, Также в библиотеке
Swagger/OpenAPI генерация для TypeScript: лучшие инструменты
Геолокация и карты в Java: библиотеки, API и примеры кода
mapstructure для Go: полное руководство с примерами
Обработка изображений на C#: resize, crop библиотекой ImageSharp
Juniper: Элегантная GraphQL библиотека на Rust для современных веб-приложений