diff --git a/server/texture-sync-server/src/model/mod.rs b/server/texture-sync-server/src/model/mod.rs index 4737e34..e06b579 100644 --- a/server/texture-sync-server/src/model/mod.rs +++ b/server/texture-sync-server/src/model/mod.rs @@ -3,6 +3,8 @@ #![allow(unused_variables)] #![allow(dead_code)] +use std::io; + mod sha256; pub use sha256::Sha256; @@ -18,3 +20,14 @@ pub struct Texture { pub resolution: (usize, usize), pub texture_hash: Sha256, } + +pub enum ReplaceTextureStatus { + // Done. + Ok, + + // Call Again With Texture Binary + NeedTextureData(Sha256), + + // Name or id already in use + Conflict, +} diff --git a/server/texture-sync-server/src/persistency/mod.rs b/server/texture-sync-server/src/persistency/mod.rs index c4f0bde..992e696 100644 --- a/server/texture-sync-server/src/persistency/mod.rs +++ b/server/texture-sync-server/src/persistency/mod.rs @@ -70,47 +70,85 @@ impl DataStore { } } - pub fn insert(&mut self, tex: Texture) -> bool { + /// * `data` The content of the texture file, if available. + pub fn insert( + &mut self, + tex: Texture, + data: Option>>, + ) -> io::Result { + use io::Write; + // Check for collisions - let pos = self + if 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, + .find(|e| e.id == tex.id || e.name == tex.name) + .is_some() + { + // Name or id already in use + Ok(ReplaceTextureStatus::Conflict) + } else { // Insert it - None => { + if self.has_hash(&tex.texture_hash)? { self.texture.push(tex); - true + Ok(ReplaceTextureStatus::Ok) + } else { + match data { + None => Ok(ReplaceTextureStatus::NeedTextureData(tex.texture_hash)), + Some(blob) => { + let mut tmp_image_path = self.data_dir.clone(); + tmp_image_path.push("textures"); + tmp_image_path.push(format!( + "{}_new", + tex.texture_hash.create_hex_string().to_lowercase() + )); + let mut final_image_path = self.data_dir.clone(); + final_image_path.push("textures"); + final_image_path.push(tex.texture_hash.create_hex_string().to_lowercase()); + { + let mut writer = fs::File::create(&tmp_image_path)?; + writer.write_all(&blob)?; + } + fs::rename(tmp_image_path, final_image_path)?; + Ok(ReplaceTextureStatus::Ok) + } + } } } } pub fn by_name<'a>(&'a self, name: &str) -> Option<&'a Texture> { - let pos = self.texture.iter().position(|e| e.name == name); - match pos { - Some(idx) => Some(self.texture.get(idx).unwrap()), - None => None, - } + self.texture.iter().find(|e| e.name == name) } pub fn by_id<'a, 'b>(&'a self, id: &'b str) -> Option<&'a Texture> { - let pos = self.texture.iter().position(|e| e.id == id); - match pos { - Some(idx) => Some(self.texture.get(idx).unwrap()), - None => None, - } + self.texture.iter().find(|e| e.id == id) } - pub fn has_hash(&self, hash: &Sha256) -> bool { - // 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() + /// Check if the texture, given by hash, physically exists on the file system. + pub fn has_hash(&self, hash: &Sha256) -> io::Result { + let mut texture_path = self.data_dir.clone(); + texture_path.push("textures"); + texture_path.push(hash.create_hex_string().to_lowercase()); + match texture_path.metadata() { + Ok(meta) => { + if meta.is_file() { + Ok(true) + } else { + Err(io::Error::new( + io::ErrorKind::Other, + format!("{:?} exists but is not a regular file.", texture_path), + )) + } + } + Err(e) => { + if e.kind() == io::ErrorKind::NotFound { + Ok(false) + } else { + Err(e) + } + } + } } pub fn get_texture_file(&mut self, hash: &Sha256) -> TextureFileResult { diff --git a/server/texture-sync-server/src/protocol/error.rs b/server/texture-sync-server/src/protocol/error.rs index daecb0d..32e1ac3 100644 --- a/server/texture-sync-server/src/protocol/error.rs +++ b/server/texture-sync-server/src/protocol/error.rs @@ -1,12 +1,5 @@ use crate::model::*; -pub enum ReplaceTextureStatus { - // Call Again With Texture Binary - NeedTextureData(Sha256), - // Done. - Ok, -} - pub type ProtocolResult = Result; pub enum ProtocolError { BadRequest(String), diff --git a/server/texture-sync-server/src/protocol/implementation/listen_forever.rs b/server/texture-sync-server/src/protocol/implementation/listen_forever.rs index 7177cb1..7880f01 100644 --- a/server/texture-sync-server/src/protocol/implementation/listen_forever.rs +++ b/server/texture-sync-server/src/protocol/implementation/listen_forever.rs @@ -109,6 +109,10 @@ where panic!("Contract Violation: handler must not return NeedTextureData \ when data is given."); } + Ok(ReplaceTextureStatus::Conflict) => { + connection + .send(&Package::Error(409, "Conflict".to_string()))?; + } Err(err) => { connection.send(&Package::from(err))?; } @@ -125,6 +129,9 @@ where } } } + Ok(ReplaceTextureStatus::Conflict) => { + connection.send(&Package::Error(409, "Conflict".to_string()))?; + } Err(err) => { connection.send(&Package::from(err))?; }