haggis-rs/src/file.rs

126 lines
3.6 KiB
Rust

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<u8>,
}
impl File {
/// Read a `File` structure from a stream of bytes
/// # Errors
/// Can return `Error` if io fails or the byte stream is not a valid
/// **Haggis** archive
pub(crate) fn read<T: Read>(reader: &mut T) -> Result<Self, Error> {
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,
}),
}
}
/// Writes a `File` structure into a stream of bytes
/// # Errors
/// Can return `Error` if io fails
pub(crate) fn write<T: 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!(),
}
}
}