Гид по технологиям

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

5 min read Разработка Обновлено 26 Dec 2025
CLI на Rust для CoinMarketCap — быстрый старт
CLI на Rust для CoinMarketCap — быстрый старт

Логотип Rust на фоне человека, работающего за iMac

О чём эта статья

В статье рассматривается практический путь создания 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 на фоне графика

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 для поля data.

Асинхронный запрос с 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)

  1. Создать проект cargo new.
  2. Добавить зависимости и настроить переменные окружения.
  3. Спроектировать структуры данных (начать с минимального набора полей).
  4. Реализовать асинхронный запрос с reqwest и десериализацию.
  5. Добавить CLI-обёртку и обработку аргументов.
  6. Написать unit- и интеграционные тесты (моки внешних запросов).
  7. Настроить CI, секреты и деплой.

Короткое резюме

Rust — надёжный выбор для CLI-инструментов благодаря скорости и безопасности. Комбинация tokio + reqwest + serde даёт простую и мощную основу для интеграции с API вроде CoinMarketCap. Следуйте правилам хранения секретов и покрывайте код тестами.

Важно: перед публикацией проверьте актуальные версии зависимостей и лимиты API.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Устранение прерывистого воспроизведения видео
Видео

Устранение прерывистого воспроизведения видео

Home Assistant на Raspberry Pi — локальный умный дом
Умный дом

Home Assistant на Raspberry Pi — локальный умный дом

Отключение фоновых приложений в Windows 11
Windows

Отключение фоновых приложений в Windows 11

Исправить Steam: Files failed to validate
Техподдержка

Исправить Steam: Files failed to validate

Автосканирование USB в Microsoft Defender
Безопасность

Автосканирование USB в Microsoft Defender

Увеличить задержку отмены отправки в Apple Mail
Руководство

Увеличить задержку отмены отправки в Apple Mail