use crate::global::{reg_entry::RegistryEntry, flags::Flags};
use crate::global::error::InternalResult;
#[cfg(feature = "compression")]
use crate::global::compressor::{CompressionAlgorithm, Compressor};
#[cfg(feature = "crypto")]
use crate::crypto::Encryptor;
#[cfg(not(feature = "crypto"))]
type Encryptor = ();
use std::{io::Read, sync::Arc};
#[derive(Debug, Clone, Copy, Default)]
#[cfg(feature = "compression")]
#[cfg_attr(docsrs, doc(cfg(feature = "compression")))]
pub enum CompressMode {
#[default]
Never,
Always,
Detect,
}
#[derive(Debug, Default, Clone)]
pub struct Leaf<R = &'static [u8]> {
pub handle: R,
pub id: Arc<str>,
pub content_version: u8,
pub flags: Flags,
#[cfg(feature = "compression")]
#[cfg_attr(docsrs, doc(cfg(feature = "compression")))]
pub compress: CompressMode,
#[cfg_attr(docsrs, doc(cfg(feature = "compression")))]
#[cfg(feature = "compression")]
pub compression_algo: CompressionAlgorithm,
#[cfg(feature = "crypto")]
#[cfg_attr(docsrs, doc(cfg(feature = "crypto")))]
pub encrypt: bool,
#[cfg(feature = "crypto")]
#[cfg_attr(docsrs, doc(cfg(feature = "crypto")))]
pub sign: bool,
}
impl<R: Read + Send + Sync> Leaf<R> {
pub fn new<S: AsRef<str>>(handle: R, id: S) -> Leaf<R> {
let default = Leaf::<&'static [u8]>::default();
Leaf {
handle,
id: Arc::from(id.as_ref()),
content_version: default.content_version,
flags: default.flags,
#[cfg(feature = "compression")]
compress: default.compress,
#[cfg(feature = "compression")]
compression_algo: default.compression_algo,
#[cfg(feature = "crypto")]
encrypt: default.encrypt,
#[cfg(feature = "crypto")]
sign: default.sign,
}
}
pub fn template<R2>(self, other: &Leaf<R2>) -> Self {
Leaf {
handle: self.handle,
id: self.id,
content_version: other.content_version,
flags: other.flags,
#[cfg(feature = "compression")]
compress: other.compress,
#[cfg(feature = "compression")]
compression_algo: other.compression_algo,
#[cfg(feature = "crypto")]
encrypt: other.encrypt,
#[cfg(feature = "crypto")]
sign: other.sign,
}
}
#[cfg(feature = "compression")]
#[cfg_attr(docsrs, doc(cfg(feature = "compression")))]
pub fn compress(mut self, compress: CompressMode) -> Self {
self.compress = compress;
self
}
pub fn version(mut self, content_version: u8) -> Self {
self.content_version = content_version;
self
}
pub fn flags(mut self, flags: Flags) -> Self {
self.flags = flags;
self
}
#[cfg(feature = "crypto")]
pub fn encrypt(mut self, encrypt: bool) -> Self {
self.encrypt = encrypt;
self
}
#[cfg(feature = "crypto")]
pub fn sign(mut self, sign: bool) -> Self {
self.sign = sign;
self
}
#[cfg(feature = "compression")]
pub fn compression_algo(mut self, compression_algo: CompressionAlgorithm) -> Self {
self.compression_algo = compression_algo;
self
}
}
impl<R> From<&mut Leaf<R>> for RegistryEntry {
fn from(leaf: &mut Leaf<R>) -> Self {
RegistryEntry {
id: leaf.id.clone(),
flags: leaf.flags,
content_version: leaf.content_version,
..RegistryEntry::empty()
}
}
}
pub(crate) struct ProcessedLeaf {
pub(crate) data: Vec<u8>,
pub(crate) entry: RegistryEntry,
#[cfg(feature = "crypto")]
pub(crate) sign: bool,
}
#[inline(never)]
pub(crate) fn process_leaf<R: Read + Send + Sync>(
leaf: &mut Leaf<R>, _encryptor: Option<&Encryptor>,
) -> InternalResult<ProcessedLeaf> {
let mut entry: RegistryEntry = leaf.into();
let mut raw = Vec::new();
#[cfg(feature = "compression")]
match leaf.compress {
CompressMode::Never => {
leaf.handle.read_to_end(&mut raw)?;
},
CompressMode::Always => {
Compressor::new(&mut leaf.handle).compress(leaf.compression_algo, &mut raw)?;
entry.flags.force_set(Flags::COMPRESSED_FLAG, true);
entry.flags.force_set(leaf.compression_algo.into(), true);
},
CompressMode::Detect => {
let mut buffer = Vec::new();
leaf.handle.read_to_end(&mut buffer)?;
let mut compressed_data = Vec::new();
Compressor::new(buffer.as_slice()).compress(leaf.compression_algo, &mut compressed_data)?;
if compressed_data.len() <= buffer.len() {
entry.flags.force_set(Flags::COMPRESSED_FLAG, true);
entry.flags.force_set(leaf.compression_algo.into(), true);
raw = compressed_data;
} else {
buffer.as_slice().read_to_end(&mut raw)?;
};
},
}
#[cfg(not(feature = "compression"))]
{
use crate::global::error::InternalError;
if entry.flags.contains(Flags::COMPRESSED_FLAG) {
return Err(InternalError::MissingFeatureError("compression"));
};
leaf.handle.read_to_end(&mut raw)?;
}
#[cfg(feature = "crypto")]
if leaf.encrypt {
if let Some(ex) = _encryptor {
raw = ex.encrypt(&raw)?;
entry.flags.force_set(Flags::ENCRYPTED_FLAG, true);
}
}
Ok(ProcessedLeaf {
data: raw,
entry,
#[cfg(feature = "crypto")]
sign: leaf.sign,
})
}