refactor: implement newtype pattern for IDs (P2.4)
Добавлены типобезопасные обёртки ChatId, MessageId, UserId для предотвращения смешивания разных типов идентификаторов на этапе компиляции. Изменения: - Создан src/types.rs с тремя newtype структурами - Реализованы методы: new(), as_i64(), From<i64>, Display - Добавлены traits: Hash, Eq, Serialize, Deserialize - Обновлены 15+ модулей для использования новых типов: * tdlib: types.rs, chats.rs, messages.rs, users.rs, reactions.rs, client.rs * app: mod.rs, chat_state.rs * input: main_input.rs * tests: app_builder.rs, test_data.rs - Исправлены 53 ошибки компиляции связанные с type conversions Преимущества: - Компилятор предотвращает смешивание разных типов ID - Улучшенная читаемость кода (явные типы вместо i64) - Самодокументирующиеся типы Статус: Priority 2 теперь 60% (3/5 задач) - ✅ Error enum - ✅ Config validation - ✅ Newtype для ID - ⏳ MessageInfo реструктуризация - ⏳ MessageBuilder pattern Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
use crate::constants::{LAZY_LOAD_USERS_PER_TICK, MAX_CHAT_USER_IDS, MAX_USER_CACHE_SIZE};
|
||||
use crate::types::{ChatId, UserId};
|
||||
use std::collections::HashMap;
|
||||
use tdlib_rs::enums::{User, UserStatus};
|
||||
use tdlib_rs::functions;
|
||||
@@ -7,9 +8,9 @@ use super::types::UserOnlineStatus;
|
||||
|
||||
/// Простой LRU-кэш на основе HashMap + Vec для отслеживания порядка
|
||||
pub struct LruCache<V> {
|
||||
map: HashMap<i64, V>,
|
||||
map: HashMap<UserId, V>,
|
||||
/// Порядок доступа: последний элемент — самый недавно использованный
|
||||
order: Vec<i64>,
|
||||
order: Vec<UserId>,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
@@ -23,7 +24,7 @@ impl<V: Clone> LruCache<V> {
|
||||
}
|
||||
|
||||
/// Получить значение и обновить порядок доступа
|
||||
pub fn get(&mut self, key: &i64) -> Option<&V> {
|
||||
pub fn get(&mut self, key: &UserId) -> Option<&V> {
|
||||
if self.map.contains_key(key) {
|
||||
// Перемещаем ключ в конец (самый недавно использованный)
|
||||
self.order.retain(|k| k != key);
|
||||
@@ -35,12 +36,12 @@ impl<V: Clone> LruCache<V> {
|
||||
}
|
||||
|
||||
/// Получить значение без обновления порядка (для read-only доступа)
|
||||
pub fn peek(&self, key: &i64) -> Option<&V> {
|
||||
pub fn peek(&self, key: &UserId) -> Option<&V> {
|
||||
self.map.get(key)
|
||||
}
|
||||
|
||||
/// Вставить значение
|
||||
pub fn insert(&mut self, key: i64, value: V) {
|
||||
pub fn insert(&mut self, key: UserId, value: V) {
|
||||
if self.map.contains_key(&key) {
|
||||
// Обновляем существующее значение
|
||||
self.map.insert(key, value);
|
||||
@@ -78,9 +79,9 @@ pub struct UserCache {
|
||||
/// LRU-кэш имён: user_id -> display_name (first_name + last_name)
|
||||
pub user_names: LruCache<String>,
|
||||
/// Связь chat_id -> user_id для приватных чатов
|
||||
pub chat_user_ids: HashMap<i64, i64>,
|
||||
pub chat_user_ids: HashMap<ChatId, UserId>,
|
||||
/// Очередь user_id для загрузки имён
|
||||
pub pending_user_ids: Vec<i64>,
|
||||
pub pending_user_ids: Vec<UserId>,
|
||||
/// LRU-кэш онлайн-статусов пользователей: user_id -> status
|
||||
pub user_statuses: LruCache<UserOnlineStatus>,
|
||||
client_id: i32,
|
||||
@@ -99,22 +100,22 @@ impl UserCache {
|
||||
}
|
||||
|
||||
/// Получить username пользователя
|
||||
pub fn get_username(&mut self, user_id: &i64) -> Option<&String> {
|
||||
pub fn get_username(&mut self, user_id: &UserId) -> Option<&String> {
|
||||
self.user_usernames.get(user_id)
|
||||
}
|
||||
|
||||
/// Получить имя пользователя
|
||||
pub fn get_name(&mut self, user_id: &i64) -> Option<&String> {
|
||||
pub fn get_name(&mut self, user_id: &UserId) -> Option<&String> {
|
||||
self.user_names.get(user_id)
|
||||
}
|
||||
|
||||
/// Получить user_id по chat_id
|
||||
pub fn get_user_id_by_chat(&self, chat_id: i64) -> Option<i64> {
|
||||
pub fn get_user_id_by_chat(&self, chat_id: ChatId) -> Option<UserId> {
|
||||
self.chat_user_ids.get(&chat_id).copied()
|
||||
}
|
||||
|
||||
/// Получить статус пользователя по chat_id
|
||||
pub fn get_status_by_chat_id(&self, chat_id: i64) -> Option<&UserOnlineStatus> {
|
||||
pub fn get_status_by_chat_id(&self, chat_id: ChatId) -> Option<&UserOnlineStatus> {
|
||||
let user_id = self.chat_user_ids.get(&chat_id)?;
|
||||
self.user_statuses.peek(user_id)
|
||||
}
|
||||
@@ -126,20 +127,20 @@ impl UserCache {
|
||||
|
||||
// Сохраняем username
|
||||
if let Some(username) = user.usernames.as_ref().map(|u| u.editable_username.clone()) {
|
||||
self.user_usernames.insert(user_id, username);
|
||||
self.user_usernames.insert(UserId::new(user_id), username);
|
||||
}
|
||||
|
||||
// Сохраняем имя
|
||||
let display_name = format!("{} {}", user.first_name, user.last_name).trim().to_string();
|
||||
self.user_names.insert(user_id, display_name);
|
||||
self.user_names.insert(UserId::new(user_id), display_name);
|
||||
|
||||
// Обновляем статус
|
||||
self.update_status(user_id, &user.status);
|
||||
self.update_status(UserId::new(user_id), &user.status);
|
||||
}
|
||||
}
|
||||
|
||||
/// Обработать обновление статуса пользователя
|
||||
pub fn update_status(&mut self, user_id: i64, status: &UserStatus) {
|
||||
pub fn update_status(&mut self, user_id: UserId, status: &UserStatus) {
|
||||
let online_status = match status {
|
||||
UserStatus::Online(_) => UserOnlineStatus::Online,
|
||||
UserStatus::Recently(_) => UserOnlineStatus::Recently,
|
||||
@@ -152,24 +153,24 @@ impl UserCache {
|
||||
}
|
||||
|
||||
/// Сохранить связь chat_id -> user_id
|
||||
pub fn register_private_chat(&mut self, chat_id: i64, user_id: i64) {
|
||||
pub fn register_private_chat(&mut self, chat_id: ChatId, user_id: UserId) {
|
||||
self.chat_user_ids.insert(chat_id, user_id);
|
||||
}
|
||||
|
||||
/// Получить имя пользователя (асинхронно с загрузкой если нужно)
|
||||
pub async fn get_user_name(&self, user_id: i64) -> String {
|
||||
pub async fn get_user_name(&self, user_id: UserId) -> String {
|
||||
// Сначала пытаемся получить из кэша
|
||||
if let Some(name) = self.user_names.peek(&user_id) {
|
||||
return name.clone();
|
||||
}
|
||||
|
||||
// Загружаем пользователя
|
||||
match functions::get_user(user_id, self.client_id).await {
|
||||
match functions::get_user(user_id.as_i64(), self.client_id).await {
|
||||
Ok(User::User(user)) => {
|
||||
let name = format!("{} {}", user.first_name, user.last_name).trim().to_string();
|
||||
name
|
||||
}
|
||||
_ => format!("User {}", user_id),
|
||||
_ => format!("User {}", user_id.as_i64()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user