From 28a97f675ec9c2358659c2830bd60613fcdf3d41 Mon Sep 17 00:00:00 2001 From: Donald Knuth Date: Sat, 3 Sep 2022 14:58:48 -0400 Subject: [PATCH] Resume does not work --- src/client.rs | 31 +++++++++++++++++++++---------- src/connection.rs | 21 ++++++++++++++++++--- src/file.rs | 43 ++++++++++++++++++++++++++++++------------- src/message.rs | 1 + src/ui.rs | 2 +- 5 files changed, 71 insertions(+), 27 deletions(-) diff --git a/src/client.rs b/src/client.rs index 61e6fc2..052d405 100644 --- a/src/client.rs +++ b/src/client.rs @@ -6,7 +6,6 @@ use crate::ui::prompt_user_for_file_confirmation; use anyhow::{anyhow, Result}; -use std::ffi::OsStr; use std::path::PathBuf; use tokio::fs::File; @@ -46,7 +45,7 @@ pub async fn receive(password: &String) -> Result<()> { // Wait for offered files, respond with desired files let desired_files = request_specific_files(&mut connection).await?; // Create files - let std_file_handles = create_files(desired_files).await?; + let std_file_handles = create_or_find_files(desired_files).await?; // Download them connection.download_files(std_file_handles).await?; return Ok(()); @@ -57,7 +56,10 @@ pub async fn offer_files( file_handles: &Vec, ) -> Result> { // Collect file offer - let files = file_handles.iter().map(|fh| fh.to_file_offer()).collect(); + let mut files = vec![]; + for handle in file_handles { + files.push(handle.to_file_offer()?); + } let msg = Message::FileOffer(FileOfferPayload { files }); // Send file offer conn.send_msg(msg).await?; @@ -92,15 +94,24 @@ pub async fn request_specific_files(conn: &mut Connection) -> Result) -> Result> { +pub async fn create_or_find_files(desired_files: Vec) -> Result> { let mut v = Vec::new(); for desired_file in desired_files { - let filename = desired_file - .path - .file_name() - .unwrap_or(OsStr::new("random.txt")); - let file = File::create(filename).await?; - let std_file_handle = StdFileHandle::new(desired_file.id, file, 0).await?; + let mut filename = desired_file.path; + filename.push_str(".part"); + let file = match File::open(filename.clone()).await { + Ok(file) => { + println!( + "File {:?} already exists. Attempting to resume download.", + filename + ); + file + } + Err(_) => File::create(filename).await?, + }; + let metadata = file.metadata().await?; + let std_file_handle = + StdFileHandle::new(desired_file.id, file, metadata.len(), desired_file.size).await?; v.push(std_file_handle) } return Ok(v); diff --git a/src/connection.rs b/src/connection.rs index 80f5df4..6b91c36 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -77,6 +77,7 @@ impl Connection { Err(e) => return Err(anyhow!(e.to_string())), } } + self.send_msg(Message::FileTransferComplete).await?; let elapsed = before.elapsed(); let mb_sent = bytes_sent / 1_048_576; println!( @@ -105,24 +106,38 @@ impl Connection { } pub async fn download_file(&mut self, handle: StdFileHandle) -> Result<()> { + let clone = handle.file.try_clone()?; let mut decoder = GzDecoder::new(handle.file); loop { let msg = self.await_msg().await?; match msg { Message::FileTransfer(payload) => { + println!("In download"); if payload.chunk_header.id != handle.id { return Err(anyhow!("Wrong file")); } - if payload.chunk.len() == 0 { - break; - } decoder.write_all(&payload.chunk[..])? } + Message::FileTransferComplete => { + break; + } _ => return Err(anyhow!("Expecting file transfer message")), } } decoder.finish()?; println!("Done downloading file."); + Connection::check_and_finish_download(clone, handle.size).await?; Ok(()) } + + pub async fn check_and_finish_download(file: std::fs::File, size: u64) -> Result<()> { + let metadata = file.metadata()?; + if metadata.len() == size { + println!("File looks good."); + return Ok(()); + } + return Err(anyhow!( + "Downloaded file does not match expected size. Try again" + )); + } } diff --git a/src/file.rs b/src/file.rs index c63e094..dec835e 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; use futures::future::try_join_all; use serde::{Deserialize, Serialize}; @@ -18,7 +18,7 @@ pub struct ChunkHeader { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct FileOffer { pub id: u8, - pub path: PathBuf, + pub path: String, pub size: u64, } @@ -26,16 +26,21 @@ pub struct StdFileHandle { pub id: u8, pub file: std::fs::File, pub start: u64, + pub size: u64, } impl StdFileHandle { - pub async fn new(id: u8, file: File, start: u64) -> Result { - let mut std_file = file.into_std().await; - std_file.seek(SeekFrom::Start(start))?; + pub async fn new(id: u8, file: File, start: u64, size: u64) -> Result { + let mut file = file.into_std().await; + file.seek(SeekFrom::Start(start))?; + if start != 0 { + println!("Seeking to {:?}", start); + } Ok(StdFileHandle { - id: id, - file: std_file, - start: start, + id, + file, + start, + size, }) } } @@ -80,15 +85,15 @@ impl FileHandle { } async fn to_std(self, chunk_header: &ChunkHeader) -> Result { - StdFileHandle::new(self.id, self.file, chunk_header.start).await + StdFileHandle::new(self.id, self.file, chunk_header.start, self.md.len()).await } - pub fn to_file_offer(&self) -> FileOffer { - FileOffer { + pub fn to_file_offer(&self) -> Result { + Ok(FileOffer { id: self.id, - path: self.path.clone(), + path: pathbuf_to_string(&self.path)?, size: self.md.len(), - } + }) } pub async fn get_file_handles(file_paths: &Vec) -> Result> { @@ -115,3 +120,15 @@ pub fn to_size_string(size: u64) -> String { result } + +pub fn pathbuf_to_string(path: &PathBuf) -> Result { + let filename = match path.file_name() { + Some(s) => s, + None => return Err(anyhow!("Could not get filename from file offer.")), + }; + let filename = filename.to_os_string(); + match filename.into_string() { + Ok(s) => Ok(s), + Err(e) => Err(anyhow!("Error converting {:?} to String", path)), + } +} diff --git a/src/message.rs b/src/message.rs index 0e27365..2bc6f75 100644 --- a/src/message.rs +++ b/src/message.rs @@ -11,6 +11,7 @@ pub enum Message { FileOffer(FileOfferPayload), FileRequest(FileRequestPayload), FileTransfer(FileTransferPayload), + FileTransferComplete, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] diff --git a/src/ui.rs b/src/ui.rs index 9b94a41..eadc95e 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -26,7 +26,7 @@ pub async fn prompt_user_input( stdin: &mut FramedRead, file_offer: &FileOffer, ) -> Option { - let prompt_name = file_offer.path.file_name().unwrap(); + let prompt_name = &file_offer.path; println!( "Accept {:?}? ({:?}). (Y/n)", prompt_name,