use { crate::{Checksum, Error}, md5::{Digest, Md5}, sha1::Sha1, sha2::Sha256, std::io::{Read, Write}, }; /// A representation of a regular file as an archive member #[derive(Debug)] pub struct File { /// The length of this file pub len: u64, /// The optional checksum of this file pub checksum: Checksum, /// The bytes making up this file pub data: Vec, } impl File { pub(crate) fn read(reader: &mut T) -> Result { let mut len = [0; 8]; reader.read_exact(&mut len)?; let len = u64::from_le_bytes(len); let checksum = Checksum::read(reader)?; let mut data = Vec::with_capacity(len.try_into()?); let mut handle = reader.take(len); handle.read_to_end(&mut data)?; match checksum { Checksum::Md5(sum) => { let mut hasher = Md5::new(); hasher.update(&data); let res = hasher.finalize(); if res == sum.into() { Ok(Self { len, checksum, data, }) } else { Err(Error::InvalidChecksum) } } Checksum::Sha1(sum) => { let mut hasher = Sha1::new(); hasher.update(&data); let res = hasher.finalize(); if res == sum.into() { Ok(Self { len, checksum, data, }) } else { Err(Error::InvalidChecksum) } } Checksum::Sha256(sum) => { let mut hasher = Sha256::new(); hasher.update(&data); let res = hasher.finalize(); if res == sum.into() { Ok(Self { len, checksum, data, }) } else { Err(Error::InvalidChecksum) } } Checksum::Skip => Ok(Self { len, checksum, data, }), } } pub(crate) fn write(&self, writer: &mut T) -> Result<(), Error> { writer.write_all(&self.len.to_le_bytes())?; Checksum::write(&self.checksum, writer)?; writer.write_all(&self.data)?; Ok(()) } } #[cfg(test)] mod tests { use super::*; use std::fs::File as Fd; const SHA1BUF: [u8; 20] = [ 155, 243, 229, 181, 239, 210, 47, 147, 46, 16, 11, 134, 200, 52, 130, 120, 126, 130, 166, 130, ]; #[test] fn load_store() { let file = File { len: 1005, checksum: Checksum::Sha1(SHA1BUF), data: include_bytes!("../test/li.txt").to_vec(), }; { let mut fd = Fd::create("test/file").unwrap(); file.write(&mut fd).unwrap(); } let mut fd = Fd::open("test/file").unwrap(); let res = File::read(&mut fd).unwrap(); assert_eq!(file.len, res.len); assert_eq!(&file.data, &res.data); match res.checksum { Checksum::Sha1(sum) => assert_eq!(sum, SHA1BUF), _ => unreachable!(), } } }