//! Trait definition for TdClient to enable dependency injection //! //! This trait allows tests to use FakeTdClient instead of real TDLib client. #![allow(dead_code)] use crate::tdlib::{AuthState, FolderInfo, MessageInfo, ProfileInfo, UserCache, UserOnlineStatus}; use crate::types::{ChatId, MessageId, UserId}; use async_trait::async_trait; use std::borrow::Cow; use std::path::PathBuf; use tdlib_rs::enums::{ChatAction, Update}; use super::ChatInfo; /// Auth operations. #[async_trait] pub trait AuthClient: Send { async fn send_phone_number(&self, phone: String) -> Result<(), String>; async fn send_code(&self, code: String) -> Result<(), String>; async fn send_password(&self, password: String) -> Result<(), String>; } /// Chat list and profile operations. #[async_trait] pub trait ChatClient: Send { async fn load_chats(&mut self, limit: i32) -> Result<(), String>; async fn load_folder_chats(&mut self, folder_id: i32, limit: i32) -> Result<(), String>; async fn leave_chat(&self, chat_id: ChatId) -> Result<(), String>; async fn get_profile_info(&self, chat_id: ChatId) -> Result; fn chats(&self) -> &[ChatInfo]; fn folders(&self) -> &[FolderInfo]; fn main_chat_list_position(&self) -> i32; fn set_main_chat_list_position(&mut self, position: i32); fn update_chats(&mut self, updater: F) where F: FnOnce(&mut Vec); fn update_folders(&mut self, updater: F) where F: FnOnce(&mut Vec); } /// Ephemeral chat actions such as typing status. #[async_trait] pub trait ChatActionClient: Send { async fn send_chat_action(&self, chat_id: ChatId, action: ChatAction); fn clear_stale_typing_status(&mut self) -> bool; fn typing_status(&self) -> Option<&(UserId, String, std::time::Instant)>; fn set_typing_status(&mut self, status: Option<(UserId, String, std::time::Instant)>); } /// Message history, search, and mutation operations. #[async_trait] pub trait MessageClient: Send { async fn get_chat_history( &mut self, chat_id: ChatId, limit: i32, ) -> Result, String>; async fn load_older_messages( &mut self, chat_id: ChatId, from_message_id: MessageId, ) -> Result, String>; async fn get_pinned_messages(&mut self, chat_id: ChatId) -> Result, String>; async fn load_current_pinned_message(&mut self, chat_id: ChatId); async fn search_messages( &self, chat_id: ChatId, query: &str, ) -> Result, String>; async fn send_message( &mut self, chat_id: ChatId, text: String, reply_to_message_id: Option, reply_info: Option, ) -> Result; async fn edit_message( &mut self, chat_id: ChatId, message_id: MessageId, new_text: String, ) -> Result; async fn delete_messages( &mut self, chat_id: ChatId, message_ids: Vec, revoke: bool, ) -> Result<(), String>; async fn forward_messages( &mut self, to_chat_id: ChatId, from_chat_id: ChatId, message_ids: Vec, ) -> Result<(), String>; async fn set_draft_message(&self, chat_id: ChatId, text: String) -> Result<(), String>; fn current_chat_messages(&self) -> Cow<'_, [MessageInfo]>; fn current_chat_id(&self) -> Option; fn current_pinned_message(&self) -> Option; fn push_message(&mut self, msg: MessageInfo); fn clear_current_chat_messages(&mut self); fn set_current_chat_messages(&mut self, messages: Vec); fn update_current_chat_messages(&mut self, updater: F) where F: FnOnce(&mut Vec); fn set_current_chat_id(&mut self, chat_id: Option); fn set_current_pinned_message(&mut self, msg: Option); fn pending_view_messages(&self) -> &[(ChatId, Vec)]; fn enqueue_pending_view_messages(&mut self, chat_id: ChatId, message_ids: Vec); async fn fetch_missing_reply_info(&mut self); async fn process_pending_view_messages(&mut self); } /// User cache and user-status operations. #[async_trait] pub trait UserClient: Send { fn get_user_status_by_chat_id(&self, chat_id: ChatId) -> Option<&UserOnlineStatus>; fn pending_user_ids(&self) -> &[UserId]; fn user_cache(&self) -> &UserCache; fn update_user_cache(&mut self, updater: F) where F: FnOnce(&mut UserCache); async fn process_pending_user_ids(&mut self); } /// Message reaction operations. #[async_trait] pub trait ReactionClient: Send { async fn get_message_available_reactions( &self, chat_id: ChatId, message_id: MessageId, ) -> Result, String>; async fn toggle_reaction( &self, chat_id: ChatId, message_id: MessageId, reaction: String, ) -> Result<(), String>; } /// File download operations. #[async_trait] pub trait FileClient: Send { async fn download_file(&self, file_id: i32) -> Result; async fn download_voice_note(&self, file_id: i32) -> Result; } /// Shared client state that does not belong to one feature area. #[async_trait] pub trait ClientState: Send { fn client_id(&self) -> i32; async fn get_me(&self) -> Result; fn auth_state(&self) -> &AuthState; fn network_state(&self) -> super::types::NetworkState; } /// Notification configuration operations. pub trait NotificationClient: Send { fn configure_notifications(&mut self, config: &crate::config::NotificationsConfig); fn sync_notification_muted_chats(&mut self); } /// Account switching operations. #[async_trait] pub trait AccountClient: Send { /// Recreates the client with a new database path (for account switching). /// /// For real TdClient: closes old client, creates new one, inits TDLib parameters. /// For FakeTdClient: no-op. async fn recreate_client(&mut self, db_path: PathBuf) -> Result<(), String>; } /// TDLib update routing. pub trait UpdateClient: Send { fn handle_update(&mut self, update: Update); } /// Facade trait for TDLib client operations /// /// This trait defines the interface for both real and fake TDLib clients, /// enabling dependency injection and easier testing. #[allow(dead_code)] pub trait TdClientTrait: AuthClient + ChatClient + ChatActionClient + MessageClient + UserClient + ReactionClient + FileClient + ClientState + NotificationClient + AccountClient + UpdateClient + Send { } impl TdClientTrait for T where T: AuthClient + ChatClient + ChatActionClient + MessageClient + UserClient + ReactionClient + FileClient + ClientState + NotificationClient + AccountClient + UpdateClient + Send { }