From 39b2a2c442b6d96663b0f87fca117017fd33d63e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20F=C3=BCrderer?= Date: Tue, 30 Apr 2019 19:08:40 +0200 Subject: [PATCH] Implement some persistency features --- .../texture-sync-server/src/model/sha256.rs | 27 ++++-- .../src/model/texture_format.rs | 2 +- .../src/persistency/collection_file.rs | 18 ++++ .../src/persistency/mod.rs | 83 +++++++++++++++++-- 4 files changed, 113 insertions(+), 17 deletions(-) create mode 100644 server/texture-sync-server/src/persistency/collection_file.rs diff --git a/server/texture-sync-server/src/model/sha256.rs b/server/texture-sync-server/src/model/sha256.rs index 09be459..e0c5df4 100644 --- a/server/texture-sync-server/src/model/sha256.rs +++ b/server/texture-sync-server/src/model/sha256.rs @@ -1,20 +1,29 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; -#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)] +#[derive(Clone, Debug, Deserialize, Serialize, Eq, Hash, PartialEq)] pub struct Sha256(#[serde(serialize_with = "as_hex", deserialize_with = "from_hex")] pub [u8; 32]); +fn hash_to_hex_string(hash: &[u8; 32]) -> String { + use std::fmt::*; + + let mut hex_string = String::with_capacity(64); + for digit in hash.iter() { + write!(hex_string, "{:02X}", digit).unwrap(); + } + hex_string +} + +impl Sha256 { + pub fn create_hex_string(&self) -> String { + hash_to_hex_string(&self.0) + } +} + fn as_hex(hash: &[u8; 32], serializer: S) -> Result where S: Serializer, { - use std::fmt::*; - - let mut hex_string = String::new(); - for digit in hash { - write!(hex_string, "{:02X}", digit).unwrap(); - } - - serializer.serialize_str(&hex_string) + serializer.serialize_str(&hash_to_hex_string(hash)) } fn from_hex<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error> diff --git a/server/texture-sync-server/src/model/texture_format.rs b/server/texture-sync-server/src/model/texture_format.rs index 98d342a..180e57c 100644 --- a/server/texture-sync-server/src/model/texture_format.rs +++ b/server/texture-sync-server/src/model/texture_format.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; -#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)] +#[derive(Clone, Debug, Deserialize, Serialize, Eq, Hash, PartialEq)] pub enum TextureFormat { #[serde(rename = "png")] PNG, diff --git a/server/texture-sync-server/src/persistency/collection_file.rs b/server/texture-sync-server/src/persistency/collection_file.rs new file mode 100644 index 0000000..960cf80 --- /dev/null +++ b/server/texture-sync-server/src/persistency/collection_file.rs @@ -0,0 +1,18 @@ +use crate::model::Texture; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::io; +use std::path::Path; + +#[derive(Deserialize, Serialize)] +struct CollectionFile { + textures: Vec, +} + +pub fn load_collection_file(path: &Path) -> io::Result> { + let file = fs::File::open(path)?; + let buf_reader = io::BufReader::new(file); + + let collection: CollectionFile = serde_json::from_reader(buf_reader)?; + Ok(collection.textures) +} diff --git a/server/texture-sync-server/src/persistency/mod.rs b/server/texture-sync-server/src/persistency/mod.rs index 22c85f9..c4f0bde 100644 --- a/server/texture-sync-server/src/persistency/mod.rs +++ b/server/texture-sync-server/src/persistency/mod.rs @@ -6,11 +6,13 @@ use crate::model::*; use std::collections::HashMap; +use std::fs; use std::io; use std::path::{Path, PathBuf}; use std::sync::Arc; pub use self::search::Query; +mod collection_file; mod image_convert; mod search; @@ -31,7 +33,15 @@ pub struct DataStore { impl DataStore { pub fn new(path: &Path) -> io::Result { - unimplemented!() + let base_path = path.to_path_buf(); + let mut collection_file_path = base_path.clone(); + collection_file_path.push("collection.json"); + + Ok(DataStore { + data_dir: base_path, + texture: collection_file::load_collection_file(&collection_file_path)?, + preview_cache: HashMap::new(), + }) } pub fn garbage_collect(&mut self) -> io::Result<()> { @@ -45,27 +55,86 @@ impl DataStore { /// returns true if successful pub fn delete(&mut self, tex: &Texture) -> bool { - unimplemented!(); + // Find the texture + let pos = self.texture.iter().position(|e| e == tex); + match pos { + // Remove it + Some(idx) => { + let removed = self.texture.remove(idx); + let key = (removed.format, removed.texture_hash); + self.preview_cache.remove(&key); + true + } + // Texture not found + None => false, + } } pub fn insert(&mut self, tex: Texture) -> bool { - unimplemented!(); + // Check for collisions + let pos = self + .texture + .iter() + .position(|e| e.id == tex.id || e.name == tex.name); + match pos { + // Texture with same name or id is already stored + Some(_) => false, + // Insert it + None => { + self.texture.push(tex); + true + } + } } pub fn by_name<'a>(&'a self, name: &str) -> Option<&'a Texture> { - unimplemented!(); + let pos = self.texture.iter().position(|e| e.name == name); + match pos { + Some(idx) => Some(self.texture.get(idx).unwrap()), + None => None, + } } pub fn by_id<'a, 'b>(&'a self, id: &'b str) -> Option<&'a Texture> { - unimplemented!(); + let pos = self.texture.iter().position(|e| e.id == id); + match pos { + Some(idx) => Some(self.texture.get(idx).unwrap()), + None => None, + } } pub fn has_hash(&self, hash: &Sha256) -> bool { - unimplemented!(); + // TODO What is the purpose of this function? + // Currently checks for textures in the list with the given hash. + // Should it check for an existing image file instead? + self.texture + .iter() + .position(|e| &e.texture_hash == hash) + .is_some() } pub fn get_texture_file(&mut self, hash: &Sha256) -> TextureFileResult { - unimplemented!(); + use io::Read; + + let mut file_path = self.data_dir.clone(); + file_path.push("textures"); + file_path.push(hash.create_hex_string().to_lowercase()); + let mut file = match fs::File::open(file_path) { + Ok(f) => f, + Err(e) => { + if e.kind() == io::ErrorKind::NotFound { + return Err(TextureFileError::NotFound); + } else { + return Err(TextureFileError::IoError(e)); + } + } + }; + let mut buffer = Vec::new(); + match file.read_to_end(&mut buffer) { + Ok(_) => (), + Err(e) => return Err(TextureFileError::IoError(e)), + }; + Ok(Arc::new(buffer)) } pub fn get_texture_preview(&mut self, hash: &Sha256) -> TextureFileResult {