use anyhow::{Result, bail};
use flate2::{Compression as GzipLevel, read::GzDecoder, write::GzEncoder};
use serde::{Deserialize, Serialize};
use std::{
io::{Read, Write},
path::Path,
};
use crate::pack;
pub const SIG: &[u8; 8] = b"PACK DIR";
pub const VERSION: u8 = 1;
#[derive(Default, Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "enc-dec", derive(bincode::Encode, bincode::Decode))]
pub enum Packer {
#[default]
Raw,
Gzip(Gzip),
Xz(Xz),
}
impl Packer {
pub(crate) fn try_read_u8(data: &[u8], offset: &mut usize) -> Result<Self> {
let packer = data[*offset].try_into()?;
*offset += 1;
Ok(packer)
}
pub fn pack_dir<P: AsRef<Path>>(&self, dir: P, pack_name: &str) -> Result<()> {
pack(dir, pack_name, *self)
}
pub(crate) fn pack(&self, data: &[u8]) -> Result<Vec<u8>> {
match self {
Packer::Raw => Ok(data.to_vec()),
Packer::Gzip(level) => {
let mut encoder = GzEncoder::new(Vec::new(), level.into());
encoder.write_all(data)?;
Ok(encoder.finish()?)
}
Packer::Xz(level) => {
use xz2::write::XzEncoder;
let mut encoder = XzEncoder::new(Vec::new(), *level as u32);
encoder.write_all(data)?;
Ok(encoder.finish()?)
}
}
}
pub(crate) fn unpack(&self, data: &[u8]) -> Result<Vec<u8>> {
match self {
Packer::Raw => Ok(data.to_vec()),
Packer::Gzip(_) => {
let mut decoder = GzDecoder::new(data);
let mut decompressed = Vec::new();
decoder.read_to_end(&mut decompressed)?;
Ok(decompressed)
}
Packer::Xz(_) => {
use xz2::read::XzDecoder;
let mut decoder = XzDecoder::new(data);
let mut decompressed = Vec::new();
decoder.read_to_end(&mut decompressed)?;
Ok(decompressed)
}
}
}
}
impl TryFrom<u8> for Packer {
type Error = anyhow::Error;
fn try_from(value: u8) -> Result<Self> {
match value {
0 => Ok(Packer::Raw),
1 => Ok(Packer::Gzip(Gzip::default())),
2 => Ok(Packer::Xz(Xz::default())),
_ => bail!("Unsupported compression type: {}", value),
}
}
}
impl From<Packer> for u8 {
fn from(val: Packer) -> Self {
match val {
Packer::Raw => 0,
Packer::Gzip { .. } => 1,
Packer::Xz { .. } => 2,
}
}
}
pub struct Raw;
impl From<Raw> for Packer {
fn from(_: Raw) -> Self {
Packer::Raw
}
}
#[derive(Default, Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "enc-dec", derive(bincode::Encode, bincode::Decode))]
#[repr(u32)]
pub enum Xz {
Level1 = 1,
Level2 = 2,
Level3 = 3,
Level4 = 4,
Level5 = 5,
#[default]
Level6 = 6,
Level7 = 7,
Level8 = 8,
Level9 = 9,
}
impl Xz {
pub fn best() -> Self {
Self::Level9
}
pub fn fast() -> Self {
Self::Level1
}
pub fn packer(&self) -> Packer {
Packer::Xz(*self)
}
pub fn pack_dir<P: AsRef<Path>>(&self, dir: P, pack_name: &str) -> Result<()> {
pack(dir, pack_name, self.packer())
}
}
impl From<Xz> for Packer {
fn from(xz: Xz) -> Self {
Self::Xz(xz)
}
}
#[derive(Default, Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "enc-dec", derive(bincode::Encode, bincode::Decode))]
#[repr(u32)]
pub enum Gzip {
Level1 = 1,
Level2 = 2,
Level3 = 3,
Level4 = 4,
Level5 = 5,
#[default]
Level6 = 6,
Level7 = 7,
Level8 = 8,
Level9 = 9,
}
impl Gzip {
pub fn best() -> Self {
Self::Level9
}
pub fn fast() -> Self {
Self::Level1
}
pub fn packer(&self) -> Packer {
Packer::Gzip(*self)
}
pub fn pack_dir<P: AsRef<Path>>(&self, dir: P, pack_name: &str) -> Result<()> {
pack(dir, pack_name, self.packer())
}
}
impl From<&Gzip> for GzipLevel {
fn from(level: &Gzip) -> Self {
GzipLevel::new(*level as u32)
}
}
impl From<Gzip> for Packer {
fn from(gzip: Gzip) -> Self {
Self::Gzip(gzip)
}
}