use std::{
cell::RefCell,
collections::HashMap,
path::{Path, PathBuf},
sync::Arc,
};
use gled_model::asset::picture::is_picture_path;
use gled_protocol::{bytes::SharedBytes, state::ClientInput};
pub(crate) type Picture = Result<SharedBytes, String>;
thread_local! {
static PICTURES: RefCell<HashMap<PathBuf, Option<Picture>>> = RefCell::new(HashMap::new());
}
pub(crate) fn get(path: &Path) -> Option<Picture> {
if !is_picture_path(path) {
return Some(Err(format!(
"{} is not in the asset repository",
path.display()
)));
}
PICTURES.with(|pictures| {
let mut pictures = pictures.borrow_mut();
match pictures.get(path) {
Some(answer) => answer.clone(),
None => {
pictures.insert(path.to_path_buf(), None);
ClientInput::RequestPicture(path.to_path_buf()).enqueue();
None
}
}
})
}
pub(crate) fn receive(path: PathBuf, picture: Picture) {
PICTURES.with(|pictures| pictures.borrow_mut().insert(path, Some(picture)));
}
pub(crate) fn store(path: PathBuf, bytes: Vec<u8>) {
let bytes = SharedBytes(Arc::new(bytes));
receive(path.clone(), Ok(bytes.clone()));
ClientInput::SavePicture { path, bytes }.enqueue();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_stored_picture_is_here_at_once() {
let path = PathBuf::from("pictures/stored.png");
store(path.clone(), b"png".to_vec());
assert_eq!(
get(&path)
.and_then(Result::ok)
.map(|bytes| bytes.0.to_vec()),
Some(b"png".to_vec())
);
}
#[test]
fn a_picture_is_asked_for_once_and_then_waited_for() {
let path = PathBuf::from("pictures/asked.png");
assert_eq!(get(&path), None);
assert_eq!(get(&path), None);
receive(path.clone(), Err("gone".to_owned()));
assert_eq!(get(&path), Some(Err("gone".to_owned())));
}
#[test]
fn a_path_outside_the_repository_has_no_picture() {
assert!(matches!(
get(Path::new("/home/someone/stage.png")),
Some(Err(_))
));
}
}