Implement some persistency features
This commit is contained in:
parent
450ecbccad
commit
39b2a2c442
@ -1,20 +1,29 @@
|
|||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
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]);
|
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<S>(hash: &[u8; 32], serializer: S) -> Result<S::Ok, S::Error>
|
fn as_hex<S>(hash: &[u8; 32], serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
use std::fmt::*;
|
serializer.serialize_str(&hash_to_hex_string(hash))
|
||||||
|
|
||||||
let mut hex_string = String::new();
|
|
||||||
for digit in hash {
|
|
||||||
write!(hex_string, "{:02X}", digit).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
serializer.serialize_str(&hex_string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_hex<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error>
|
fn from_hex<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
|
#[derive(Clone, Debug, Deserialize, Serialize, Eq, Hash, PartialEq)]
|
||||||
pub enum TextureFormat {
|
pub enum TextureFormat {
|
||||||
#[serde(rename = "png")]
|
#[serde(rename = "png")]
|
||||||
PNG,
|
PNG,
|
||||||
|
@ -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<Texture>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_collection_file(path: &Path) -> io::Result<Vec<Texture>> {
|
||||||
|
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)
|
||||||
|
}
|
@ -6,11 +6,13 @@
|
|||||||
use crate::model::*;
|
use crate::model::*;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub use self::search::Query;
|
pub use self::search::Query;
|
||||||
|
mod collection_file;
|
||||||
mod image_convert;
|
mod image_convert;
|
||||||
mod search;
|
mod search;
|
||||||
|
|
||||||
@ -31,7 +33,15 @@ pub struct DataStore {
|
|||||||
|
|
||||||
impl DataStore {
|
impl DataStore {
|
||||||
pub fn new(path: &Path) -> io::Result<DataStore> {
|
pub fn new(path: &Path) -> io::Result<DataStore> {
|
||||||
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<()> {
|
pub fn garbage_collect(&mut self) -> io::Result<()> {
|
||||||
@ -45,27 +55,86 @@ impl DataStore {
|
|||||||
|
|
||||||
/// returns true if successful
|
/// returns true if successful
|
||||||
pub fn delete(&mut self, tex: &Texture) -> bool {
|
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 {
|
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> {
|
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> {
|
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 {
|
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 {
|
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 {
|
pub fn get_texture_preview(&mut self, hash: &Sha256) -> TextureFileResult {
|
||||||
|
Loading…
Reference in New Issue
Block a user