use std::borrow::Cow;
use std::io::{Cursor, Read};
use std::result::Result;
use std::sync::Arc;
use rust_embed::Embed;
pub type AssetManagerRc = Arc<dyn AssetManagerTrait + Send + Sync>;
pub trait AssetManagerTrait {
fn open(&self, name: &str) -> AssetResult<AssetFileBox>;
fn open_or_err(&self, name: &str) -> AssetFileBox {
self.open(name).expect("Unable to open asset")
}
}
pub type AssetFileBox = Box<dyn AssetFileTrait + Send>;
pub trait AssetFileTrait { fn read(&self) -> AssetResult<Box<dyn Read + Send + Sync>>;
fn read_str(&self) -> AssetResult<String>;
fn read_or_err(&self) -> Box<dyn Read + Send + Sync> {
self.read().expect("Unable to read asset")
}
fn read_str_or_err(&self) -> String {
self.read_str().expect("Unable to read asset")
}
}
pub type AssetResult<T> = Result<T, AssetError>;
#[derive(Debug)]
pub enum AssetError {
NotFound,
Decode,
}
#[derive(Embed)]
#[folder = "asset"]
struct Asset;
pub struct EmbedAssetManager;
impl EmbedAssetManager {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
}
}
}
impl AssetManagerTrait for EmbedAssetManager {
fn open(&self, name: &str) -> AssetResult<AssetFileBox> {
assert!(name.starts_with("/"));
let file = Asset::get(&name[1..]).ok_or(AssetError::NotFound)?;
Ok(Box::new(EmbedAssetFile::new(file.data)))
}
}
struct EmbedAssetFile {
data: Cow<'static, [u8]>,
}
impl EmbedAssetFile {
fn new(data: Cow<'static, [u8]>) -> Self {
Self {
data,
}
}
}
impl AssetFileTrait for EmbedAssetFile {
fn read(&self) -> AssetResult<Box<dyn Read + Send + Sync>> {
Ok(Box::new(Cursor::new(self.data.clone())))
}
fn read_str(&self) -> AssetResult<String> {
Ok(String::from(str::from_utf8(&self.data).map_err(|_| AssetError::Decode)?))
}
}