use std::sync::Arc;
pub trait FileSource: Send + Sync {
fn read(&self, path: &str) -> Option<Vec<u8>>;
}
#[allow(dead_code)]
pub struct DiskFileSource;
impl FileSource for DiskFileSource {
fn read(&self, path: &str) -> Option<Vec<u8>> {
std::fs::read(path).ok()
}
}
pub(crate) type FileSourceHandle = Arc<dyn FileSource>;
#[allow(dead_code)]
pub fn set_file_source(lua: &mlua::Lua, source: Arc<dyn FileSource>) {
lua.set_app_data::<FileSourceHandle>(source);
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
struct InMemSource(Vec<(String, Vec<u8>)>);
impl FileSource for InMemSource {
fn read(&self, path: &str) -> Option<Vec<u8>> {
self.0
.iter()
.find(|(p, _)| p == path)
.map(|(_, b)| b.clone())
}
}
#[test]
fn disk_source_reads_real_file() {
let tmp = tempfile::NamedTempFile::new().unwrap();
std::fs::write(tmp.path(), b"hello").unwrap();
let src = DiskFileSource;
let bytes = src.read(tmp.path().to_str().unwrap()).unwrap();
assert_eq!(bytes, b"hello");
}
#[test]
fn disk_source_returns_none_for_missing() {
let src = DiskFileSource;
assert!(src.read("/definitely/does/not/exist").is_none());
}
#[test]
fn in_mem_source_returns_registered_path() {
let src = InMemSource(vec![("hi.txt".into(), b"hello".to_vec())]);
assert_eq!(src.read("hi.txt").unwrap(), b"hello");
assert!(src.read("missing").is_none());
}
#[test]
fn set_file_source_stores_handle_in_app_data() {
let lua = mlua::Lua::new();
let src: Arc<dyn FileSource> = Arc::new(DiskFileSource);
set_file_source(&lua, src);
assert!(lua.app_data_ref::<FileSourceHandle>().is_some());
}
}