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

214 lines
6.8 KiB
Rust
Raw Normal View History

2019-04-20 01:24:19 +02:00
// TODO: remove on implementation
#![allow(unused_imports)]
#![allow(unused_variables)]
#![allow(dead_code)]
use crate::model::*;
use std::collections::HashMap;
2019-04-30 19:08:40 +02:00
use std::fs;
2019-04-20 01:24:19 +02:00
use std::io;
use std::path::{Path, PathBuf};
use std::sync::Arc;
2019-05-04 18:35:49 +02:00
use sha2::{self, Digest};
2019-04-20 01:24:19 +02:00
pub use self::search::Query;
2019-04-30 19:08:40 +02:00
mod collection_file;
2019-04-20 01:24:19 +02:00
mod image_convert;
2019-04-24 17:01:43 +02:00
mod search;
2019-04-20 01:24:19 +02:00
2019-04-24 17:01:43 +02:00
pub type TextureFileResult = Result<Arc<Vec<u8>>, TextureFileError>;
2019-04-20 01:24:19 +02:00
pub enum TextureFileError {
NotFound,
IoError(io::Error),
ImageError(::image::ImageError),
}
pub struct DataStore {
// private attributes
// may change
data_dir: PathBuf,
texture: Vec<Texture>,
preview_cache: HashMap<(TextureFormat, Sha256), Arc<Vec<u8>>>,
}
2019-05-04 18:35:49 +02:00
fn sha256(data: &[u8]) -> Sha256 {
let mut hasher = sha2::Sha256::new();
hasher.input(data);
let hash_result = hasher.result();
let hash_slice = hash_result.as_slice();
let mut hash_arr = [0u8; 32];
for i in 0..32 {
hash_arr[i] = hash_slice[i];
}
Sha256(hash_arr)
}
2019-04-20 01:24:19 +02:00
impl DataStore {
pub fn new(path: &Path) -> io::Result<DataStore> {
2019-04-30 19:08:40 +02:00
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(),
})
2019-04-20 01:24:19 +02:00
}
pub fn garbage_collect(&mut self) -> io::Result<()> {
unimplemented!()
}
pub fn query(&mut self, query: &self::search::Query) -> Vec<Texture> {
unimplemented!();
// calls self::search::search(... )
}
/// returns true if successful
pub fn delete(&mut self, tex: &Texture) -> bool {
2019-04-30 19:08:40 +02:00
// 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 mut key = (TextureFormat::PNG, removed.texture_hash);
self.preview_cache.remove(&key); // Delete png preview
key.0 = TextureFormat::JPEG;
self.preview_cache.remove(&key); // Delete jpeg preview
2019-04-30 19:08:40 +02:00
true
}
// Texture not found
None => false,
}
2019-04-20 01:24:19 +02:00
}
2019-05-03 11:19:34 +02:00
/// * `data` The content of the texture file, if available.
pub fn insert(
&mut self,
tex: Texture,
data: Option<Arc<Vec<u8>>>,
) -> ProtocolResult<ReplaceTextureStatus> {
2019-05-03 11:19:34 +02:00
use io::Write;
2019-04-30 19:08:40 +02:00
// Check for collisions
2019-05-03 11:19:34 +02:00
if self
2019-04-30 19:08:40 +02:00
.texture
.iter()
2019-05-03 11:19:34 +02:00
.find(|e| e.id == tex.id || e.name == tex.name)
.is_some()
{
// Name or id already in use
Err(ProtocolError::Conflict(
"Name or id is already in use.".to_string(),
))
2019-05-03 11:19:34 +02:00
} else {
2019-04-30 19:08:40 +02:00
// Insert it
2019-05-03 11:19:34 +02:00
if self.has_hash(&tex.texture_hash)? {
2019-04-30 19:08:40 +02:00
self.texture.push(tex);
2019-05-03 11:19:34 +02:00
Ok(ReplaceTextureStatus::Ok)
} else {
match data {
None => Ok(ReplaceTextureStatus::NeedTextureData(tex.texture_hash)),
Some(blob) => {
2019-05-04 18:35:49 +02:00
if sha256(&blob) != tex.texture_hash {
return Err(ProtocolError::BadRequest(
"The texture does not have the given hash value.".to_string(),
));
}
2019-05-03 11:19:34 +02:00
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)
}
}
2019-04-30 19:08:40 +02:00
}
}
2019-04-20 01:24:19 +02:00
}
pub fn by_name<'a>(&'a self, name: &str) -> Option<&'a Texture> {
2019-05-03 11:19:34 +02:00
self.texture.iter().find(|e| e.name == name)
2019-04-20 01:24:19 +02:00
}
pub fn by_id<'a, 'b>(&'a self, id: &'b str) -> Option<&'a Texture> {
2019-05-03 11:19:34 +02:00
self.texture.iter().find(|e| e.id == id)
2019-04-20 01:24:19 +02:00
}
2019-05-03 11:19:34 +02:00
/// Check if the texture, given by hash, physically exists on the file system.
pub fn has_hash(&self, hash: &Sha256) -> io::Result<bool> {
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)
}
}
}
2019-04-20 01:24:19 +02:00
}
pub fn get_texture_file(&mut self, hash: &Sha256) -> TextureFileResult {
2019-04-30 19:08:40 +02:00
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))
2019-04-20 01:24:19 +02:00
}
pub fn get_texture_preview(&mut self, hash: &Sha256) -> TextureFileResult {
unimplemented!();
}
2019-05-04 18:35:49 +02:00
fn store_metadata(&self) -> io::Result<()> {
let mut tmp_path = self.data_dir.clone();
tmp_path.push("collection_new.json");
collection_file::store_collection_file(&tmp_path, &self.texture)?;
let mut final_path = self.data_dir.clone();
final_path.push("collection.json");
fs::rename(tmp_path, final_path)
}
2019-04-20 01:24:19 +02:00
}