use crate::Error;
use std::collections::HashMap;
use std::io::{Read, Write};
use std::path::Path;
pub struct Assets {
files: HashMap<String, Vec<u8>>,
}
impl Assets {
pub fn from_reader(reader: impl Read) -> Result<Self, Error> {
let mut archive = tar::Archive::new(reader);
let mut files = HashMap::new();
for entry in archive.entries()? {
let mut entry = entry?;
let path = entry.path()?;
let path_str = path.to_string_lossy().to_string();
let mut contents = Vec::new();
entry.read_to_end(&mut contents)?;
files.insert(path_str, contents);
}
Ok(Self { files })
}
pub fn get_string(&self, path: impl AsRef<str>) -> Result<String, Error> {
let path = path.as_ref();
let bytes = self.files.get(path).ok_or_else(|| Error::AssetNotFound(path.to_string()))?;
String::from_utf8(bytes.clone()).map_err(|e| Error::AssetUtf8Error(path.to_string(), e))
}
pub fn get_bytes(&self, path: impl AsRef<str>) -> Result<&[u8], Error> {
let path = path.as_ref();
self.files
.get(path)
.map(std::vec::Vec::as_slice)
.ok_or_else(|| Error::AssetNotFound(path.to_string()))
}
pub fn contains(&self, path: impl AsRef<str>) -> bool {
self.files.contains_key(path.as_ref())
}
pub fn list(&self) -> impl Iterator<Item = &str> {
self.files.keys().map(String::as_str)
}
}
pub fn pack_assets(out: impl Write, root: impl AsRef<Path>) -> Result<(), Error> {
let root = root.as_ref();
let mut builder = tar::Builder::new(out);
walk_dir(root, root, &mut builder)?;
builder.finish()?;
Ok(())
}
fn walk_dir(root: &Path, current: &Path, builder: &mut tar::Builder<impl Write>) -> Result<(), Error> {
if !current.is_dir() {
return Ok(());
}
for entry in std::fs::read_dir(current)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
walk_dir(root, &path, builder)?;
} else if path.is_file() {
let relative = path.strip_prefix(root)?;
builder.append_path_with_name(&path, relative)?;
}
}
Ok(())
}