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

Создание CLI на Rust с запросами к CoinMarketCap

6 min read Development Обновлено 08 Jan 2026
CLI на Rust: запросы к CoinMarketCap
CLI на Rust: запросы к CoinMarketCap

Почему CLI остаётся важным инструментом

Командная строка (CLI) — лёгкий, быстрый и скриптуемый интерфейс для взаимодействия с системами, автоматизации и удалённого администрирования. CLI особенно полезен для разработчиков, SRE и инженеров автоматизации: оно легко интегрируется в конвейеры CI/CD, запускается на серверах без GUI и хорошо подходит для быстрой отладки.

Короткое определение: CLI — текстовая программа, управляемая через аргументы и стандартный ввод/вывод.

Почему Rust для CLI

  • Надёжность памяти: компилятор Rust ловит многие ошибки на этапе компиляции.
  • Высокая производительность и низкие накладные расходы.
  • Экосистема: mature‑крейты для парсинга аргументов, работы с терминалом и сериализации.
  • Хорошая поддержка асинхронности через Tokio и совместимость с reqwest.

Краткая подсказка: если нужен быстрый, безопасный и переносимый бинарник — Rust отличный выбор.

Основные варианты для построения CLI в Rust

Основные библиотеки для парсинга аргументов и взаимодействия с терминалом:

  • Clap — полнофункциональная, широко используемая библиотека.
  • StructOpt — удобная обёртка над Clap (часто уже объединена с Clap в новых версиях).
  • Termion / Crossterm — для работы с выводом в терминал и управления курсором.

Противопоказание: для очень простых однофайловых утилит можно обойтись std::env::args(), но теряете удобства парсинга, валидирования и помощи пользователю.

Начало проекта: Cargo и зависимости

Создайте новый проект с помощью Cargo:

cargo new crypto_cli

Откройте Cargo.toml и добавьте зависимости (версии из примера можно обновить):

[dependencies]
# Асинхронный рантайм
tokio = { version = "1.15", features = ["full"] }
# HTTP-клиент с поддержкой async и JSON
reqwest = { version = "0.11", features = ["json"] }
# Сериализация/десериализация
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
# По желанию: clap = "3" для парсинга аргументов

Важно: обновляйте версии в соответствии с текущими выпусками на crates.io.

Структура проекта и выделение модулей

Рекомендуемая простая структура:

  • src/main.rs — точка входа и вызов CLI
  • src/cli.rs — разбор аргументов и маршрутизация команд
  • src/api.rs — логика HTTP‑запросов и десериализации

Создайте файлы:

touch src/api.rs src/cli.rs

Разделение помогает тестировать модули отдельно и поддерживать код.

Вызов API CoinMarketCap с помощью Reqwest

Логотип CoinMarketCap на фоне графика курсов криптовалют

CoinMarketCap предоставляет REST API с данными о котировках, исторических ценах и метаданных. Чтобы работать с приватными ключами, зарегистрируйтесь на их сайте и получите API‑ключ на странице разработчика.

Рекомендуемая практика: НЕ храните ключ в коде. Используйте переменные окружения или менеджер секретов.

Пример структур данных для десериализации ответа

Ниже — пример структур в Rust, адаптированных для serde. Этот код показывает, как можно описать ожидаемые поля ответа. В реальном мире структура ответа может быть динамической (словарь с id), поэтому в примерах мы также укажем альтернативы.

use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize)]
struct ApiResponse {
    data: Data,
}

#[derive(Debug, Deserialize, Serialize)]
struct Data {
    // Пример статической привязки для первых четырёх id
    #[serde(rename = "1")]
    crypto_1: Cryptocurrency,

    #[serde(rename = "2")]
    crypto_2: Cryptocurrency,

    #[serde(rename = "3")]
    crypto_3: Cryptocurrency,

    #[serde(rename = "4")]
    crypto_4: Cryptocurrency,
}

#[derive(Debug, Deserialize, Serialize)]
struct Cryptocurrency {
    id: u32,
    name: String,
    symbol: String,
    quote: Quote,
}

#[derive(Debug, Deserialize, Serialize)]
struct Quote {
    USD: QuoteDetails,
}

#[derive(Debug, Deserialize, Serialize)]
struct QuoteDetails {
    price: f64,
    volume_24h: f64,
}

Примечание: если API возвращает динамическое поле data: { “1”: {…}, “2”: {…} }, удобнее использовать HashMap:

use std::collections::HashMap;

#[derive(Debug, Deserialize)]
struct ApiResponseMap {
    data: HashMap,
}

Такой подход гибче, если набор id заранее неизвестен.

Асинхронный HTTP‑запрос с обработкой ответа

Ниже — скорректированный и безопасный пример запроса с использованием переменной окружения для ключа и методом .json() для автоматической десериализации.

use reqwest::Client;
use reqwest::Error;
use std::env;

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"),
    ];

    // Получаем API-ключ из переменной окружения CMC_API_KEY
    let api_key = env::var("CMC_API_KEY").expect("Set CMC_API_KEY env variable");

    let response = client
        .get(url)
        .header("X-CMC_PRO_API_KEY", api_key)
        .query(¶ms)
        .send()
        .await?;

    // Десериализуем JSON в структуру (требует serde в зависимостях)
    let result: ApiResponse = response.json().await?;

    println!("{:#?}", result);
    Ok(())
}

Объяснение: client.get(…).header(…).query(…).send().await? — отправляет запрос и возвращает ответ. response.json().await? автоматически десериализует тело ответа в указанный тип.

Важно: обрабатывать ошибки сетевого уровня и случаев, когда API вернёт код 4xx/5xx — в production‑коде добавьте проверку response.status() и подробную обработку ошибок.

Безопасное хранение API‑ключей

Рекомендации:

  • Используйте переменные окружения (например, CMC_API_KEY) в окружениях разработки и CI.
  • В production используйте менеджеры секретов (Vault, AWS Secrets Manager, GCP Secret Manager).
  • Не коммитьте ключи в репозиторий и добавьте исключения в .gitignore.

Пример экспорта переменной в Unix‑окружении:

export CMC_API_KEY="ваш_api_ключ"

В Windows PowerShell:

$env:CMC_API_KEY = "ваш_api_ключ"

Получение аргументов командной строки

В простом варианте можно использовать std::env::args(). Для более мощного парсинга — clap.

Пример cli.rs с простым парсингом и вызовом функции 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!("Invalid command. Usage: cargo run crypto");
    }
}

А точка входа в main.rs:

mod api;
mod cli;
use crate::cli::cli;

#[tokio::main]
async fn main() {
    cli().await;
}

Запуск в режиме разработки:

cargo run -- crypto

(Обратите внимание: после двойного тире все параметры передаются в программу.)

Пример вывода запроса с консоли

Обработка ошибок и надёжность

  • Проверяйте HTTP‑статусы: response.status().is_success().
  • Обрабатывайте таймауты и сетевые ошибки: используйте конфигурацию клиента с .timeout(…).
  • В production добавьте ретраи и экспоненциальную задержку при ошибках 5xx.
  • Логируйте ошибки с контекстом, но не логи API‑ключи.

Тестирование и критерии приёмки

Критерии приёмки минимального функционала:

  • Программа принимает команду crypto и возвращает десериализуемый объект.
  • При отсутствии ключа выводится понятная ошибка.
  • Обработка ошибок сети и API реализована (статус != 200 => сообщение об ошибке).

Тесты:

  • Unit test для функций парсинга ответа с фиктивным JSON.
  • Интеграционный тест с mock‑сервером (wiremock, httpmock).

Альтернативные подходы

  • Синхронный клиент: если проект не требует concurrency, можно использовать синхронные библиотеки — меньше сложности, но теряется асинхронная масштабируемость.
  • GraphQL/WS: если API поддерживает WebSocket или GraphQL, можно получать обновления в реальном времени.
  • Использовать готовые SDK от провайдера (если доступны).

Шаблоны и чек‑листы для ролей

Разработчик:

  • Настроить Cargo.toml и зависимости.
  • Реализовать api.rs и cli.rs.
  • Добавить обработку ошибок и тесты.
  • Добавить документацию использования.

Оператор/DevOps:

  • Настроить переменные окружения в CI/CD.
  • Настроить мониторинг и алерты.
  • Регулярно обновлять зависимости и проверять уязвимости.

QA:

  • Написать unit/integration тесты.
  • Проверить поведение при недоступности API и при таймаутах.

Сниппет: частые команды Cargo

cargo build          # собрать проект
cargo run -- crypto  # запустить с аргументом
cargo test           # запустить тесты
cargo fmt            # отформатировать код
cargo clippy         # запустить линтер

Мини‑методология разработки CLI на Rust (быстрый план из 5 шагов)

  1. Спроектировать интерфейс команд и аргументов (что принимает пользователь).
  2. Определить структуру проекта и API‑слой.
  3. Настроить безопасность (секреты, переменные окружения).
  4. Реализовать функционал и добавить логирование/обработку ошибок.
  5. Тесты, CI и деплой как single‑binary в target окружение.

Decision tree для выбора подхода (Mermaid)

flowchart TD
    A[Нужен CLI?] -->|Да| B{Требуется асинхронность}
    B -->|Да| C[Использовать Tokio + reqwest]
    B -->|Нет| D[Использовать std + синхронный клиент]
    C --> E{Нужна сложная обработка аргументов?}
    E -->|Да| F[Clap]
    E -->|Нет| G[std::env::args'']
    D --> G

Совместимость, миграция и расширение

  • Если в будущем планируете GUI или веб, выделяйте бизнес‑логику в отдельные модули, чтобы её можно было переиспользовать в Actix/Rocket.
  • Обновление зависимостей: проверьте мажорные версии Tokio и Reqwest — они могут содержать несовместимые изменения.

Безопасность и приватность

  • Не логируйте содержимое API‑ключей или чувствительные поля ответа.
  • Для пользовательских данных проверьте требования GDPR/местного законодательства перед передачей данных третьим сторонам.

Когда подход с Rust + reqwest может не сработать

  • Если вам нужен быстрый прототип с минимальными зависимостями — скрипт на Python/Node иногда быстрее в разработке.
  • Если важно, чтобы приложение работало на старых системах без возможности установки бинарников — придётся учитывать целевую платформу.

Заключение

Rust даёт сильные гарантии безопасности и эффективности при разработке CLI. В связке с Tokio, Reqwest и Serde вы получаете современный стек для работы с сетевыми API. Важно проектировать безопасное хранение ключей, тщательно обрабатывать ошибки и писать тесты.

В следующих шагах: замените статическое перечисление id на динамическую модель (HashMap), подключите clap для удобного парсинга аргументов и вынесите конфигурацию в отдельный модуль.

Важное: перед публикацией убедитесь, что в бинарник не попал ваш API‑ключ и что в репозитории нет секретов.

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

  • Создали базовую структуру CLI на Rust.
  • Сделали асинхронный запрос к CoinMarketCap и десериализовали ответ.
  • Добавили рекомендации по безопасности, тестированию и расширению.
Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

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

RDP: полный гид по настройке и безопасности
Инфраструктура

RDP: полный гид по настройке и безопасности

Android как клавиатура и трекпад для Windows
Гайды

Android как клавиатура и трекпад для Windows

Советы и приёмы для работы с PDF
Документы

Советы и приёмы для работы с PDF

Calibration в Lightroom Classic: как и когда использовать
Фото

Calibration в Lightroom Classic: как и когда использовать

Отключить Siri Suggestions на iPhone
iOS

Отключить Siri Suggestions на iPhone

Рисование таблиц в Microsoft Word — руководство
Office

Рисование таблиц в Microsoft Word — руководство