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 для выхода).Для этого мы создадим Канал (Channel). Один поток будет спать и генерировать события «Тик» каждую секунду, а также слушать клавиатуру. Главный поток будет просто читать из этого канала.
Рендеринг интерфейса с Ratatui
Ratatui использует систему Layout (макетов), которая работает по принципу ограничений (Constraints). Мы делим экран на прямоугольные области (Chunks), указывая их размер в процентах, фиксированных символах или минимальных значениях.
Создадим функцию отрисовки, которая принимает текущее состояние и рисует виджеты:
Главный цикл приложения
Теперь соберём всё воедино в файле main.rs. Главный цикл будет получать события из канала, обновлять состояние и вызывать отрисовку.
Возможные ошибки и их решения
При разработке TUI-приложений новички часто сталкиваются со специфическими проблемами:
print! или очищать экран вручную перед каждым кадром. Ratatui решает эту проблему благодаря двойной буферизации: библиотека сравнивает предыдущий кадр с новым и отправляет в терминал только изменившиеся символы.restore_terminal не вызовется. Вы не увидите курсор, а вводимый текст не будет отображаться. Чтобы починить терминал, введите вслепую команду reset и нажмите Enter.rx.recv() заменён на неблокирующий вызов без задержки), цикл будет выполняться миллионы раз в секунду. Использование каналов с таймаутом (как в нашем setup_events) гарантирует, что программа «спит», пока не появится событие.Созданный нами каркас — это прочный фундамент. Вы можете легко расширить его, добавив новые вкладки (Tabs), таблицы со списком процессов (Table) или графики истории загрузки сети (Sparkline), просто добавляя новые поля в AppState и новые виджеты в функцию draw_ui.