Создание CLI-приложений на Rust с вызовом CoinMarketCap API

О чём эта статья
В статье рассматривается практический путь создания CLI-инструмента на Rust для получения котировок криптовалют через CoinMarketCap API. Показаны: инициализация проекта, зависимости, структуры данных для serde, пример асинхронного запроса с reqwest и простой CLI-парсер с использованием std::env. В конце — рекомендации по тестированию, безопасности и развёртыванию.
Почему CLI и почему Rust
CLI — лёгкий и предсказуемый способ автоматизации, интеграции в пайплайны и удалённого администрирования. Rust подходит для CLI по ряду причин:
- высокая производительность и низкий оверхед;
- безопасность памяти без сборщика мусора;
- богатая экосистема для работы с вводом-выводом, сериализацией и асинхронностью;
- удобная кроссплатформенность и простая доставка бинара.
Краткое определение: CLI — текстовый интерфейс для взаимодействия с программой через терминал.
Начало проекта
Создайте новый проект Cargo для CLI:
cargo new crypto_cliЭто создаст каркас проекта с папкой src и файлом Cargo.toml.
Зависимости
Откройте Cargo.toml и добавьте зависимости для асинхронных HTTP-запросов и сериализации JSON:
[dependencies]
# Асинхронный рантайм
tokio = { version = "1.15", features = ["full"] }
# HTTP-клиент с поддержкой async
reqwest = { version = "0.11", features = ["json"] }
# Сериализация/десериализация
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"Примечание: версии указаны демонстративно согласно примерам; перед установкой проверьте актуальные версии в crates.io.
Структура проекта
Разбейте код по файлам для разделения ответственности:
touch src/api.rs src/cli.rsФайл main.rs будет вызывать функцию CLI, а api.rs будет содержать логику работы с внешним API.
Работа с CoinMarketCap API через reqwest
CoinMarketCap предоставляет платный и бесплатный доступ к данным через ключ API. Зарегистрируйтесь на сайте CoinMarketCap, создайте ключ и запомните лимиты запросов для вашей подписки.
Важно: не храните API-ключи в открытом репозитории — используйте переменные окружения или секреты CI/CD.
Описание конечной точки
Мы используем конечную точку /v2/cryptocurrency/quotes/latest, чтобы получить актуальные котировки для списка id-ов криптовалют. Ответ API содержит вложенные объекты; для удобной работы используем serde для десериализации только нужных полей.
Структуры данных (serde)
Ниже пример структур для извлечения id, name, symbol и quote. Подстройте поля под реальную схему ответа API: здесь показана упрощённая модель.
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize)]
pub struct ApiResponse {
pub data: Data,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Data {
// В ответе объекты ключи — строки с id, например "1", "1027" и т.д.
#[serde(rename = "1")]
pub crypto_1: Cryptocurrency,
#[serde(rename = "2")]
pub crypto_2: Cryptocurrency,
#[serde(rename = "3")]
pub crypto_3: Cryptocurrency,
#[serde(rename = "4")]
pub crypto_4: Cryptocurrency,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Cryptocurrency {
pub id: u32,
pub name: String,
pub symbol: String,
pub quote: Quote,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Quote {
pub USD: QuoteDetails,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct QuoteDetails {
pub price: f64,
pub volume_24h: f64,
}Примечание: если число криптовалют динамическое, вместо фиксированных полей используйте HashMap
Асинхронный запрос с reqwest
Ниже пример функции, которая делает GET-запрос и десериализует JSON напрямую в структуру через response.json().await. Обратите внимание на обработку ошибок и передачу API-ключа через заголовок.
use reqwest::Client;
use reqwest::Error;
use crate::api::ApiResponse; // скорректируйте путь согласно размещению типов
pub async fn crypto() -> Result<(), Error> {
let client = Client::new();
let url = "https://pro-api.coinmarketcap.com/v2/cryptocurrency/quotes/latest";
let params = [
("id", "1,2,3,4"),
("convert", "USD"),
];
let response = client
.get(url)
.header("X-CMC_PRO_API_KEY", std::env::var("CMC_API_KEY").unwrap_or_default())
.query(¶ms)
.send()
.await?;
// Десериализация JSON тела ответа в структуру
let result: ApiResponse = response.json().await?;
println!("{:#?}", result);
Ok(())
}Важно: используйте std::env::var(“CMC_API_KEY”) для получения ключа из переменной окружения, не хардкодьте ключ в коде.
Реализация CLI-парсера
Для простого проекта можно использовать std::env::args. Для более сложных CLI рекомендую библиотеки clap или structopt.
Пример простого cli.rs, который вызывает функцию crypto при аргументе “crypto”:
use std::env;
use crate::api::crypto;
pub async fn cli() {
let args: Vec = env::args().collect();
if args.len() > 1 && args[1] == "crypto" {
if let Err(e) = crypto().await {
eprintln!("Ошибка при запросе: {}", e);
}
} else {
println!("Недопустимая команда. Использование: cargo run -- crypto");
}
} И main.rs, который запускает tokio-рантайм и вызывает CLI:
mod api;
mod cli;
use crate::cli::cli;
#[tokio::main]
async fn main() {
cli().await;
}Результат выполнения
На экране вы увидите распечатанный десериализованный объект ApiResponse. Для production-решений вывод форматируйте компактнее и проверяйте значения на наличие ошибок.
Практические советы и подводные камни
Важно:
- Следите за лимитами API; при превышении сервис вернёт код ошибки и/или пустые данные.
- Обрабатывайте ошибки сети и таймауты. У reqwest есть настройки таймаута на клиенте.
- Валидация входных данных: никогда не доверяйте внешнему API целиком — проверяйте основные поля до использования.
- Для динамических ключей в data используйте коллекции (HashMap) вместо статических полей.
Когда такой подход не подойдёт:
- Если нужен интерактивный интерфейс с поддеревом команд — используйте clap/structopt.
- Если объём данных очень большой и нужен стриминг — используйте стриминг-ответы и парсинг по частям.
Безопасность и хранение секретов
Рекомендации:
- Храните ключи в переменных окружения (например, CMC_API_KEY).
- Для CI/CD используйте секреты провайдера (GitHub Actions Secrets, GitLab CI/CD variables).
- Ограничьте права ключа и пользуйтесь ролями/политиками, если провайдер поддерживает.
- Логи: не выводите API-ключи в открытые логи.
Тестирование и критерии приёмки
Критерии приёмки
- Приложение корректно запускается командой cargo run – crypto.
- При наличии валидного API-ключа и сети возвращаются данные для указанных id.
- Ошибки сети или неподдерживаемые ответы корректно обрабатываются и логируются.
- API-ключ не хранится в репозитории.
Минимальные тесты
- Тест интеграции: мок ответа API и проверка десериализации в ApiResponse.
- Тест CLI: запуск функции cli с подменой аргументов (unit-test) и проверка поведения при отсутствии аргумента.
- Тест на ошибку: имитация 429/500 ответа и проверка корректной обработки.
Чеклист по ролям
Для разработчика
- Настроить переменную окружения CMC_API_KEY.
- Реализовать и покрыть unit-тестами десериализацию.
- Обработать ошибки запроса и таймаут.
Для DevOps
- Настроить секреты в системе CI/CD.
- Ограничить из окружения доступ к секретам и логам.
- Настроить мониторинг ошибки 4xx/5xx для API-запросов.
Для QA
- Проверить поведение при неверном ключе.
- Проверить корректность вывода при отсутствии сети.
Советы по расширению и альтернативы
- Парсинг аргументов: замените std::env на clap для удобства и автогенерации help.
- Кэширование ответов: добавьте локальный кеш (файл или Redis) при частых запросах и строгих лимитах.
- Масштабирование: для массовых запросов используйте ограничение параллелизма (tokio::spawn + Semaphore).
Совместимость и миграция
- reqwest 0.11 и tokio 1.x совместимы в большинстве примеров; при обновлении версий проверьте изменение API (async/await поддерживается).
- serde сохраняет совместимость при работе с деривацией; при изменении схемы ответа используйте опциональные поля (Option
).
Краткая методология разработки (мини-SOP)
- Создать проект cargo new.
- Добавить зависимости и настроить переменные окружения.
- Спроектировать структуры данных (начать с минимального набора полей).
- Реализовать асинхронный запрос с reqwest и десериализацию.
- Добавить CLI-обёртку и обработку аргументов.
- Написать unit- и интеграционные тесты (моки внешних запросов).
- Настроить CI, секреты и деплой.
Короткое резюме
Rust — надёжный выбор для CLI-инструментов благодаря скорости и безопасности. Комбинация tokio + reqwest + serde даёт простую и мощную основу для интеграции с API вроде CoinMarketCap. Следуйте правилам хранения секретов и покрывайте код тестами.
Важно: перед публикацией проверьте актуальные версии зависимостей и лимиты API.
Похожие материалы
Устранение прерывистого воспроизведения видео
Home Assistant на Raspberry Pi — локальный умный дом
Отключение фоновых приложений в Windows 11
Исправить Steam: Files failed to validate
Автосканирование USB в Microsoft Defender