use crate::model::*; use std::collections::*; use std::io; use std::path::{Path, PathBuf}; mod image_convert; mod metadata_file; pub type TextureFileResult = Result, TextureFileError>; pub enum TextureFileError { NotFound, IoError(io::Error), ImageError(::image::ImageError), } impl From for TextureFileError { fn from(err: io::Error) -> Self { TextureFileError::IoError(err) } } impl From<::image::ImageError> for TextureFileError { fn from(err: ::image::ImageError) -> Self { TextureFileError::ImageError(err) } } pub struct DataStore { //data_path: PathBuf, base_dir: PathBuf, textures: HashSet, id_index: HashMap, name_index: HashMap, _preview_cache: HashMap<(TextureFormat, Sha256), Vec>, } impl DataStore { fn texture_base_path(&self) -> PathBuf { let mut path = self.base_dir.clone(); path.push("textures"); path } fn texture_file_path(&self, sha: &Sha256) -> PathBuf { let mut path = self.texture_base_path(); path.push(sha.as_hex_string()); path } fn index_file_path(&self) -> PathBuf { let mut path = self.base_dir.clone(); path.push("collection.json"); path } pub fn new(base_dir: &Path) -> io::Result { let mut store = DataStore { base_dir: base_dir.into(), textures: Default::default(), id_index: Default::default(), name_index: Default::default(), _preview_cache: Default::default(), }; let metadata_file = metadata_file::MetadataFile::load(base_dir)?; for texture in metadata_file.textures.iter() { match store.insert(texture.clone()) { true => (), false => { panic!("inserting {:#?} failed !!!", texture); // TODO: What should be done? } } } store.garbage_collect()?; store.flush_metadata()?; Ok(store) } pub fn texture_by_id(&self, id: &str) -> Option { self.id_index.get(id).cloned() } pub fn texture_by_name(&self, id: &str) -> Option { self.name_index.get(id).cloned() } pub fn insert(&mut self, texture: Texture) -> bool { if self.id_index.contains_key(&texture.id) { return false; } if self.name_index.contains_key(&texture.name) { return false; } if !self.is_texture_file_on_disk(&texture.texture_hash) { return false; } self.id_index.insert(texture.id.clone(), texture.clone()); self.name_index .insert(texture.name.clone(), texture.clone()); self.textures.insert(texture.clone()); true } /// returns true if successful pub fn delete(&mut self, tex: &Texture) -> bool { if self.textures.remove(tex) { // remove assert!(self.id_index.remove(&tex.id).is_some()); assert!(self.name_index.remove(&tex.name).is_some()); // don't delete cache, since it could be used // by other texture. true } else { false } } /// Check if the texture, given by hash, physically exists on the file system. pub fn is_texture_file_on_disk(&self, hash: &Sha256) -> bool { let file_path = self.texture_file_path(&hash); file_path.is_file() } pub fn read_texture_file_by_hash(&self, hash: &Sha256) -> io::Result> { use std::fs::*; use std::io::*; let file_path = self.texture_file_path(&hash); let mut file = File::open(file_path)?; let mut buffer = Vec::new(); file.read_to_end(&mut buffer)?; Ok(buffer) } pub fn store_texture_file(&mut self, data: &[u8]) -> io::Result<()> { use std::fs; use std::io::Write; let hash = crate::model::Sha256::from_data(data); fs::create_dir_all(&self.texture_base_path())?; let file_path = self.texture_file_path(&hash); let mut file = fs::File::create(&file_path)?; file.write_all(data) } pub fn flush_metadata(&self) -> io::Result<()> { let f = metadata_file::MetadataFile::from_iterator(self.textures.iter()); f.store(self.index_file_path().as_path()) } pub fn get_texture_preview( &mut self, _hash: &Sha256, _desired_format: TextureFormat, ) -> TextureFileResult { unimplemented!(); } pub fn borrow_textures(&self) -> impl Iterator { self.textures.iter() } pub fn garbage_collect(&mut self) -> io::Result<()> { //unimplemented!() /// VERY TODO: eprintln!("WARNING: We are sorry the GC isn't implemented yet :'( "); Ok(()) } }