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

@@ -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,