use std::{collections::HashMap, sync::mpsc::Sender}; use crate::{error::Result, message::Message, utils::zlib::deflate}; #[derive(Debug, Clone)] #[allow(dead_code)] pub struct File { pub username: String, pub name: String, pub size: u64, pub attribs: HashMap, } pub struct UploadFailed { pub filename: String, } impl UploadFailed { pub fn new_from_message(message: &mut Message) -> Self { let filename = message.read_string(); Self { filename } } } #[derive(Debug, Clone)] #[allow(dead_code)] pub struct SearchResult { pub token: u32, pub files: Vec, pub slots: u8, pub speed: u32, pub username: String, } #[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(); let data: Vec = message.get_slice(pointer, size); let deflated = deflate(&data)?; let mut message = Message::new_with_data(deflated); let username = message.read_string(); let token = message.read_int32(); let n_files = message.read_int32(); let mut files: Vec = Vec::new(); for _ in 0..n_files { message.read_int8(); let name = message.read_string(); let size = message.read_int64(); message.read_string(); let n_attribs = message.read_int32(); let mut attribs: HashMap = HashMap::new(); for _ in 0..n_attribs { attribs.insert(message.read_int32(), message.read_int32()); } files.push(File { username: username.clone(), name, size, attribs, }); } let slots = message.read_int8(); let speed = message.read_int32(); Ok(Self { token, files, slots, speed, username, }) } } #[derive(Debug, Clone)] #[allow(dead_code)] pub struct Transfer { pub direction: u32, pub token: u32, pub filename: String, pub size: u64, } #[derive(Debug, Clone, Default)] pub struct DownloadMetadata { pub bitrate: Option, pub length_seconds: Option, pub peer_upload_speed: Option, pub peer_free_slots: Option, } #[derive(Debug, Clone)] pub struct Download { pub username: String, pub filename: String, pub token: u32, pub size: u64, pub download_directory: String, pub status: DownloadStatus, pub sender: Sender, pub queue_position: Option, pub metadata: DownloadMetadata, } impl Download { #[must_use] pub const fn is_finished(&self) -> bool { matches!( self.status, DownloadStatus::Completed | DownloadStatus::Failed(_) | DownloadStatus::TimedOut ) } #[must_use] pub const fn bytes_downloaded(&self) -> u64 { match &self.status { DownloadStatus::InProgress { bytes_downloaded, .. } | DownloadStatus::Paused { bytes_downloaded, .. } => *bytes_downloaded, DownloadStatus::Completed => self.size, _ => 0, } } #[must_use] pub const fn speed_bytes_per_sec(&self) -> f64 { match &self.status { DownloadStatus::InProgress { speed_bytes_per_sec, .. } => *speed_bytes_per_sec, _ => 0.0, } } } #[derive(Debug, Clone)] #[allow(dead_code)] pub enum DownloadStatus { Queued, InProgress { bytes_downloaded: u64, total_bytes: u64, speed_bytes_per_sec: f64, }, Paused { bytes_downloaded: u64, total_bytes: u64, }, Completed, /// Failed, optionally with a human-readable reason. Failed(Option), TimedOut, } impl Transfer { pub fn new_from_message(message: &mut Message) -> Self { let direction = message.read_int32(); let token = message.read_int32(); let filename = message.read_string(); let size = message.read_int64(); Self { direction, token, filename, size, } } } #[cfg(test)] mod tests { use super::*; // A truncated TransferRequest from an untrusted peer must parse to defaults // rather than panic (the read_* primitives are bounds-checked). #[test] fn transfer_new_from_truncated_message_does_not_panic() { let mut message = Message::new_with_data(vec![1, 0, 0]); let transfer = Transfer::new_from_message(&mut message); assert_eq!(transfer.direction, 0); assert_eq!(transfer.token, 0); assert_eq!(transfer.filename, ""); assert_eq!(transfer.size, 0); } }