Simplified deep nesting across the codebase using modern Rust patterns:
let-else guards, early returns, iterator chains, and extracted functions.
**Files improved:**
1. src/tdlib/messages.rs (44 → 28 spaces max indent)
- fetch_missing_reply_info(): 7 → 2-3 levels
* Extracted fetch_and_update_reply()
* Used filter_map and iterator chains
- get_chat_history() retry loop: 6 → 3 levels
* Early continue for empty results
* Used .flatten() instead of nested if-let
2. src/input/main_input.rs (40 → 36 spaces max indent)
- handle_forward_mode(): 7 → 2-3 levels
* Extracted forward_selected_message()
- Reaction picker: 5 → 2-3 levels
* Extracted send_reaction()
- Scroll + load older: 6 → 2-3 levels
* Extracted load_older_messages_if_needed()
3. src/config.rs (36 → 32 spaces max indent)
- load_credentials(): 7 → 2-3 levels
* Extracted load_credentials_from_file()
* Extracted load_credentials_from_env()
* Used ? operator for Option chains
**Results:**
- Max nesting in entire project: ≤32 spaces (8 levels)
- 8 new functions extracted for better separation of concerns
- All 343 tests passing ✅
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
38 KiB
Возможности для рефакторинга
Результаты аудита кодовой базы от 2026-02-02 Обновлено: 2026-02-04 Статус: В работе (2/10 категорий полностью завершены, 3 частично)
Оглавление
- Дублирование кода
- Большие файлы/функции
- Сложная вложенность
- Нарушение Single Responsibility
- Плохая инкапсуляция
- Отсутствующие абстракции
- Несогласованность
- Перекрытие функциональности
- Проблемы производительности
- Отсутствующие архитектурные паттерны
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.rssrc/app/handlers/*.rssrc/ui/modals/*.rs
2. Большие файлы/функции
Приоритет: 🔴 Высокий Статус: ✅ ПОЛНОСТЬЮ ЗАВЕРШЕНО! (обновлено 2026-02-04) Объем: Все 4 файла отрефакторены! (4/4, 100%! 🎉)
Проблемы
| Файл | Строки | Проблема | Статус |
|---|---|---|---|
src/input/main_input.rs |
~handle() на |
✅ РЕШЕНО (handle() → 82 строки) | |
src/tdlib/client.rs |
✅ РЕШЕНО (1259 → 599 строк, -52%) | ||
src/ui/messages.rs |
905 | ✅ НЕ ТРЕБУЕТСЯ (render() → 92 строки, Phase 5) | |
src/tdlib/messages.rs |
✅ РЕШЕНО (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 Updatemessage_converter.rs— конвертация TDLib Message → MessageInfochat_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.rssrc/tdlib/client.rssrc/ui/messages.rssrc/tdlib/messages.rs
3. Сложная вложенность
Приоритет: 🟡 Средний Статус: ✅ ПОЛНОСТЬЮ ЗАВЕРШЕНО! (обновлено 2026-02-04) Объем: ~30 функций → 0 функций (все проблемные решены)
Проблемы
4-5 уровней вложенности в обработке ввода✅ Решено в main_input.rs- Глубокая вложенность в обработке обновлений TDLib
Множественные✅ Решено в main_input.rsif let/matchвложенные друг в друга
Примеры
// 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-elsehandle_profile_open()— применён early return guardhandle_enter_key()— разделена на части, сокращена с ~130 до ~40 строкhandle_message_search_mode()— извлечена логика поискаhandle_escape_key()— преобразован в early returnshandle_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()— поиск чата по IDupdate_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.rssrc/tdlib/client.rs
5. Плохая инкапсуляция
Приоритет: 🔴 Высокий
Статус: ✅ Частично выполнено (2026-02-01)
Объем: Вся структура App
Проблемы
-
22 публичных поля в
Apppub 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 через getterapp.config()) - ✅ Добавлены 30+ методов-геттеров и сеттеров для всех полей
- ⏳ Остальные поля оставлены pub для совместимости (требуется массовый рефакторинг)
- ✅
- Добавить getter методы где нужно - Выполнено
- 30+ методов:
phone_input(),set_phone_input(),screen(),set_screen(),is_loading(), и т.д.
- 30+ методов:
- Полная инкапсуляция всех полей (требует обновления 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.rssrc/app/state.rs(новый)- Все тесты
6. Отсутствующие абстракции
Приоритет: 🟡 Средний Статус: ❌ Не начато Объем: 3 основные абстракции
Проблемы
6.1. Нет KeyHandler trait
Обработка клавиш размазана по коду:
// В каждом экране повторяется
match key.code {
KeyCode::Char('q') => { ... }
KeyCode::Esc => { ... }
// ...
}
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.rstrait KeyHandler { fn handle_key(&mut self, app: &mut App, key: KeyEvent) -> Result<bool>; } - Реализовать для каждого экрана:
ChatListKeyHandlerMessagesKeyHandlerComposeKeyHandlerSearchKeyHandler
6.2. Создать network utilities
- Создать
src/utils/network.rsasync 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. Создать систему горячих клавиш
- Создать
src/config/keybindings.rs - Загружать из конфига
- Позволить переопределять
Файлы
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. Перекрытие функциональности
Приоритет: 🟡 Средний Статус: ❌ Не начато Объем: 2 основные области
Проблемы
8.1. Фильтрация чатов (3 места)
- В
App::filter_chats_by_folder() - В
App::filter_chats() - В UI слое при рендеринге
8.2. Обработка сообщений (3+ модуля)
src/tdlib/messages.rs- получение от TDLibsrc/app/mod.rs- бизнес-логикаsrc/ui/messages.rs- рендеринг- Размыто, что за что отвечает
Решение
8.1. Централизовать фильтрацию
- Создать
src/app/chat_filter.rs - Один источник правды для фильтрации
- UI и App используют его
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.rssrc/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.rssrc/app/search.rssrc/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(новые)
Приоритизация
🔴 Высокий приоритет (начать первым)
- Дублирование кода - быстрый win, улучшит поддерживаемость
- Большие файлы - критично для навигации и понимания кода
- Плохая инкапсуляция - защитит от ошибок, улучшит API
🟡 Средний приоритет (после высокого)
- Сложная вложенность - улучшит читаемость
- Single Responsibility - улучшит архитектуру
- Отсутствующие абстракции - упростит расширение
- Перекрытие функциональности - уберет путаницу
🟢 Низкий приоритет (когда будет время)
- Несогласованность - косметические улучшения
- Производительность - пока не critical path
- Архитектурные паттерны - 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 строк (в идеале)
- Улучшенная тестируемость
- Более четкое разделение ответственностей