#![allow(clippy::len_without_is_empty)]
use bytes::Bytes;
use std::fmt;
#[cfg(feature = "compress")]
use crate::Compression;
use crate::{CompressionAlgorithm, CompressionError, HashSum};
#[derive(Debug, Clone, PartialEq)]
pub struct Chunk(pub(crate) Bytes);
impl<T> From<T> for Chunk
where
T: Into<bytes::Bytes>,
{
fn from(b: T) -> Self {
Self(b.into())
}
}
impl Chunk {
#[inline]
pub fn data(&self) -> &[u8] {
&self.0[..]
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn verify(self) -> VerifiedChunk {
VerifiedChunk::new(self)
}
#[cfg(feature = "compress")]
#[inline]
pub fn compress(
self,
compression: Option<Compression>,
) -> Result<CompressedChunk, CompressionError> {
CompressedChunk::try_compress(compression, self)
}
#[inline]
pub fn into_inner(self) -> Bytes {
self.0
}
}
#[derive(Debug, Clone)]
pub struct VerifiedChunk {
pub(crate) chunk: Chunk,
pub(crate) hash_sum: HashSum,
}
impl VerifiedChunk {
pub fn new(chunk: Chunk) -> Self {
Self {
hash_sum: HashSum::b2_digest(chunk.data()),
chunk,
}
}
#[inline]
pub fn len(&self) -> usize {
self.chunk.len()
}
#[inline]
pub fn chunk(&self) -> &Chunk {
&self.chunk
}
#[inline]
pub fn data(&self) -> &[u8] {
self.chunk.data()
}
#[inline]
pub fn hash(&self) -> &HashSum {
&self.hash_sum
}
#[inline]
pub fn into_parts(self) -> (HashSum, Chunk) {
(self.hash_sum, self.chunk)
}
}
#[derive(Debug, Clone)]
pub struct CompressedChunk {
pub(crate) data: Bytes,
pub(crate) source_size: usize,
pub(crate) compression: Option<CompressionAlgorithm>,
}
impl CompressedChunk {
#[cfg(feature = "compress")]
pub fn try_compress(
compression: Option<Compression>,
chunk: Chunk,
) -> Result<CompressedChunk, CompressionError> {
if let Some(compression) = compression {
Ok(CompressedChunk {
source_size: chunk.len(),
data: compression.compress(chunk.0)?,
compression: Some(compression.algorithm),
})
} else {
Ok(CompressedChunk {
source_size: chunk.len(),
data: chunk.0,
compression: None,
})
}
}
#[inline]
pub fn data(&self) -> &[u8] {
&self.data[..]
}
#[inline]
pub fn len(&self) -> usize {
self.data.len()
}
pub fn decompress(self) -> Result<Chunk, CompressionError> {
Ok(match self.compression {
Some(compression) => Chunk::from(compression.decompress(self.data, self.source_size)?),
None => Chunk::from(self.data),
})
}
#[inline]
pub fn compression(&self) -> Option<CompressionAlgorithm> {
self.compression
}
#[inline]
pub fn into_inner(self) -> (Option<CompressionAlgorithm>, Bytes) {
(self.compression, self.data)
}
}
#[derive(Debug, Clone)]
pub struct CompressedArchiveChunk {
pub(crate) chunk: CompressedChunk,
pub(crate) expected_hash: HashSum,
}
impl CompressedArchiveChunk {
pub fn len(&self) -> usize {
self.chunk.len()
}
pub fn decompress(self) -> Result<ArchiveChunk, CompressionError> {
Ok(ArchiveChunk {
chunk: self.chunk.decompress()?,
expected_hash: self.expected_hash,
})
}
}
#[derive(Debug)]
pub struct HashSumMismatchError {
expected: HashSum,
got: HashSum,
pub invalid_chunk: Chunk,
}
impl std::error::Error for HashSumMismatchError {}
impl fmt::Display for HashSumMismatchError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "expected hash {} but got {}", self.expected, self.got)
}
}
#[derive(Debug, Clone)]
pub struct ArchiveChunk {
pub(crate) chunk: Chunk,
pub(crate) expected_hash: HashSum,
}
impl ArchiveChunk {
pub fn len(&self) -> usize {
self.chunk.len()
}
pub fn verify(self) -> Result<VerifiedChunk, HashSumMismatchError> {
let mut hash_sum = HashSum::b2_digest(self.chunk.data());
hash_sum.truncate(self.expected_hash.len());
if hash_sum != self.expected_hash {
Err(HashSumMismatchError {
expected: self.expected_hash,
got: hash_sum,
invalid_chunk: self.chunk,
})
} else {
Ok(VerifiedChunk {
chunk: self.chunk,
hash_sum,
})
}
}
}