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}