2024-11-20 14:52:26 +00:00
|
|
|
use std::{
|
2024-11-21 14:08:36 +00:00
|
|
|
collections::{hash_map::Iter, HashMap},
|
2024-11-20 14:52:26 +00:00
|
|
|
fmt::{self, Display},
|
2024-11-21 23:46:05 +00:00
|
|
|
io::Read,
|
2024-11-20 14:52:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
use thiserror::Error;
|
|
|
|
|
|
|
|
#[derive(Debug, Error)]
|
2024-11-21 23:46:05 +00:00
|
|
|
pub enum Error {
|
|
|
|
#[error("Asset could not be found")]
|
|
|
|
NotFound,
|
|
|
|
#[error("Asset could not be opened")]
|
|
|
|
Inaccessible,
|
|
|
|
|
|
|
|
#[error("An unexpected IO error occured when retrieving an asset {0}")]
|
|
|
|
UnexpectedError(std::io::Error),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<std::io::Error> for Error {
|
|
|
|
fn from(err: std::io::Error) -> Error {
|
|
|
|
use std::io::ErrorKind::*;
|
|
|
|
|
|
|
|
match err.kind() {
|
|
|
|
NotFound => Error::NotFound,
|
|
|
|
PermissionDenied | UnexpectedEof => Error::Inaccessible,
|
|
|
|
_ => Error::UnexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
2024-11-20 14:52:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
|
|
|
|
pub struct AssetId(String);
|
|
|
|
|
|
|
|
impl Display for AssetId {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2024-11-21 14:08:36 +00:00
|
|
|
write!(f, "AssetId({})", self.0)
|
2024-11-20 14:52:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<&str> for AssetId {
|
|
|
|
fn from(s: &str) -> Self {
|
|
|
|
AssetId(s.to_owned())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-21 14:08:36 +00:00
|
|
|
impl From<String> for AssetId {
|
|
|
|
fn from(s: String) -> Self {
|
|
|
|
AssetId(s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct AssetIter<'a>(Iter<'a, AssetId, String>);
|
|
|
|
|
|
|
|
impl <'a> Iterator for AssetIter<'a> {
|
|
|
|
type Item = (&'a AssetId, &'a String);
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
self.0.next()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-20 14:52:26 +00:00
|
|
|
pub trait Assets {
|
2024-11-21 14:08:36 +00:00
|
|
|
fn assets<'a>(&'a self) -> AssetIter<'a>;
|
2024-11-20 14:52:26 +00:00
|
|
|
|
|
|
|
fn get(&self, asset_id: AssetId) -> Result<Vec<u8>, Error>;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct FsAssets {
|
|
|
|
assets: HashMap<AssetId, String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FsAssets {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
assets: HashMap::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn assets<'a>(&'a self) -> impl Iterator<Item = &'a AssetId> {
|
|
|
|
self.assets.keys()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Assets for FsAssets {
|
2024-11-21 14:08:36 +00:00
|
|
|
fn assets<'a>(&'a self) -> AssetIter<'a> {
|
|
|
|
AssetIter(self.assets.iter())
|
2024-11-20 14:52:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get(&self, asset_id: AssetId) -> Result<Vec<u8>, Error> {
|
2024-11-21 23:46:05 +00:00
|
|
|
let path = match self.assets.get(&asset_id) {
|
|
|
|
Some(asset) => Ok(asset),
|
|
|
|
None => Err(Error::NotFound),
|
|
|
|
}?;
|
|
|
|
let mut content: Vec<u8> = Vec::new();
|
|
|
|
let mut file = std::fs::File::open(&path)?;
|
|
|
|
file.read_to_end(&mut content)?;
|
|
|
|
Ok(content)
|
2024-11-20 14:52:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
pub mod mocks {
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
pub struct MemoryAssets {
|
|
|
|
assets: HashMap<AssetId, String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MemoryAssets {
|
|
|
|
pub fn new(data: Vec<(AssetId, String)>) -> Self {
|
|
|
|
let mut m = HashMap::new();
|
|
|
|
data.into_iter().for_each(|(asset, path)| {
|
|
|
|
m.insert(asset, path);
|
|
|
|
});
|
|
|
|
Self { assets: m }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Assets for MemoryAssets {
|
2024-11-21 14:08:36 +00:00
|
|
|
fn assets<'a>(&'a self) -> AssetIter<'a> {
|
|
|
|
AssetIter(self.assets.iter())
|
2024-11-20 14:52:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get(&self, asset_id: AssetId) -> Result<Vec<u8>, Error> {
|
|
|
|
unimplemented!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|