diff --git a/soulseek-rs-lib/src/actor/peer_actor.rs b/soulseek-rs-lib/src/actor/peer_actor.rs index 634fc51..34cecc6 100644 --- a/soulseek-rs-lib/src/actor/peer_actor.rs +++ b/soulseek-rs-lib/src/actor/peer_actor.rs @@ -8,7 +8,7 @@ use crate::message::peer::{ use crate::message::server::MessageFactory; use crate::message::{Handlers, Message, MessageReader, MessageType}; use crate::peer::Peer; -use crate::types::{Download, FileSearchResult, Transfer}; +use crate::types::{Download, SearchResult, Transfer}; use crate::{debug, error, trace, warn}; use std::io::{self, Error, Write}; @@ -20,7 +20,7 @@ use std::time::{Duration, Instant}; #[derive(Debug, Clone)] pub enum PeerMessage { SendMessage(Message), - FileSearchResult(FileSearchResult), + FileSearchResult(SearchResult), TransferRequest(Transfer), UploadFailed(String, String), TransferResponse { diff --git a/soulseek-rs-lib/src/client.rs b/soulseek-rs-lib/src/client.rs index de9227a..c1900bb 100644 --- a/soulseek-rs-lib/src/client.rs +++ b/soulseek-rs-lib/src/client.rs @@ -6,7 +6,7 @@ use crate::{ actor::{peer_registry::PeerRegistry, ActorSystem}, error::{Result, SoulseekRs}, peer::{listen::Listen, ConnectionType, DownloadPeer, NewPeer, Peer}, - types::{Download, FileSearchResult}, + types::{Download, Search, SearchResult}, utils::{md5, thread_pool::ThreadPool}, Transfer, }; @@ -70,7 +70,7 @@ impl Default for ClientSettings { pub enum ClientOperation { NewPeer(NewPeer), ConnectToPeer(Peer), - SearchResult(FileSearchResult), + SearchResult(SearchResult), PeerDisconnected(String, Option), PierceFireWall(Peer), DownloadFromPeer(u32, Peer, bool), @@ -89,7 +89,7 @@ pub struct ClientContext { pub peer_registry: Option, sender: Option>, server_sender: Option>, - search_results: Vec, + searches: HashMap, pub download_tokens: HashMap, actor_system: Arc, } @@ -113,7 +113,7 @@ impl ClientContext { peer_registry: None, sender: None, server_sender: None, - search_results: Vec::new(), + searches: HashMap::new(), download_tokens: HashMap::new(), actor_system, } @@ -234,7 +234,7 @@ impl Client { &self, query: &str, timeout: Duration, - ) -> Result> { + ) -> Result> { self.search_with_cancel(query, timeout, None) } @@ -243,15 +243,22 @@ impl Client { query: &str, timeout: Duration, cancel_flag: Option>, - ) -> Result> { + ) -> Result> { info!("Searching for {}", query); - // Clear previous search results - self.context.write().unwrap().search_results.clear(); - if let Some(handle) = &self.server_handle { let hash = md5::md5(query); let token = u32::from_str_radix(&hash[0..5], 16)?; + + // Initialize new search with query string as key + self.context.write().unwrap().searches.insert( + query.to_string(), + Search { + token, + results: Vec::new(), + }, + ); + let _ = handle.send(ServerMessage::FileSearch { token, query: query.to_string(), @@ -277,15 +284,32 @@ impl Client { break; } } - Ok(self.context.read().unwrap().search_results.clone()) + + Ok(self.get_search_results(query)) } - pub fn get_search_results_count(&self) -> usize { - self.context.read().unwrap().search_results.len() + pub fn get_search_results_count(&self, search_key: &str) -> usize { + self.context + .read() + .unwrap() + .searches + .get(search_key) + .map(|s| s.results.len()) + .unwrap_or(0) } - pub fn get_search_results(&self) -> Vec { - self.context.read().unwrap().search_results.clone() + pub fn get_search_results(&self, search_key: &str) -> Vec { + self.context + .read() + .unwrap() + .searches + .get(search_key) + .map(|s| s.results.clone()) + .unwrap_or_default() + } + + pub fn get_all_searches(&self) -> HashMap { + self.context.read().unwrap().searches.clone() } pub fn download( @@ -385,13 +409,18 @@ impl Client { ); }); } - ClientOperation::SearchResult(file_search) => { - trace!("[client] SearchResult {:?}", file_search); - client_context - .write() - .unwrap() - .search_results - .push(file_search.clone()); + ClientOperation::SearchResult(search_result) => { + trace!("[client] SearchResult {:?}", search_result); + let mut context = client_context.write().unwrap(); + let result_token = search_result.token; + + // Find the search with matching token + for search in context.searches.values_mut() { + if search.token == result_token { + search.results.push(search_result); + break; + } + } } ClientOperation::PeerDisconnected(username, error) => { let context = client_context.read().unwrap(); diff --git a/soulseek-rs-lib/src/lib.rs b/soulseek-rs-lib/src/lib.rs index 874815a..24dc83f 100644 --- a/soulseek-rs-lib/src/lib.rs +++ b/soulseek-rs-lib/src/lib.rs @@ -13,7 +13,7 @@ pub mod utils; pub mod prelude { pub use crate::actor::server_actor::PeerAddress; pub use crate::types::{ - DownloadResult, DownloadStatus, File, FileSearchResult, Transfer, + DownloadResult, DownloadStatus, File, Search, SearchResult, Transfer, }; pub use crate::{debug, error, info, trace, warn}; } @@ -23,5 +23,5 @@ pub use actor::server_actor::PeerAddress; pub use client::{Client, ClientSettings}; pub use error::{Result, SoulseekRs}; pub use types::{ - DownloadResult, DownloadStatus, File, FileSearchResult, Transfer, + DownloadResult, DownloadStatus, File, Search, SearchResult, Transfer, }; diff --git a/soulseek-rs-lib/src/message/peer/file_search_response.rs b/soulseek-rs-lib/src/message/peer/file_search_response.rs index 33f09f1..cd0ea8a 100644 --- a/soulseek-rs-lib/src/message/peer/file_search_response.rs +++ b/soulseek-rs-lib/src/message/peer/file_search_response.rs @@ -1,6 +1,6 @@ use crate::message::{Message, MessageHandler}; use crate::peer::PeerMessage; -use crate::types::FileSearchResult; +use crate::types::SearchResult; use std::sync::mpsc::Sender; pub struct FileSearchResponse; @@ -9,7 +9,7 @@ impl MessageHandler for FileSearchResponse { 9 } fn handle(&self, message: &mut Message, sender: Sender) { - let file_search = match FileSearchResult::new_from_message(message) { + let file_search = match SearchResult::new_from_message(message) { Ok(result) => result, Err(_) => return, // Skip malformed search results }; @@ -43,7 +43,7 @@ fn test_new_from_message() { let mut message = Message::new_with_data(data); message.set_pointer(8); - let file_search = FileSearchResult::new_from_message(&mut message).unwrap(); + let file_search = SearchResult::new_from_message(&mut message).unwrap(); assert_eq!(file_search.token, 882125677); assert_eq!(file_search.files.len(), 2); let file = &file_search.files[0]; diff --git a/soulseek-rs-lib/src/types.rs b/soulseek-rs-lib/src/types.rs index 3ca7b54..ad346d6 100644 --- a/soulseek-rs-lib/src/types.rs +++ b/soulseek-rs-lib/src/types.rs @@ -22,7 +22,7 @@ impl UploadFailed { } #[derive(Debug, Clone)] #[allow(dead_code)] -pub struct FileSearchResult { +pub struct SearchResult { pub token: u32, pub files: Vec, pub slots: u8, @@ -30,7 +30,13 @@ pub struct FileSearchResult { pub username: String, } -impl FileSearchResult { +#[derive(Debug, Clone)] +pub struct Search { + pub token: u32, + pub results: Vec, +} + +impl SearchResult { pub fn new_from_message(message: &mut Message) -> Result { let pointer = message.get_pointer(); let size = message.get_size(); diff --git a/soulseek-rs/src/main.rs b/soulseek-rs/src/main.rs index b056155..f529acf 100644 --- a/soulseek-rs/src/main.rs +++ b/soulseek-rs/src/main.rs @@ -125,7 +125,7 @@ fn search_and_download(config: SearchConfig) -> Result<()> { cancel_flag.store(true, std::sync::atomic::Ordering::Relaxed); // Get final results - let results = client.get_search_results(); + let results = client.get_search_results(&config.query); if selected_indices.is_empty() { ratatui::restore(); diff --git a/soulseek-rs/src/ui/download_selector.rs b/soulseek-rs/src/ui/download_selector.rs index e060254..a050f9e 100644 --- a/soulseek-rs/src/ui/download_selector.rs +++ b/soulseek-rs/src/ui/download_selector.rs @@ -91,7 +91,8 @@ impl FileSelector { // Poll for new search results if active if self.search_active { if let Some(ref client) = self.client { - let current_count = client.get_search_results_count(); + let current_count = + client.get_search_results_count(&self.soulseek_query); if current_count != self.last_result_count { self.update_results_from_client(); self.last_result_count = current_count; @@ -124,7 +125,8 @@ impl FileSelector { fn update_results_from_client(&mut self) { if let Some(ref client) = self.client { - let search_results = client.get_search_results(); + let search_results = + client.get_search_results(&self.soulseek_query); // Convert search results to FileDisplayData let mut new_items = Vec::new();