TextureSync/server/texture-sync-server/src/persistency/mod.rs

193 lines
4.9 KiB
Rust

use crate::model::*;
use std::collections::*;
use std::io;
use std::path::{Path, PathBuf};
mod image_convert;
mod metadata_file;
pub type TextureFileResult = Result<Vec<u8>, TextureFileError>;
pub enum TextureFileError {
NotFound,
IoError(io::Error),
ImageError(::image::ImageError),
}
impl From<io::Error> 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<Texture>,
id_index: HashMap<String, Texture>,
name_index: HashMap<String, Texture>,
_preview_cache: HashMap<(TextureFormat, Sha256), Vec<u8>>,
}
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<DataStore> {
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<Texture> {
self.id_index.get(id).cloned()
}
pub fn texture_by_name(&self, id: &str) -> Option<Texture> {
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<Vec<u8>> {
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<Item = &Texture> {
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(())
}
}