cardinal_zip/
lib.rs

1use std::fmt;
2
3use tracing::{debug, trace};
4
5pub mod generate;
6pub mod utils;
7pub mod versions;
8
9#[cfg(target_arch = "wasm32")]
10pub use generate::generate_latest_czip;
11pub use generate::{generate_latest, generate_latest_bin, LatestCzip};
12
13pub use crate::versions::v1::CZipV1;
14
15#[derive(Debug)]
16pub enum CZipError {
17    UnexpectedEof(&'static str),
18    InvalidMagic(u8),
19    InvalidUtf8 {
20        label: &'static str,
21        source: std::str::Utf8Error,
22    },
23    Toml(toml::de::Error),
24    TrailingData(usize),
25}
26
27impl fmt::Display for CZipError {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        match self {
30            CZipError::UnexpectedEof(label) => {
31                write!(f, "unexpected end of data while reading {label}")
32            }
33            CZipError::InvalidMagic(id) => write!(f, "unknown CZip magic identifier: {id}"),
34            CZipError::InvalidUtf8 { label, .. } => write!(f, "{label} contains invalid UTF-8"),
35            CZipError::Toml(_) => write!(f, "configuration TOML is invalid"),
36            CZipError::TrailingData(bytes) => {
37                write!(
38                    f,
39                    "trailing data detected after parsing archive ({bytes} bytes)"
40                )
41            }
42        }
43    }
44}
45
46impl std::error::Error for CZipError {
47    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
48        match self {
49            CZipError::InvalidUtf8 { source, .. } => Some(source),
50            CZipError::Toml(err) => Some(err),
51            _ => None,
52        }
53    }
54}
55
56pub type Result<T> = std::result::Result<T, CZipError>;
57
58#[derive(Debug, Clone)]
59pub enum CZip {
60    V1(CZipV1),
61}
62
63impl From<CZip> for Vec<u8> {
64    fn from(value: CZip) -> Self {
65        let mut buffer = Vec::new();
66        let id = get_magic_identifier(&value);
67        trace!(magic = id, "Serializing CZip archive");
68        buffer.push(id);
69
70        match value {
71            CZip::V1(inner) => {
72                debug!("Encoding CZip V1 payload");
73                let payload: Vec<u8> = inner.into();
74                buffer.extend_from_slice(&payload);
75            }
76        }
77
78        buffer
79    }
80}
81
82fn get_magic_identifier(czip: &CZip) -> u8 {
83    match czip {
84        CZip::V1(_) => 1,
85    }
86}
87
88impl TryFrom<&[u8]> for CZip {
89    type Error = CZipError;
90
91    fn try_from(bytes: &[u8]) -> Result<Self> {
92        let (first, rest) = bytes
93            .split_first()
94            .ok_or(CZipError::UnexpectedEof("magic identifier"))?;
95
96        match first {
97            1 => {
98                trace!(magic = *first, "Detected CZip V1 archive");
99                let archive = CZipV1::try_from(rest)?;
100                Ok(CZip::V1(archive))
101            }
102            id => Err(CZipError::InvalidMagic(*id)),
103        }
104    }
105}