1use anyhow::Error;
2use flate2::read::GzDecoder;
3use std::collections::HashMap;
4use std::io::Read;
5use std::sync::Arc;
6use tar::Archive;
7use thiserror::Error;
8
9#[derive(Debug, Error)]
10enum Reason {
11 #[error("wrong format")]
12 WrongFormat,
13}
14
15#[derive(Clone, Debug)]
16pub struct Assets {
17 files: Arc<HashMap<String, Vec<u8>>>,
18}
19
20impl Assets {
21 pub fn parse(assets: &[u8]) -> Result<Assets, Error> {
23 let tar = GzDecoder::new(assets);
24 let mut archive = Archive::new(tar);
25 let mut files = HashMap::new();
26 for entry in archive.entries()? {
27 let mut entry = entry?;
28 let mut data = Vec::new();
29 entry.read_to_end(&mut data)?;
30 if !data.is_empty() {
31 let name = entry
32 .path()?
33 .to_str()
34 .ok_or(Reason::WrongFormat)?
35 .to_owned();
36 #[cfg(debug_assertions)]
37 log::trace!("Register asset file: {}", name);
38 files.insert(name, data);
39 }
40 }
41 Ok(Self {
42 files: Arc::new(files),
43 })
44 }
45
46 pub fn get(&self, path: &str) -> Option<&[u8]> {
47 self.files.get(path).map(Vec::as_ref)
48 }
49
50 pub fn iter(&self) -> impl Iterator<Item = (&str, &[u8])> {
51 self.files.iter().map(|(s, d)| (s.as_ref(), d.as_ref()))
52 }
53}
54
55pub mod build {
56 use anyhow::Error;
57 use flate2::write::GzEncoder;
58 use flate2::Compression;
59 use std::fs::File;
60
61 pub fn pack(dir: &str, to: &str) -> Result<(), Error> {
62 let tar_gz = File::create(to)?;
63 let enc = GzEncoder::new(tar_gz, Compression::default());
64 let mut tar = tar::Builder::new(enc);
65 tar.append_dir_all(".", dir)?;
66 Ok(())
67 }
68}