refactor: generalize LruCache to support any key type (P5.16)

Made LruCache generic over key type K, not just UserId:
- LruCache<V> → LruCache<K, V>
- Added trait bounds: K: Eq + Hash + Clone + Copy
- Updated UserCache field types:
  * user_usernames: LruCache<UserId, String>
  * user_names: LruCache<UserId, String>
  * user_statuses: LruCache<UserId, UserOnlineStatus>

Benefits:
- Reusable cache implementation for any key types
- Type-safe caching
- No additional dependencies

Progress: Priority 5: 2/3 tasks, Total: 18/20 (90%)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Mikhail Kilin
2026-02-01 02:26:03 +03:00
parent 56bddabbe1
commit 67739583f3
3 changed files with 46 additions and 19 deletions

View File

@@ -904,9 +904,9 @@ let message = MessageBuilder::new(MessageId::new(123))
- ✅ Priority 2: 5/5 (100%)
- ✅ Priority 3: 4/4 (100%) 🎉
- ✅ Priority 4: 4/4 (100%) 🎉
- ⏳ Priority 5: 1/3 (33%, P5.15 завершено)
- ⏳ Priority 5: 2/3 (67%, P5.15, P5.16 завершены)
**Общий прогресс: 17/20 задач (85%)**
**Общий прогресс: 18/20 задач (90%)**
**Последние изменения (1 февраля 2026)**:
- ✅ **P5.15 — Feature flags для зависимостей** (2026-02-01)
@@ -915,9 +915,14 @@ let message = MessageBuilder::new(MessageId::new(123))
- Условная компиляция в коде с graceful degradation
- Преимущества: уменьшение размера бинарника, модульность
- ✅ **P5.16 — LRU cache обобщение** (2026-02-01)
- Обобщена структура `LruCache<K, V>` в src/tdlib/users.rs
- Type-safe: `K: Eq + Hash + Clone + Copy`, `V: Clone`
- Обновлены типы в UserCache: `LruCache<UserId, String>`, `LruCache<UserId, UserOnlineStatus>`
- Переиспользуемая реализация без дополнительных зависимостей
**Следующие шаги**:
- P5.16: LRU cache обобщение
- P5.17: Tracing вместо println!
- P5.17: Tracing вместо println! (последняя задача Priority 5!)
## Известные проблемы

View File

@@ -728,12 +728,28 @@ open = { version = "5.0", optional = true }
---
### 16. LRU cache обобщение
### 16. LRU cache обобщение ✅ ЗАВЕРШЕНО
**Проблема**: Отдельные LRU кеши для `user_names` и `user_statuses`.
**Решение**: Создать обобщённый `LruCache<K, V>` или использовать готовый крейт `lru = "0.12"`.
**Реализовано**:
- ✅ Обобщённая структура `LruCache<K, V>` в `src/tdlib/users.rs`
- ✅ Type parameters:
- `K: Eq + Hash + Clone + Copy` — тип ключа
- `V: Clone` — тип значения
- ✅ Обновлена `UserCache`:
- `user_usernames: LruCache<UserId, String>`
- `user_names: LruCache<UserId, String>`
- `user_statuses: LruCache<UserId, UserOnlineStatus>`
-Все методы обобщены: `get()`, `peek()`, `insert()`, `contains_key()`, `len()`
**Преимущества**:
- ✅ Переиспользуемая реализация для любых типов ключей
- ✅ Type-safe кеширование
- ✅ Без дополнительных зависимостей
---
### 17. Tracing вместо println!
@@ -781,10 +797,11 @@ tracing-subscriber = "0.3"
- [x] P4.12 — Rustdoc ✅
- [x] P4.13 — Config validation ✅
- [x] P4.14 — Async/await consistency ✅
- [ ] Priority 5: 1/3 задач
- [ ] Priority 5: 2/3 задач
- [x] P5.15 — Feature flags ✅
- [x] P5.16 — LRU cache обобщение ✅
**Всего**: 17/20 задач (85%)
**Всего**: 18/20 задач (90%)
---

View File

@@ -13,27 +13,32 @@ use super::types::UserOnlineStatus;
///
/// # Type Parameters
///
/// * `K` - Тип ключа (должен реализовывать `Eq + Hash + Clone + Copy`)
/// * `V` - Тип значения (должен реализовывать `Clone`)
///
/// # Examples
///
/// ```ignore
/// let mut cache = LruCache::<String>::new(100);
/// let mut cache = LruCache::<UserId, String>::new(100);
/// cache.insert(UserId::new(1), "Alice".to_string());
/// assert_eq!(cache.get(&UserId::new(1)), Some(&"Alice".to_string()));
/// ```
pub struct LruCache<V> {
pub struct LruCache<K, V> {
/// Хранилище ключ-значение.
map: HashMap<UserId, V>,
map: HashMap<K, V>,
/// Порядок доступа: последний элемент — самый недавно использованный.
order: Vec<UserId>,
order: Vec<K>,
/// Максимальная ёмкость кэша.
capacity: usize,
}
impl<V: Clone> LruCache<V> {
impl<K, V> LruCache<K, V>
where
K: Eq + std::hash::Hash + Clone + Copy,
V: Clone,
{
/// Создает новый LRU кэш с заданной ёмкостью.
pub fn new(capacity: usize) -> Self {
Self {
@@ -44,7 +49,7 @@ impl<V: Clone> LruCache<V> {
}
/// Получает значение и обновляет порядок доступа (помечает как использованное).
pub fn get(&mut self, key: &UserId) -> Option<&V> {
pub fn get(&mut self, key: &K) -> Option<&V> {
if self.map.contains_key(key) {
// Перемещаем ключ в конец (самый недавно использованный)
self.order.retain(|k| k != key);
@@ -56,12 +61,12 @@ impl<V: Clone> LruCache<V> {
}
/// Получить значение без обновления порядка (для read-only доступа)
pub fn peek(&self, key: &UserId) -> Option<&V> {
pub fn peek(&self, key: &K) -> Option<&V> {
self.map.get(key)
}
/// Вставить значение
pub fn insert(&mut self, key: UserId, value: V) {
pub fn insert(&mut self, key: K, value: V) {
if self.map.contains_key(&key) {
// Обновляем существующее значение
self.map.insert(key, value);
@@ -81,7 +86,7 @@ impl<V: Clone> LruCache<V> {
}
/// Проверить наличие ключа
pub fn contains_key(&self, key: &UserId) -> bool {
pub fn contains_key(&self, key: &K) -> bool {
self.map.contains_key(key)
}
@@ -118,10 +123,10 @@ impl<V: Clone> LruCache<V> {
/// ```
pub struct UserCache {
/// LRU-кэш usernames: user_id → username.
pub user_usernames: LruCache<String>,
pub user_usernames: LruCache<UserId, String>,
/// LRU-кэш имён: user_id → display_name (first_name + last_name).
pub user_names: LruCache<String>,
pub user_names: LruCache<UserId, String>,
/// Связь chat_id → user_id для приватных чатов.
pub chat_user_ids: HashMap<ChatId, UserId>,
@@ -130,7 +135,7 @@ pub struct UserCache {
pub pending_user_ids: Vec<UserId>,
/// LRU-кэш онлайн-статусов: user_id → status.
pub user_statuses: LruCache<UserOnlineStatus>,
pub user_statuses: LruCache<UserId, UserOnlineStatus>,
/// ID клиента TDLib для API вызовов.
client_id: i32,