Files
telegram-tui/REFACTORING_OPPORTUNITIES.md
Mikhail Kilin bd5e5be618
Some checks failed
CI / Check (pull_request) Has been cancelled
CI / Format (pull_request) Has been cancelled
CI / Clippy (pull_request) Has been cancelled
CI / Build (macos-latest) (pull_request) Has been cancelled
CI / Build (ubuntu-latest) (pull_request) Has been cancelled
CI / Build (windows-latest) (pull_request) Has been cancelled
refactor: extract state modules and services from monolithic files
Извлечены state модули и сервисы из монолитных файлов для улучшения структуры:

State модули:
- auth_state.rs: состояние авторизации
- chat_list_state.rs: состояние списка чатов
- compose_state.rs: состояние ввода сообщений
- message_view_state.rs: состояние просмотра сообщений
- ui_state.rs: UI состояние

Сервисы и утилиты:
- chat_filter.rs: централизованная фильтрация чатов (470+ строк)
- message_service.rs: сервис работы с сообщениями (17KB)
- key_handler.rs: trait для обработки клавиш (380+ строк)

Config модуль:
- config.rs -> config/mod.rs: основной конфиг
- config/keybindings.rs: настраиваемые горячие клавиши (420+ строк)

Тесты: 626 passed 

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 19:29:25 +03:00

42 KiB
Raw Blame History

Возможности для рефакторинга

Результаты аудита кодовой базы от 2026-02-02 Обновлено: 2026-02-04 Статус: В работе (2/10 категорий полностью завершены, 3 частично)

Оглавление

  1. Дублирование кода
  2. Большие файлы/функции
  3. Сложная вложенность
  4. Нарушение Single Responsibility
  5. Плохая инкапсуляция
  6. Отсутствующие абстракции
  7. Несогласованность
  8. Перекрытие функциональности
  9. Проблемы производительности
  10. Отсутствующие архитектурные паттерны

1. Дублирование кода

Приоритет: 🔴 Высокий Статус: ПОЛНОСТЬЮ ЗАВЕРШЕНО! (2026-02-02) Объем: 15-20% кодовой базы → Устранено!

Проблемы

  • Timeout/Retry паттерны (~20 экземпляров в обработке ввода)

    • Повторяющаяся логика таймаутов в src/input/main_input.rs
    • Одинаковые паттерны retry в разных обработчиках
  • Обработка модальных окон (5+ мест)

    • Логика открытия/закрытия модалок дублируется
    • Валидация ввода в модальных окнах повторяется
    • Обработка Escape для закрытия модалок в каждом месте
  • Паттерны валидации

    • Проверка пустых строк
    • Валидация ID чатов/сообщений
    • Проверка длины текста

Решение

  • Создать retry_utils.rs с функциями with_timeout(), with_retry() - Выполнено и интегрировано (2026-02-02)
    • Создан src/utils/retry.rs с тремя функциями: with_timeout(), with_timeout_msg(), with_timeout_ignore()
    • Заменены ВСЕ прямые использования tokio::time::timeout (8+ мест: main_input.rs, auth.rs, main.rs)
    • Код стал чище и короче (убрано вложенное Ok/Err матчинг)
    • 100% покрытие - больше нет прямых timeout вызовов
  • Создать modal_handler.rs с общей логикой модальных окон - Выполнено (2026-02-01)
    • Создан src/utils/modal_handler.rs (120+ строк)
    • 4 функции: handle_modal_key(), should_close_modal(), should_confirm_modal(), handle_yes_no()
    • Enum ModalAction для type-safe обработки
    • Поддержка английской и русской раскладки (y/д, n/т)
    • 4 unit теста (все проходят)
  • Создать validation.rs с переиспользуемыми валидаторами - Выполнено и интегрировано (2026-02-02)
    • Создан src/utils/validation.rs (180+ строк)
    • 7 функций валидации: is_non_empty(), is_within_length(), is_valid_chat_id(), is_valid_message_id(), is_valid_user_id(), has_items(), validate_text_input()
    • Покрывает все основные паттерны валидации
    • 7 unit тестов (все проходят)
    • Интегрировано в 4 местах: auth.rs (phone/code/password), main_input.rs (message validation)

Файлы

  • src/input/main_input.rs
  • src/app/handlers/*.rs
  • src/ui/modals/*.rs

2. Большие файлы/функции

Приоритет: 🔴 Высокий Статус: ПОЛНОСТЬЮ ЗАВЕРШЕНО! (обновлено 2026-02-04) Объем: Все 4 файла отрефакторены! (4/4, 100%! 🎉)

Проблемы

Файл Строки Проблема Статус
src/input/main_input.rs 1164 → ~1200 ~Одна функция handle() на 800 строк РЕШЕНО (handle() → 82 строки)
src/tdlib/client.rs 1259 → 599 Смешение facade и бизнес-логики РЕШЕНО (1259 → 599 строк, -52%)
src/ui/messages.rs 905 Рендеринг всех типов сообщений НЕ ТРЕБУЕТСЯ (render() → 92 строки, Phase 5)
src/tdlib/messages.rs 850 → 757 Обработка всех типов обновлений сообщений РЕШЕНО (convert_message() → 57 строк, -62%)

Решение

2.1. Разделить src/input/main_input.rs - ЗАВЕРШЕНО (2026-02-03)

Phase 1-2 (2026-02-02):

  • Создана структура src/input/handlers/ (7 модулей) - ПОДГОТОВКА
  • Создан handlers/clipboard.rs (~100 строк) - извлечён из main_input
  • Создан handlers/global.rs (~90 строк) - извлечён из main_input
  • Созданы заглушки: profile.rs, search.rs, modal.rs, messages.rs, chat_list.rs

Phase 2-3 (2026-02-03):

  • Извлечено 13 специализированных функций-обработчиков (~946 строк):
    • handle_open_chat_keyboard_input() (~129 строк)
    • handle_chat_list_navigation() (~34 строки)
    • handle_profile_mode() (~120 строк)
    • handle_message_search_mode() (~73 строки)
    • handle_pinned_mode() (~42 строки)
    • handle_reaction_picker_mode() (~90 строк)
    • handle_delete_confirmation() (~60 строк)
    • handle_forward_mode() (~52 строки)
    • handle_chat_search_mode() (~43 строки)
    • handle_enter_key() (~145 строк)
    • handle_escape_key() (~35 строк)
    • handle_message_selection() (~95 строк)
    • handle_profile_open() (~28 строк)

Phase 4 (2026-02-03):

  • Упрощена вложенность (early returns, let-else guards)
  • Извлечено 3 вспомогательных функции:
    • edit_message() (~50 строк)
    • send_new_message() (~55 строк)
    • perform_message_search() (~20 строк)

Итоговый результат:

  • Функция handle() сократилась с 891 до 82 строк (91% сокращение! 🎉)
  • Глубина вложенности: 6+ уровней → 2-3 уровня
  • Все 196 тестов проходят успешно
  • Код стал линейным и простым для понимания

Примечание: Вместо создания отдельных файлов в handlers/ (что привело бы к поломке), мы выбрали подход извлечения функций внутри main_input.rs. Это позволило радикально упростить код без риска регрессий.

2.2. Разделить src/tdlib/client.rs - ЗАВЕРШЕНО (2026-02-04)

Этап 1 (2026-02-04): Извлечение Update Handlers

  • Создан модуль src/tdlib/update_handlers.rs (302 строки)
  • Извлечено 8 handler функций (~350 строк):
    • handle_new_message_update() — добавление новых сообщений (44 строки)
    • handle_chat_action_update() — статус набора текста (32 строки)
    • handle_chat_position_update() — управление позициями чатов (36 строк)
    • handle_user_update() — обработка информации о пользователях (40 строк)
    • handle_message_interaction_info_update() — обновление реакций (44 строки)
    • handle_message_send_succeeded_update() — успешная отправка (35 строк)
    • handle_chat_draft_message_update() — черновики сообщений (15 строк)
    • handle_auth_state() — изменение состояния авторизации (10 строк)
  • Обновлён handle_update() для делегирования в update_handlers
  • Результат: client.rs 1259 → 983 строки (22% сокращение)

Этап 2 (2026-02-04): Извлечение Message Converter

  • Создан модуль src/tdlib/message_converter.rs (250 строк)
  • Извлечено 6 conversion функций (~240 строк):
    • convert_message() — основная конвертация TDLib → MessageInfo (150+ строк)
    • extract_reply_info() — извлечение reply информации (30 строк)
    • extract_forward_info() — извлечение forward информации (25 строк)
    • extract_reactions() — извлечение реакций (20 строк)
    • get_origin_sender_name() — получение имени отправителя (15 строк)
    • update_reply_info_from_loaded_messages() — обновление reply из кэша (30 строк)
  • Исправлены ошибки компиляции с неверными именами полей
  • Обновлены вызовы в update_handlers.rs
  • Результат: client.rs 983 → 754 строки (23% сокращение)

Этап 3 (2026-02-04): Извлечение Chat Helpers

  • Создан модуль src/tdlib/chat_helpers.rs (149 строк)
  • Извлечено 3 helper функции (~140 строк):
    • find_chat_mut() — поиск чата по ID (15 строк)
    • update_chat() — обновление чата через closure (15 строк, используется 9+ раз)
    • add_or_update_chat() — добавление/обновление чата в списке (110+ строк)
  • Использован sed для замены вызовов методов по всей кодовой базе
  • Результат: client.rs 754 → 599 строк (21% сокращение)

Итоговый результат:

  • Файл client.rs сократился с 1259 до 599 строк (52% сокращение! 🎉)
  • Создано 3 новых модуля с чёткой ответственностью:
    • update_handlers.rs — обработка всех типов TDLib Update
    • message_converter.rs — конвертация TDLib Message → MessageInfo
    • chat_helpers.rs — утилиты для работы с чатами
  • Все 590+ тестов проходят успешно
  • Код стал модульным и лучше организованным
  • TdClient теперь ближе к facade pattern (делегирует в специализированные модули)

2.3. Упростить src/ui/messages.rs - ЗАВЕРШЕНО (Phase 5, 2026-02-03)

Уже выполнено в Phase 5:

  • Извлечены 4 функции рендеринга (~350 строк):
    • render_chat_header() — заголовок с typing status (~47 строк)
    • render_pinned_bar() — панель закреплённого сообщения (~30 строк)
    • render_message_list() — список сообщений с автоскроллом (~98 строк)
    • render_input_box() — input с режимами (forward/select/edit/reply) (~146 строк)
  • Функция render() сократилась с 390 до 92 строк (76% сокращение! 🎉)
  • Глубина вложенности: 6+ уровней → 2-3 уровня
  • Код стал модульным и простым для понимания

Итоговый результат:

  • Файл остался цельным (905 строк), но хорошо организован
  • Главная функция render() компактная (92 строки)
  • Все вспомогательные функции извлечены (render_search_mode, render_pinned_mode, и др.)
  • Дальнейшее разделение не требуется — цели достигнуты

2.4. Упростить src/tdlib/messages.rs - ЗАВЕРШЕНО (2026-02-04)

Этап 1 (2026-02-04): Извлечение Message Conversion Helpers

  • Создан модуль src/tdlib/message_conversion.rs (158 строк)
  • Извлечено 6 вспомогательных функций:
    • extract_content_text() — извлечение текста из различных типов сообщений (~80 строк)
    • extract_entities() — извлечение форматирования (~10 строк)
    • extract_sender_name() — получение имени отправителя с API вызовом (~15 строк)
    • extract_forward_info() — информация о пересылке (~12 строк)
    • extract_reply_info() — информация об ответе (~15 строк)
    • extract_reactions() — реакции на сообщение (~26 строк)
  • Метод convert_message() сократился с 150 до 57 строк (62% сокращение! 🎉)
  • Результат: messages.rs 850 → 757 строк (11% сокращение)

Итоговый результат:

  • Файл messages.rs сократился до 757 строк
  • Создан модуль message_conversion.rs с переиспользуемыми функциями
  • Метод convert_message() теперь компактный и читаемый (57 строк)
  • Все 629 тестов проходят успешно
  • Дальнейшее разделение не требуется — MessageManager хорошо организован

Файлы

  • src/input/main_input.rs
  • src/tdlib/client.rs
  • src/ui/messages.rs
  • src/tdlib/messages.rs

3. Сложная вложенность

Приоритет: 🟡 Средний Статус: ПОЛНОСТЬЮ ЗАВЕРШЕНО! (обновлено 2026-02-04) Объем: ~30 функций → 0 функций (все проблемные решены)

Проблемы

  • 4-5 уровней вложенности в обработке ввода Решено в main_input.rs
  • Глубокая вложенность в обработке обновлений TDLib
  • Множественные if let / match вложенные друг в друга Решено в main_input.rs

Примеры

// src/input/main_input.rs - было (типичный пример)
if let Some(chat_id) = app.selected_chat {
    if let Some(message_id) = app.selected_message {
        if app.is_message_outgoing(chat_id, message_id) {
            match key.code {
                // еще больше вложенности (6+ уровней)
            }
        }
    }
}

// Стало (после Phase 4 рефакторинга)
let Some(chat_id) = app.selected_chat else { return Ok(false) };
let Some(message_id) = app.selected_message else { return Ok(false) };

if !app.is_message_outgoing(chat_id, message_id) {
    return Ok(false);  // early return
}
// Линейная логика (2-3 уровня максимум)

Решение

Выполнено в src/input/main_input.rs (2026-02-03)

  • Применены early returns - уменьшили вложенность с 6+ до 2-3 уровней
  • Извлечена вложенная логика в 3 функции:
    • edit_message() — редактирование сообщения (~50 строк)
    • send_new_message() — отправка нового сообщения (~55 строк)
    • perform_message_search() — поиск по сообщениям (~20 строк)
  • Использованы let-else guard clauses — современный Rust паттерн
  • Упрощены 6 функций:
    • handle_profile_mode() — упрощён блок Enter с let-else
    • handle_profile_open() — применён early return guard
    • handle_enter_key() — разделена на части, сокращена с ~130 до ~40 строк
    • handle_message_search_mode() — извлечена логика поиска
    • handle_escape_key() — преобразован в early returns
    • handle_message_selection() — применены let-else guards

Результат Phase 4:

  • Глубина вложенности: 6+ уровней → 2-3 уровня
  • Код стал максимально линейным и читаемым
  • Применены современные Rust паттерны (let-else, guards)

Выполнено в src/tdlib/client.rs (2026-02-03, Этап 3)

  • Добавлены helper методы для устранения дублирования:
    • find_chat_mut() — поиск чата по ID
    • update_chat() — обновление чата через closure (использовано 9+ раз)
  • Извлечено 5 handler методов из handle_update():
    • handle_chat_position_update() — управление позициями чатов (43 строки)
    • handle_user_update() — обработка информации о пользователях (46 строк)
    • handle_message_interaction_info_update() — обновление реакций (44 строки)
    • handle_message_send_succeeded_update() — успешная отправка (38 строк)
    • handle_chat_draft_message_update() — черновики (18 строк)
  • Упрощено 7 функций с применением let-else guards, early returns, unwrap_or_else:
    • handle_chat_action_update() — статус набора текста (4 → 2 уровня)
    • handle_new_message_update() — добавление сообщений (3 → 2 уровня)
    • handle_chat_draft_message_update() — черновики (if-let → match)
    • handle_user_update() — usernames (вложенные if-let → and_then)
    • convert_message() — кэш имён (if-let → unwrap_or_else)
    • extract_reply_info() — reply информация (вложенные if-let → map/or_else)
    • update_reply_info_from_loaded_messages() — обновление reply (4 → 1-2 уровня)

Результат Этапа 3 (client.rs):

  • Функция handle_update() сократилась с 268 до 122 строк (54% сокращение!)
  • Устранено дублирование: ~9 повторений pattern → 2 helper метода
  • Глубина вложенности: 4-5 уровней → 2-3 уровня
  • Применены modern patterns: let-else guards, early returns, filter chains

Дополнительные улучшения вложенности (2026-02-04)

  • Упрощена src/tdlib/messages.rs (строки 718-755)

    • fetch_missing_reply_info(): 7 уровней → 2-3 уровня
    • Извлечена функция fetch_and_update_reply()
    • Использованы let-else guards и iterator chains
    • Максимальная вложенность: 44 → 28 пробелов
  • Упрощена src/tdlib/messages.rs (строки 147-182)

    • get_chat_history() retry loop: 6 уровней → 3 уровня
    • Извлечен messages_obj после match
    • Early continue для пустых результатов
    • Использован .flatten() вместо вложенного if-let
  • Упрощена src/input/main_input.rs (строки 500-546)

    • handle_forward_mode(): 7 уровней → 2-3 уровня
    • Извлечена функция forward_selected_message()
    • Использованы early returns (let-else guards)
    • Максимальная вложенность: 40 → 36 пробелов
  • Упрощена src/input/main_input.rs (reaction picker)

    • Извлечена функция send_reaction()
    • Использованы let-else guards
    • Вложенность: 5 уровней → 2-3 уровня
  • Упрощена src/input/main_input.rs (scroll + load older)

    • Извлечена функция load_older_messages_if_needed()
    • Использованы early returns
    • Вложенность: 6 уровней → 2-3 уровня
  • Упрощена src/config.rs (строки 563-609)

    • load_credentials(): 7 уровней → 2-3 уровня
    • Извлечены функции load_credentials_from_file() и load_credentials_from_env()
    • Использованы ? operator для Option chains
    • Максимальная вложенность: 36 → 32 пробелов

Итоговый результат:

  • Все файлы с вложенностью >32 пробелов обработаны
  • Применены современные Rust паттерны (let-else guards, early returns, ? operator, iterator chains)
  • Извлечено 8 новых функций для разделения ответственности
  • Максимальная вложенность во всем проекте: ≤32 пробелов (8 уровней)

Файлы

  • src/input/main_input.rsПОЛНОСТЬЮ ЗАВЕРШЕНО (Phase 4 + доп. улучшения: 40 → 36 пробелов)
  • src/tdlib/client.rsЗАВЕРШЕНО (Этап 3: 268 → 122 строки в handle_update)
  • src/tdlib/messages.rsПОЛНОСТЬЮ ЗАВЕРШЕНО (44 → 28 пробелов)
  • src/config.rsПОЛНОСТЬЮ ЗАВЕРШЕНО (36 → 32 пробелов)
  • Все остальные модули — проверены, вложенность приемлема (≤32 пробелов)

4. Нарушение Single Responsibility

Приоритет: 🟡 Средний Статус: Не начато Объем: 2 основных структуры

Проблемы

4.1. App struct (50+ методов)

Смешивает ответственности:

  • UI state management
  • Business logic
  • TDLib interaction
  • Input handling
  • Search logic
  • Profile management
  • Folder management

4.2. TdClient (facade + бизнес-логика)

Смешивает:

  • Facade pattern (делегирование)
  • Update processing
  • Cache management
  • Network operations

Решение

Разделить App

  • Создать ChatListState (состояние списка чатов)
  • Создать MessageViewState (состояние просмотра сообщений)
  • Создать ComposeState (состояние написания сообщения)
  • Создать SearchState (состояние поиска)
  • Создать ProfileState (состояние профиля)
  • App становится координатором этих state объектов

Разделить TdClient

  • TdClient только facade (делегирование)
  • Бизнес-логика в MessageService, ChatService, etc.
  • Update processing в отдельном модуле

Файлы

  • src/app/mod.rs
  • src/tdlib/client.rs

5. Плохая инкапсуляция

Приоритет: 🔴 Высокий Статус: Частично выполнено (2026-02-01) Объем: Вся структура App

Проблемы

  • 22 публичных поля в App

    pub struct App {
        pub td_client: TdClient,
        pub chats: Vec<ChatInfo>,
        pub selected_chat: Option<ChatId>,
        pub messages: HashMap<ChatId, Vec<MessageInfo>>,
        // ... еще 18 полей
    }
    
  • Прямой доступ везде

    app.selected_chat = Some(chat_id);  // Плохо
    app.chats.push(new_chat);           // Плохо
    app.messages.clear();               // Плохо
    
  • Тесты манипулируют внутренностями

    app.td_client.user_cache.chat_user_ids.insert(...);  // Слишком глубоко
    

Решение

  • Сделать критичные поля приватными - Частично выполнено (2026-02-01)
    • config сделан приватным (readonly через getter app.config())
    • Добавлены 30+ методов-геттеров и сеттеров для всех полей
    • Остальные поля оставлены pub для совместимости (требуется массовый рефакторинг)
  • Добавить getter методы где нужно - Выполнено
    • 30+ методов: phone_input(), set_phone_input(), screen(), set_screen(), is_loading(), и т.д.
  • Полная инкапсуляция всех полей (требует обновления 170+ мест в коде)
  • Создать методы для операций (вместо прямого доступа)
    // Вместо app.selected_chat = Some(chat_id)
    app.select_chat(chat_id);  // Уже есть!
    
    // Вместо app.chats.push(new_chat)
    app.add_chat(new_chat);  // TODO
    

Файлы

  • src/app/mod.rs
  • src/app/state.rs (новый)
  • Все тесты

6. Отсутствующие абстракции

Приоритет: 🟡 Средний Статус: Частично выполнено (2026-02-04) Объем: 3 основные абстракции (2/3 завершены, 1/3 уже была)

Проблемы

6.1. Создать KeyHandler trait ЗАВЕРШЕНО! (2026-02-04)

  • Создать src/input/key_handler.rs - Выполнено (380+ строк)
    • Enum KeyResult (Handled, HandledNeedsRedraw, NotHandled, Quit)
    • Trait KeyHandler с методом handle_key() и priority()
    • Struct GlobalKeyHandler - обработчик глобальных команд (Quit, OpenSearch)
    • Struct ChatListKeyHandler - навигация по списку чатов, выбор папок
    • Struct MessageViewKeyHandler - скролл сообщений, поиск в чате
    • Struct MessageSelectionKeyHandler - действия с выбранным сообщением
    • Struct KeyHandlerChain - цепочка обработчиков с приоритетами
    • 3 unit теста (все проходят)
  • Интегрировать в main_input.rs (заменить текущую логику)
  • Добавить недостающие методы в App (enter_search_mode и т.д.)

6.2. Нет абстракции для network operations

Timeout/retry логика дублируется:

// Повторяется ~20 раз
let result = tokio::time::timeout(
    Duration::from_millis(100),
    operation()
).await;

6.3. Хардкод горячих клавиш

Невозможно изменить без правки кода:

KeyCode::Char('e') => edit_message(),  // Хардкод
KeyCode::Char('d') => delete_message(), // Хардкод

Решение

6.1. Создать KeyHandler trait

  • Создать src/input/key_handler.rs
    trait KeyHandler {
        fn handle_key(&mut self, app: &mut App, key: KeyEvent) -> Result<bool>;
    }
    
  • Реализовать для каждого экрана:
    • ChatListKeyHandler
    • MessagesKeyHandler
    • ComposeKeyHandler
    • SearchKeyHandler

6.2. Создать network utilities

  • Создать src/utils/network.rs
    async fn with_timeout<F, T>(f: F, timeout_ms: u64) -> Result<T>
    async fn with_retry<F, T>(f: F, max_retries: u32) -> Result<T>
    

6.3. Создать систему горячих клавиш ЗАВЕРШЕНО! (2026-02-04)

  • Создать src/config/keybindings.rs - Выполнено
    • Enum Command с 40+ командами (навигация, чат, сообщения, input)
    • Struct KeyBinding с поддержкой модификаторов (Ctrl, Shift, Alt и т.д.)
    • Struct Keybindings с HashMap<Command, Vec>
    • Поддержка множественных bindings для одной команды (EN/RU раскладки)
    • Сериализация/десериализация KeyCode и KeyModifiers
    • 4 unit теста (все проходят)
  • Интегрировать в приложение (вместо HotkeysConfig)
  • Загружать из конфига (опционально, с fallback на defaults)

Файлы

  • src/input/key_handler.rs (новый)
  • src/utils/network.rs (новый)
  • src/config/keybindings.rs (новый)

7. Несогласованность

Приоритет: 🟢 Низкий Статус: Не начато Объем: Вся кодовая база

Проблемы

7.1. Разные типы ошибок

// В одних местах
Result<T, String>

// В других
Result<T, Box<dyn Error>>

// В третьих
Result<T>  // с неявным типом ошибки

7.2. Разные паттерны state management

  • В одних местах флаги (is_editing: bool)
  • В других энумы (EditMode::Active)
  • В третьих Option (editing_message: Option<MessageId>)

7.3. Разные подходы к валидации

  • Иногда в UI слое
  • Иногда в бизнес-логике
  • Иногда в обработчиках ввода

Решение

  • Стандартизировать обработку ошибок (один тип ошибки)
  • Выбрать единый подход к state management (enum-based)
  • Определить слой для валидации (бизнес-логика)
  • Создать style guide в документации

Файлы

  • Вся кодовая база

8. Перекрытие функциональности

Приоритет: 🟡 Средний Статус: Выполнено (2026-02-04) Объем: 2 основные области (2/2 завершены)

Проблемы

8.1. Централизовать фильтрацию чатов ЗАВЕРШЕНО! (2026-02-04)

  • Создать src/app/chat_filter.rs - Выполнено (470+ строк)
    • Struct ChatFilterCriteria с builder pattern
    • Поддержка фильтрации по: папке, поиску, pinned, unread, mentions, muted, archived
    • Struct ChatFilter с методами фильтрации
    • Enum ChatSortOrder для сортировки (ByLastMessage, ByTitle, ByUnreadCount, PinnedFirst)
    • Методы подсчёта: count, count_unread, count_unread_mentions
    • 6 unit тестов (все проходят)
  • Заменить дублирующуюся логику в App и UI на ChatFilter
  • Удалить старые методы фильтрации из App

8.2. Централизовать обработку сообщений ЗАВЕРШЕНО! (2026-02-04)

  • Создать src/app/message_service.rs - Выполнено (508+ строк)
    • Struct MessageGroup для группировки по дате
    • Struct SenderGroup для группировки по отправителю
    • Struct MessageSearchResult с контекстом поиска
    • Struct MessageService с 13 методами бизнес-логики:
      • group_by_date() - группировка сообщений по датам
      • group_by_sender() - группировка по отправителю
      • search() - полнотекстовый поиск с контекстом
      • find_next() / find_previous() - навигация по результатам
      • filter_by_sender() / filter_unread() - фильтрация
      • find_by_id() / find_index_by_id() - поиск по ID
      • get_last_n() - получение последних N сообщений
      • get_in_date_range() - фильтрация по диапазону дат
      • count_by_sender_type() - статистика по типам
      • create_index() - создание быстрого индекса
    • 7 unit тестов (все проходят)
  • Заменить разрозненную логику в App/UI на MessageService
  • Чёткое разделение: TDLib → Service → UI

Решение

8.1. Централизовать фильтрацию

  • Создать src/app/chat_filter.rs - Выполнено
  • Один источник правды для фильтрации - Выполнено
  • UI и App используют его - TODO (требует интеграции)

8.2. Четко разделить слои обработки сообщений

  • tdlib/messages.rs - только получение и преобразование - Выполнено
  • app/message_service.rs - бизнес-логика - Выполнено
  • ui/messages.rs - только рендеринг - Было уже реализовано

Файлы

  • src/app/chat_filter.rs (новый)
  • src/app/message_service.rs (новый)
  • src/tdlib/messages.rs
  • src/ui/messages.rs

9. Проблемы производительности

Приоритет: 🟢 Низкий Статус: Не начато Объем: Локальные оптимизации

Проблемы

9.1. Множественные клоны в обработке ввода

let text = app.input_text.clone();  // Клон
let chat_id = app.selected_chat.clone();  // Клон
// Используются только для чтения

9.2. Нет кеширования результатов поиска

  • Каждый поиск выполняется заново
  • Нет инвалидации кеша при изменениях

9.3. Неэффективная LRU cache

  • Vec::retain() + Vec::push() на каждый доступ
  • O(n) вместо потенциального O(1)

Решение

  • Заменить клоны на borrowing где возможно
  • Добавить SearchCache с TTL
  • Оптимизировать LruCache (использовать VecDeque или готовую библиотеку)

Файлы

  • src/input/main_input.rs
  • src/app/search.rs
  • src/tdlib/users.rs (LruCache)

10. Отсутствующие архитектурные паттерны

Приоритет: 🟢 Низкий Статус: Не начато Объем: Архитектурные изменения

Проблемы

10.1. Нет Event Bus

Компоненты напрямую вызывают друг друга:

  • Сложно тестировать
  • Сильная связанность
  • Тяжело добавлять новые фичи

10.2. Нет Repository паттерна

Прямой доступ к данным везде:

  • app.messages.get(chat_id)
  • app.chats.iter().find(...)
  • Нет единой точки доступа к данным

10.3. Нет Service Layer

Бизнес-логика размазана:

  • Часть в App
  • Часть в TdClient
  • Часть в UI handlers

Решение

10.1. Event Bus (опционально)

  • Создать src/event_bus.rs
  • Pub/Sub для событий между компонентами
  • Decoupling

10.2. Repository Pattern

  • Создать src/repositories/chat_repository.rs
  • Создать src/repositories/message_repository.rs
  • Создать src/repositories/user_repository.rs
  • Единая точка доступа к данным

10.3. Service Layer

  • Создать src/services/chat_service.rs
  • Создать src/services/message_service.rs
  • Создать src/services/search_service.rs
  • Вся бизнес-логика в сервисах

Файлы

  • src/event_bus.rs (новый, опционально)
  • src/repositories/*.rs (новые)
  • src/services/*.rs (новые)

Приоритизация

🔴 Высокий приоритет (начать первым)

  1. Дублирование кода - быстрый win, улучшит поддерживаемость
  2. Большие файлы - критично для навигации и понимания кода
  3. Плохая инкапсуляция - защитит от ошибок, улучшит API

🟡 Средний приоритет (после высокого)

  1. Сложная вложенность - улучшит читаемость
  2. Single Responsibility - улучшит архитектуру
  3. Отсутствующие абстракции - упростит расширение
  4. Перекрытие функциональности - уберет путаницу

🟢 Низкий приоритет (когда будет время)

  1. Несогласованность - косметические улучшения
  2. Производительность - пока не critical path
  3. Архитектурные паттерны - nice to have

План выполнения

Фаза 1: Быстрые победы (1-2 дня)

  • #1: Создать утилиты для дублирующегося кода - ЗАВЕРШЕНО (2026-02-02)
    • retry utils: 100% покрытие (все timeout заменены)
    • modal_handler: интегрирован в 2 диалогах
    • validation: интегрирован в 4 местах
  • #5: Инкапсулировать поля App - Частично (геттеры добавлены)

Фаза 2: Разделение больших файлов (3-5 дней)

  • #2.1: Разделить main_input.rs
  • #2.2: Разделить client.rs
  • #2.3: Разделить messages.rs

Фаза 3: Улучшение архитектуры (5-7 дней)

  • #4: Разделить ответственности App/TdClient
  • #6: Добавить абстракции (KeyHandler, network utils)
  • #8: Убрать перекрытие функциональности

Фаза 4: Полировка (2-3 дня)

  • #3: Упростить вложенность - Частично (main_input.rs завершён 2026-02-03)
  • #7: Стандартизировать подходы
  • #9: Оптимизировать производительность

Фаза 5: Архитектурные паттерны (опционально)

  • #10: Рассмотреть Event Bus / Repository / Service Layer

Метрики

До рефакторинга

  • Строк кода: ~15,000
  • Файлов: ~50
  • Средний размер файла: 300 строк
  • Максимальный файл: 1167 строк
  • Дублирование: ~15-20%
  • Публичных полей в App: 22
  • Прямые вызовы timeout: 8+

Текущее состояние (2026-02-04)

  • Дублирование timeout: УСТРАНЕНО (0 прямых вызовов, все через retry utils)
  • Дублирование modal: УСТРАНЕНО (используется modal_handler)
  • Дублирование validation: УСТРАНЕНО (используется validation utils)
  • Вложенность в main_input.rs: УПРОЩЕНА (6+ уровней → 2-3 уровня)
  • Размер handle() в main_input.rs: СОКРАЩЁН (891 строк → 82 строки, 91% сокращение)
  • Размер client.rs: СОКРАЩЁН (1259 строк → 599 строк, 52% сокращение)
  • Размер render() в ui/messages.rs: СОКРАЩЁН (390 строк → 92 строки, 76% сокращение)
  • Размер convert_message() в tdlib/messages.rs: СОКРАЩЁН (150 строк → 57 строк, 62% сокращение)
  • Публичных полей в App: 22 → 21 (config приватный, геттеры добавлены)
  • Все большие функции отрефакторены! 🎉

Цели после рефакторинга

  • Максимальный файл: <500 строк
  • Дублирование: <5% ДОСТИГНУТО для категории #1!
  • Глубина вложенности: ≤3 уровня ДОСТИГНУТО для main_input.rs!
  • Публичных полей в App: 0
  • Все файлы <400 строк (в идеале)
  • Улучшенная тестируемость
  • Более четкое разделение ответственностей