chore: refactor to support multi search

This commit is contained in:
Michel de Graaf
2025-11-13 10:28:16 +01:00
parent 4a34583f9c
commit cafa40a09d
7 changed files with 70 additions and 33 deletions
+2 -2
View File
@@ -8,7 +8,7 @@ use crate::message::peer::{
use crate::message::server::MessageFactory; use crate::message::server::MessageFactory;
use crate::message::{Handlers, Message, MessageReader, MessageType}; use crate::message::{Handlers, Message, MessageReader, MessageType};
use crate::peer::Peer; use crate::peer::Peer;
use crate::types::{Download, FileSearchResult, Transfer}; use crate::types::{Download, SearchResult, Transfer};
use crate::{debug, error, trace, warn}; use crate::{debug, error, trace, warn};
use std::io::{self, Error, Write}; use std::io::{self, Error, Write};
@@ -20,7 +20,7 @@ use std::time::{Duration, Instant};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum PeerMessage { pub enum PeerMessage {
SendMessage(Message), SendMessage(Message),
FileSearchResult(FileSearchResult), FileSearchResult(SearchResult),
TransferRequest(Transfer), TransferRequest(Transfer),
UploadFailed(String, String), UploadFailed(String, String),
TransferResponse { TransferResponse {
+50 -21
View File
@@ -6,7 +6,7 @@ use crate::{
actor::{peer_registry::PeerRegistry, ActorSystem}, actor::{peer_registry::PeerRegistry, ActorSystem},
error::{Result, SoulseekRs}, error::{Result, SoulseekRs},
peer::{listen::Listen, ConnectionType, DownloadPeer, NewPeer, Peer}, peer::{listen::Listen, ConnectionType, DownloadPeer, NewPeer, Peer},
types::{Download, FileSearchResult}, types::{Download, Search, SearchResult},
utils::{md5, thread_pool::ThreadPool}, utils::{md5, thread_pool::ThreadPool},
Transfer, Transfer,
}; };
@@ -70,7 +70,7 @@ impl Default for ClientSettings {
pub enum ClientOperation { pub enum ClientOperation {
NewPeer(NewPeer), NewPeer(NewPeer),
ConnectToPeer(Peer), ConnectToPeer(Peer),
SearchResult(FileSearchResult), SearchResult(SearchResult),
PeerDisconnected(String, Option<SoulseekRs>), PeerDisconnected(String, Option<SoulseekRs>),
PierceFireWall(Peer), PierceFireWall(Peer),
DownloadFromPeer(u32, Peer, bool), DownloadFromPeer(u32, Peer, bool),
@@ -89,7 +89,7 @@ pub struct ClientContext {
pub peer_registry: Option<PeerRegistry>, pub peer_registry: Option<PeerRegistry>,
sender: Option<Sender<ClientOperation>>, sender: Option<Sender<ClientOperation>>,
server_sender: Option<Sender<ServerMessage>>, server_sender: Option<Sender<ServerMessage>>,
search_results: Vec<FileSearchResult>, searches: HashMap<String, Search>,
pub download_tokens: HashMap<u32, Download>, pub download_tokens: HashMap<u32, Download>,
actor_system: Arc<ActorSystem>, actor_system: Arc<ActorSystem>,
} }
@@ -113,7 +113,7 @@ impl ClientContext {
peer_registry: None, peer_registry: None,
sender: None, sender: None,
server_sender: None, server_sender: None,
search_results: Vec::new(), searches: HashMap::new(),
download_tokens: HashMap::new(), download_tokens: HashMap::new(),
actor_system, actor_system,
} }
@@ -234,7 +234,7 @@ impl Client {
&self, &self,
query: &str, query: &str,
timeout: Duration, timeout: Duration,
) -> Result<Vec<FileSearchResult>> { ) -> Result<Vec<SearchResult>> {
self.search_with_cancel(query, timeout, None) self.search_with_cancel(query, timeout, None)
} }
@@ -243,15 +243,22 @@ impl Client {
query: &str, query: &str,
timeout: Duration, timeout: Duration,
cancel_flag: Option<Arc<AtomicBool>>, cancel_flag: Option<Arc<AtomicBool>>,
) -> Result<Vec<FileSearchResult>> { ) -> Result<Vec<SearchResult>> {
info!("Searching for {}", query); info!("Searching for {}", query);
// Clear previous search results
self.context.write().unwrap().search_results.clear();
if let Some(handle) = &self.server_handle { if let Some(handle) = &self.server_handle {
let hash = md5::md5(query); let hash = md5::md5(query);
let token = u32::from_str_radix(&hash[0..5], 16)?; 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 { let _ = handle.send(ServerMessage::FileSearch {
token, token,
query: query.to_string(), query: query.to_string(),
@@ -277,15 +284,32 @@ impl Client {
break; break;
} }
} }
Ok(self.context.read().unwrap().search_results.clone())
Ok(self.get_search_results(query))
} }
pub fn get_search_results_count(&self) -> usize { pub fn get_search_results_count(&self, search_key: &str) -> usize {
self.context.read().unwrap().search_results.len() self.context
.read()
.unwrap()
.searches
.get(search_key)
.map(|s| s.results.len())
.unwrap_or(0)
} }
pub fn get_search_results(&self) -> Vec<FileSearchResult> { pub fn get_search_results(&self, search_key: &str) -> Vec<SearchResult> {
self.context.read().unwrap().search_results.clone() self.context
.read()
.unwrap()
.searches
.get(search_key)
.map(|s| s.results.clone())
.unwrap_or_default()
}
pub fn get_all_searches(&self) -> HashMap<String, Search> {
self.context.read().unwrap().searches.clone()
} }
pub fn download( pub fn download(
@@ -385,13 +409,18 @@ impl Client {
); );
}); });
} }
ClientOperation::SearchResult(file_search) => { ClientOperation::SearchResult(search_result) => {
trace!("[client] SearchResult {:?}", file_search); trace!("[client] SearchResult {:?}", search_result);
client_context let mut context = client_context.write().unwrap();
.write() let result_token = search_result.token;
.unwrap()
.search_results // Find the search with matching token
.push(file_search.clone()); for search in context.searches.values_mut() {
if search.token == result_token {
search.results.push(search_result);
break;
}
}
} }
ClientOperation::PeerDisconnected(username, error) => { ClientOperation::PeerDisconnected(username, error) => {
let context = client_context.read().unwrap(); let context = client_context.read().unwrap();
+2 -2
View File
@@ -13,7 +13,7 @@ pub mod utils;
pub mod prelude { pub mod prelude {
pub use crate::actor::server_actor::PeerAddress; pub use crate::actor::server_actor::PeerAddress;
pub use crate::types::{ pub use crate::types::{
DownloadResult, DownloadStatus, File, FileSearchResult, Transfer, DownloadResult, DownloadStatus, File, Search, SearchResult, Transfer,
}; };
pub use crate::{debug, error, info, trace, warn}; 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 client::{Client, ClientSettings};
pub use error::{Result, SoulseekRs}; pub use error::{Result, SoulseekRs};
pub use types::{ pub use types::{
DownloadResult, DownloadStatus, File, FileSearchResult, Transfer, DownloadResult, DownloadStatus, File, Search, SearchResult, Transfer,
}; };
@@ -1,6 +1,6 @@
use crate::message::{Message, MessageHandler}; use crate::message::{Message, MessageHandler};
use crate::peer::PeerMessage; use crate::peer::PeerMessage;
use crate::types::FileSearchResult; use crate::types::SearchResult;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
pub struct FileSearchResponse; pub struct FileSearchResponse;
@@ -9,7 +9,7 @@ impl MessageHandler<PeerMessage> for FileSearchResponse {
9 9
} }
fn handle(&self, message: &mut Message, sender: Sender<PeerMessage>) { fn handle(&self, message: &mut Message, sender: Sender<PeerMessage>) {
let file_search = match FileSearchResult::new_from_message(message) { let file_search = match SearchResult::new_from_message(message) {
Ok(result) => result, Ok(result) => result,
Err(_) => return, // Skip malformed search results Err(_) => return, // Skip malformed search results
}; };
@@ -43,7 +43,7 @@ fn test_new_from_message() {
let mut message = Message::new_with_data(data); let mut message = Message::new_with_data(data);
message.set_pointer(8); 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.token, 882125677);
assert_eq!(file_search.files.len(), 2); assert_eq!(file_search.files.len(), 2);
let file = &file_search.files[0]; let file = &file_search.files[0];
+8 -2
View File
@@ -22,7 +22,7 @@ impl UploadFailed {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[allow(dead_code)] #[allow(dead_code)]
pub struct FileSearchResult { pub struct SearchResult {
pub token: u32, pub token: u32,
pub files: Vec<File>, pub files: Vec<File>,
pub slots: u8, pub slots: u8,
@@ -30,7 +30,13 @@ pub struct FileSearchResult {
pub username: String, pub username: String,
} }
impl FileSearchResult { #[derive(Debug, Clone)]
pub struct Search {
pub token: u32,
pub results: Vec<SearchResult>,
}
impl SearchResult {
pub fn new_from_message(message: &mut Message) -> Result<Self> { pub fn new_from_message(message: &mut Message) -> Result<Self> {
let pointer = message.get_pointer(); let pointer = message.get_pointer();
let size = message.get_size(); let size = message.get_size();
+1 -1
View File
@@ -125,7 +125,7 @@ fn search_and_download(config: SearchConfig) -> Result<()> {
cancel_flag.store(true, std::sync::atomic::Ordering::Relaxed); cancel_flag.store(true, std::sync::atomic::Ordering::Relaxed);
// Get final results // Get final results
let results = client.get_search_results(); let results = client.get_search_results(&config.query);
if selected_indices.is_empty() { if selected_indices.is_empty() {
ratatui::restore(); ratatui::restore();
+4 -2
View File
@@ -91,7 +91,8 @@ impl FileSelector {
// Poll for new search results if active // Poll for new search results if active
if self.search_active { if self.search_active {
if let Some(ref client) = self.client { 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 { if current_count != self.last_result_count {
self.update_results_from_client(); self.update_results_from_client();
self.last_result_count = current_count; self.last_result_count = current_count;
@@ -124,7 +125,8 @@ impl FileSelector {
fn update_results_from_client(&mut self) { fn update_results_from_client(&mut self) {
if let Some(ref client) = self.client { 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 // Convert search results to FileDisplayData
let mut new_items = Vec::new(); let mut new_items = Vec::new();