unitypkg_core/
writer.rs

1use std::io::{self, Write};
2
3use flate2::{write::GzEncoder, Compression};
4use tar::{Builder, Header};
5
6use crate::Package;
7
8pub fn write_package<W: Write>(
9    package: Package,
10    w: W,
11    compression: Compression,
12) -> Result<(), io::Error> {
13    let encoder = GzEncoder::new(w, compression);
14    let mut builder = Builder::new(encoder);
15
16    for (uuid, asset) in package.assets {
17        let guid_str = uuid.to_string().replace("-", "");
18
19        {
20            let path = format!("./{}/pathname", guid_str);
21            let mut header = Header::new_gnu();
22            header.set_path(&path)?;
23            header.set_size(asset.pathname.len() as u64);
24            header.set_cksum();
25            builder.append(&header, asset.pathname.as_bytes())?;
26        }
27
28        if let Some(preview) = asset.preview {
29            let path = format!("./{}/preview.png", guid_str);
30            let mut header = Header::new_gnu();
31            header.set_path(&path)?;
32            header.set_size(preview.len() as u64);
33            header.set_cksum();
34            builder.append(&header, preview.as_slice())?;
35        }
36
37        if let Some(meta) = asset.meta {
38            let path = format!("./{}/asset.meta", guid_str);
39            let mut header = Header::new_gnu();
40            header.set_path(&path)?;
41            header.set_size(meta.len() as u64);
42            header.set_cksum();
43            builder.append(&header, meta.as_slice())?;
44        }
45
46        if let Some(data) = asset.data {
47            let path = format!("./{}/asset", guid_str);
48            let mut header = Header::new_gnu();
49            header.set_path(&path)?;
50            header.set_size(data.len() as u64);
51            header.set_cksum();
52            builder.append(&header, data.as_slice())?;
53        }
54    }
55
56    builder.finish()?;
57
58    Ok(())
59}
60
61#[cfg(test)]
62mod tests {
63    use std::{
64        collections::HashMap,
65        io::{Cursor, Read},
66    };
67
68    use flate2::read::GzDecoder;
69    use tar::Archive;
70    use uuid::Uuid;
71
72    use super::*;
73    use crate::PackageAssetBuilder;
74
75    #[test]
76    fn write() {
77        let mut package = Package::new();
78        let uuid = Uuid::new_v4();
79        let guid_str = uuid.to_string().replace("-", "");
80        let mut builder = PackageAssetBuilder::default();
81        builder.pathname = Some("test_pathname".to_string());
82        builder.preview = Some(vec![1, 2, 3, 4]);
83        builder.meta = Some(vec![5, 6, 7, 8]);
84        builder.data = Some(vec![9, 10, 11, 12]);
85
86        package.assets.insert(uuid, builder.build());
87
88        let mut buffer = Cursor::new(Vec::new());
89        write_package(package, &mut buffer, Compression::default()).unwrap();
90
91        let output = buffer.into_inner();
92        assert!(!output.is_empty());
93
94        let decoder = GzDecoder::new(Cursor::new(output));
95        let mut archive = Archive::new(decoder);
96
97        let mut expected_files = HashMap::new();
98        expected_files.insert(
99            format!("{}/pathname", guid_str),
100            "test_pathname".as_bytes().to_vec(),
101        );
102        expected_files.insert(format!("{}/preview.png", guid_str), vec![1, 2, 3, 4]);
103        expected_files.insert(format!("{}/asset.meta", guid_str), vec![5, 6, 7, 8]);
104        expected_files.insert(format!("{}/asset", guid_str), vec![9, 10, 11, 12]);
105
106        for entry in archive.entries().unwrap() {
107            let mut entry = entry.unwrap();
108            let path = entry.path().unwrap().to_str().unwrap().to_string();
109            let mut content = Vec::new();
110            entry.read_to_end(&mut content).unwrap();
111
112            if let Some(expected_content) = expected_files.get(&path) {
113                assert_eq!(content, *expected_content);
114                expected_files.remove(&path);
115            } else {
116                panic!("Unexpected file in archive: {}", path);
117            }
118        }
119
120        assert!(expected_files.is_empty());
121    }
122}