Создание системы мониторинга на Rust с Ratatui и crossterm

Этот материал — подробное руководство по созданию терминальной системы мониторинга для Linux на Rust, отражающее современные тренды TUI-разработки [medium.com](https://medium.com/@e_moreira/building-interactive-terminal-user-interfaces-with-ratatui-a-comprehensive-guide-to-creating-a-c6f39b0b8742). Вы изучите правильную архитектуру приложений, работу с Ratatui и crossterm, а также сбор и отображение системных метрик.

1. Разработка интерактивного TUI-мониторинга на Rust: от архитектуры до рендеринга

Разработка интерактивного TUI-мониторинга на Rust: от архитектуры до рендеринга

Графические интерфейсы удобны, но когда дело доходит до серверов на базе Linux, терминал остаётся главным инструментом разработчика и системного администратора. Создание TUI (Terminal User Interface) позволяет объединить скорость работы в командной строке с наглядностью графических дашбордов.

В основе современных терминальных интерфейсов на Rust лежат два мощных инструмента: Ratatui для отрисовки виджетов и crossterm для управления самим окном терминала и перехвата нажатий клавиш. Чтобы приложение работало быстро и не зависало, необходимо правильно спроектировать его архитектуру.

Архитектура TUI-приложения

Создание консольного интерфейса требует строгого разделения ответственности. Если смешать логику сбора метрик, обработку клавиатуры и отрисовку интерфейса в одном цикле, приложение быстро станет неконтролируемым: интерфейс начнёт «тормозить» при сборе тяжелых данных, а нажатия клавиш будут пропускаться.

Правильный подход базируется на паттерне, похожем на Model-View-Update (MVU). Мы разделим приложение на три независимые части:

| Компонент | Зона ответственности | Инструмент в Rust | | :--- | :--- | :--- | | State (Состояние) | Хранит текущие данные: загрузку CPU, память, выбранную вкладку. | Структура (struct) с данными. | | Events (События) | Собирает нажатия клавиш и таймеры обновления в единый поток. | std::sync::mpsc (каналы). | | Render (Отрисовка) | Читает состояние и рисует интерфейс. Не меняет данные! | Библиотека ratatui. |

!Архитектура TUI-приложения: потоки данных и разделение ответственности

Такое разделение позволяет одному потоку (фоновому) собирать метрики системы и слушать клавиатуру, а главному потоку — только обновлять состояние и перерисовывать экран.

Настройка проекта и зависимости

Для начала создадим новый проект:

Откроем файл Cargo.toml и добавим необходимые зависимости:

> Immediate Mode Rendering — принцип отрисовки, который использует Ratatui. Вместо того чтобы хранить объекты кнопок и графиков в памяти и менять их свойства, интерфейс полностью перерисовывается с нуля каждый кадр на основе текущего состояния.

Управление терминалом: магия crossterm

Обычный терминал работает в строчном режиме: он ждёт, пока вы нажмёте Enter, прежде чем отправить введённый текст программе. Для интерактивного мониторинга нам нужен Raw Mode (сырой режим). В этом режиме каждое нажатие клавиши мгновенно передаётся программе, а специальные сочетания (например, Ctrl+C) не прерывают её работу автоматически — мы должны обработать их сами.

Также нам понадобится Alternate Screen (альтернативный экран). Это отдельный буфер терминала. Когда вы открываете vim или htop, они не затирают вашу историю команд, а открываются на «новом листе». При выходе старый текст возвращается.

Напишем модуль для инициализации и восстановления терминала:

Если программа упадёт с ошибкой (panic) до вызова restore_terminal, терминал останется в сломанном состоянии. Поэтому в реальных проектах часто используют хуки на панику для безопасного выхода.

Модель данных и сбор метрик

Создадим структуру, которая будет описывать всё, что происходит в нашем приложении. Для сбора данных используем библиотеку sysinfo.

Чтобы отобразить использование памяти в виде прогресс-бара, нам потребуется вычислить процент. Формула расчёта доли: , где — процент использования, — занятая память, — общий объём памяти. Например, если занято 4 ГБ из 16 ГБ, то .

Система событий: объединяем время и клавиатуру

Наш монитор должен обновлять экран по двум причинам:

  • Пользователь нажал кнопку (например, q для выхода).
  • Прошло определённое время (например, 1 секунда), и нужно обновить графики.
  • Для этого мы создадим Канал (Channel). Один поток будет спать и генерировать события «Тик» каждую секунду, а также слушать клавиатуру. Главный поток будет просто читать из этого канала.

    Рендеринг интерфейса с Ratatui

    Ratatui использует систему Layout (макетов), которая работает по принципу ограничений (Constraints). Мы делим экран на прямоугольные области (Chunks), указывая их размер в процентах, фиксированных символах или минимальных значениях.

    Создадим функцию отрисовки, которая принимает текущее состояние и рисует виджеты:

    Главный цикл приложения

    Теперь соберём всё воедино в файле main.rs. Главный цикл будет получать события из канала, обновлять состояние и вызывать отрисовку.

    Возможные ошибки и их решения

    При разработке TUI-приложений новички часто сталкиваются со специфическими проблемами:

  • Мерцание экрана (Flickering). Возникает, если использовать стандартный print! или очищать экран вручную перед каждым кадром. Ratatui решает эту проблему благодаря двойной буферизации: библиотека сравнивает предыдущий кадр с новым и отправляет в терминал только изменившиеся символы.
  • Терминал «сломался» после ошибки. Если программа завершилась с паникой (panic), функция restore_terminal не вызовется. Вы не увидите курсор, а вводимый текст не будет отображаться. Чтобы починить терминал, введите вслепую команду reset и нажмите Enter.
  • Высокая нагрузка на CPU самой программой. Если в главном цикле нет ожидания (например, rx.recv() заменён на неблокирующий вызов без задержки), цикл будет выполняться миллионы раз в секунду. Использование каналов с таймаутом (как в нашем setup_events) гарантирует, что программа «спит», пока не появится событие.
  • Созданный нами каркас — это прочный фундамент. Вы можете легко расширить его, добавив новые вкладки (Tabs), таблицы со списком процессов (Table) или графики истории загрузки сети (Sparkline), просто добавляя новые поля в AppState и новые виджеты в функцию draw_ui.