use std::{ fs, io::{self, Read, Seek, SeekFrom}, path::PathBuf, }; use log::warn; use serde::{Deserialize, Serialize}; use crate::sharry; use super::{Checked, Chunk, FileTrait}; #[derive(Serialize, Deserialize, Debug)] pub struct Uploading { /// canonical path to a regular file path: PathBuf, /// size of that file size: u64, /// hash of that file hash: Option, file_id: sharry::FileID, #[serde(skip)] last_offset: Option, offset: u64, } impl Uploading { pub(super) fn new( path: PathBuf, size: u64, hash: Option, file_id: sharry::FileID, ) -> Self { Self { path, size, hash, file_id, last_offset: None, offset: 0, } } pub fn get_offset(&self) -> u64 { self.offset } pub fn rewind(mut self) -> Option { if let Some(last_offset) = self.last_offset { self.last_offset = None; self.offset = last_offset; Some(self) } else { warn!("attempted to rewind twice"); None } } pub fn read<'t>(&mut self, buf: &'t mut [u8]) -> io::Result> { let mut f = fs::File::open(&self.path)?; f.seek(SeekFrom::Start(self.offset))?; let read_len = f.read(buf)?; if read_len == 0 { return Err(io::Error::new( io::ErrorKind::UnexpectedEof, format!("could not read from file {:?}", self.path.display()), )); } let chunk = Chunk::new(self.file_id.clone(), self.offset, &buf[..read_len]); self.last_offset = Some(self.offset); self.offset += chunk.get_length(); Ok(chunk) } pub fn check_eof(self) -> Result { if self.offset < self.size { Ok(self) } else { Err(self.path) } } pub fn abort(self) -> Checked { Checked { path: self.path, size: self.size, hash: self.hash, } } } impl<'t> FileTrait<'t> for Uploading { fn get_name(&'t self) -> &'t str { ::extract_file_name(&self.path) } fn get_size(&self) -> u64 { self.size } fn check_hash(&self, on_progress: impl FnMut(u64)) -> crate::Result<()> { super::check_hash( &self.path, self.size, self.hash.as_ref().map(String::as_str), on_progress, ) } }