Implement some persistency features
This commit is contained in:
		@ -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<S>(hash: &[u8; 32], serializer: S) -> Result<S::Ok, S::Error>
 | 
			
		||||
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>
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
 | 
			
		||||
@ -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 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<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<()> {
 | 
			
		||||
@ -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 {
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user