use rust_embed::EmbeddedFile;
use crate::filesystem::{Filesystem, FilesystemError};
use async_trait::async_trait;
pub trait EmbedFilesystemProvider: Send + Sync {
fn get(&self, path: &str) -> Option<EmbeddedFile>;
}
pub struct EmbedFilesystem {
provider: Box<dyn EmbedFilesystemProvider>,
root_dir: String,
}
impl EmbedFilesystem {
#[inline]
pub fn new(provider: Box<dyn EmbedFilesystemProvider>) -> Self {
Self {
provider,
root_dir: String::new(),
}
}
pub fn with_root_dir(mut self, root_dir: &str) -> Self {
self.root_dir = if root_dir.is_empty() {
String::new()
} else {
root_dir.trim_end_matches(['/', '\\']).to_string() + "/"
};
self
}
}
#[async_trait]
impl Filesystem for EmbedFilesystem {
async fn read_bytes(&self, asset_path: &str) -> Result<Vec<u8>, FilesystemError> {
let prefixed_path = self.root_dir.clone() + asset_path;
let embedded = self
.provider
.get(&prefixed_path)
.ok_or_else(|| FilesystemError::NotFound(prefixed_path))?;
Ok(embedded.data.into_owned())
}
}
#[cfg(test)]
mod test {
use super::*;
use std::sync::Arc;
#[derive(rust_embed::Embed)]
#[folder = "$CARGO_MANIFEST_DIR/tests"]
struct TestEmbedFsProvider;
impl EmbedFilesystemProvider for TestEmbedFsProvider {
fn get(&self, path: &str) -> Option<EmbeddedFile> {
TestEmbedFsProvider::get(path)
}
}
#[test]
fn read_bytes() {
let fs: Arc<dyn Filesystem> = Arc::new(EmbedFilesystem::new(Box::new(TestEmbedFsProvider)));
let greeting = pollster::block_on(fs.read_bytes("test_data_0/hello.txt")).unwrap();
assert_eq!(greeting, b"Hello world\n");
}
#[test]
fn read_bytes_with_root() {
let fs: Arc<dyn Filesystem> = Arc::new(
EmbedFilesystem::new(Box::new(TestEmbedFsProvider)).with_root_dir("test_data_1///"),
);
let greeting = pollster::block_on(fs.read_bytes("hello.txt")).unwrap();
assert_eq!(greeting, b"Hello earth\n");
}
}