2019-04-20 01:24:19 +02:00
use crate ::model ::* ;
2019-05-05 19:16:52 +02:00
use std ::collections ::* ;
2019-04-20 01:24:19 +02:00
use std ::io ;
use std ::path ::{ Path , PathBuf } ;
mod image_convert ;
2019-05-05 19:16:52 +02:00
mod metadata_file ;
2019-04-20 01:24:19 +02:00
2019-05-05 20:28:35 +02:00
pub type TextureFileResult = Result < Vec < u8 > , TextureFileError > ;
2019-04-20 01:24:19 +02:00
pub enum TextureFileError {
NotFound ,
IoError ( io ::Error ) ,
ImageError ( ::image ::ImageError ) ,
}
2019-05-05 19:16:52 +02:00
impl From < io ::Error > for TextureFileError {
fn from ( err : io ::Error ) -> Self {
TextureFileError ::IoError ( err )
}
2019-04-20 01:24:19 +02:00
}
2019-05-05 19:16:52 +02:00
impl From < ::image ::ImageError > for TextureFileError {
fn from ( err : ::image ::ImageError ) -> Self {
TextureFileError ::ImageError ( err )
2019-05-04 18:35:49 +02:00
}
2019-05-05 19:16:52 +02:00
}
pub struct DataStore {
//data_path: PathBuf,
base_dir : PathBuf ,
textures : HashSet < Texture > ,
id_index : HashMap < String , Texture > ,
name_index : HashMap < String , Texture > ,
2019-05-07 17:46:17 +02:00
_preview_cache : HashMap < ( TextureFormat , Sha256 ) , Vec < u8 > > ,
2019-05-04 18:35:49 +02:00
}
2019-04-20 01:24:19 +02:00
impl DataStore {
2019-05-05 19:54:26 +02:00
fn texture_base_path ( & self ) -> PathBuf {
2019-05-05 19:16:52 +02:00
let mut path = self . base_dir . clone ( ) ;
path . push ( " textures " ) ;
2019-05-05 19:54:26 +02:00
path
}
fn texture_file_path ( & self , sha : & Sha256 ) -> PathBuf {
let mut path = self . texture_base_path ( ) ;
2019-05-05 19:16:52 +02:00
path . push ( sha . as_hex_string ( ) ) ;
path
}
2019-04-30 19:08:40 +02:00
2019-05-05 19:16:52 +02:00
fn index_file_path ( & self ) -> PathBuf {
let mut path = self . base_dir . clone ( ) ;
path . push ( " collection.json " ) ;
path
2019-04-20 01:24:19 +02:00
}
2019-05-05 19:16:52 +02:00
pub fn new ( base_dir : & Path ) -> io ::Result < DataStore > {
let mut store = DataStore {
base_dir : base_dir . into ( ) ,
textures : Default ::default ( ) ,
id_index : Default ::default ( ) ,
name_index : Default ::default ( ) ,
2019-05-07 17:46:17 +02:00
_preview_cache : Default ::default ( ) ,
2019-05-05 19:16:52 +02:00
} ;
2019-05-08 15:49:00 +02:00
let metadata_file = metadata_file ::MetadataFile ::load ( & store . index_file_path ( ) ) ? ;
2019-05-05 19:16:52 +02:00
for texture in metadata_file . textures . iter ( ) {
2019-05-05 20:28:35 +02:00
match store . insert ( texture . clone ( ) ) {
2019-05-05 19:16:52 +02:00
true = > ( ) ,
false = > {
2019-05-05 19:39:08 +02:00
panic! ( " inserting {:#?} failed !!! " , texture ) ; // TODO: What should be done?
2019-05-05 19:16:52 +02:00
}
}
}
2019-05-07 22:43:14 +02:00
store . garbage_collect ( ) ? ;
store . flush_metadata ( ) ? ;
2019-05-05 19:16:52 +02:00
Ok ( store )
2019-04-20 01:24:19 +02:00
}
2019-05-05 19:16:52 +02:00
pub fn texture_by_id ( & self , id : & str ) -> Option < Texture > {
self . id_index . get ( id ) . cloned ( )
2019-04-20 01:24:19 +02:00
}
2019-05-05 19:16:52 +02:00
pub fn texture_by_name ( & self , id : & str ) -> Option < Texture > {
self . name_index . get ( id ) . cloned ( )
}
2019-05-05 20:28:35 +02:00
pub fn insert ( & mut self , texture : Texture ) -> bool {
2019-05-05 19:16:52 +02:00
if self . id_index . contains_key ( & texture . id ) {
2019-05-05 20:28:35 +02:00
return false ;
2019-05-05 19:16:52 +02:00
}
if self . name_index . contains_key ( & texture . name ) {
2019-05-05 20:28:35 +02:00
return false ;
2019-04-30 19:08:40 +02:00
}
2019-05-05 19:16:52 +02:00
2019-05-05 20:28:35 +02:00
if ! self . is_texture_file_on_disk ( & texture . texture_hash ) {
return false ;
2019-05-05 19:16:52 +02:00
}
self . id_index . insert ( texture . id . clone ( ) , texture . clone ( ) ) ;
self . name_index
. insert ( texture . name . clone ( ) , texture . clone ( ) ) ;
self . textures . insert ( texture . clone ( ) ) ;
2019-05-05 20:28:35 +02:00
true
2019-04-20 01:24:19 +02:00
}
2019-05-05 19:16:52 +02:00
/// returns true if successful
pub fn delete ( & mut self , tex : & Texture ) -> bool {
if self . textures . remove ( tex ) {
// remove
assert! ( self . id_index . remove ( & tex . id ) . is_some ( ) ) ;
assert! ( self . name_index . remove ( & tex . name ) . is_some ( ) ) ;
// don't delete cache, since it could be used
// by other texture.
true
2019-05-03 11:19:34 +02:00
} else {
2019-05-05 19:16:52 +02:00
false
2019-04-30 19:08:40 +02:00
}
2019-04-20 01:24:19 +02:00
}
2019-05-05 19:16:52 +02:00
/// Check if the texture, given by hash, physically exists on the file system.
2019-05-05 20:28:35 +02:00
pub fn is_texture_file_on_disk ( & self , hash : & Sha256 ) -> bool {
2019-05-05 19:16:52 +02:00
let file_path = self . texture_file_path ( & hash ) ;
2019-04-20 01:24:19 +02:00
2019-05-05 20:28:35 +02:00
file_path . is_file ( )
2019-04-20 01:24:19 +02:00
}
2019-05-05 20:28:35 +02:00
pub fn read_texture_file_by_hash ( & self , hash : & Sha256 ) -> io ::Result < Vec < u8 > > {
2019-05-05 19:16:52 +02:00
use std ::fs ::* ;
use std ::io ::* ;
let file_path = self . texture_file_path ( & hash ) ;
2019-04-30 19:08:40 +02:00
2019-05-05 19:16:52 +02:00
let mut file = File ::open ( file_path ) ? ;
2019-04-30 19:08:40 +02:00
let mut buffer = Vec ::new ( ) ;
2019-05-05 19:16:52 +02:00
file . read_to_end ( & mut buffer ) ? ;
2019-05-05 20:28:35 +02:00
Ok ( buffer )
2019-04-20 01:24:19 +02:00
}
2019-05-05 19:16:52 +02:00
pub fn store_texture_file ( & mut self , data : & [ u8 ] ) -> io ::Result < ( ) > {
use std ::fs ;
use std ::io ::Write ;
let hash = crate ::model ::Sha256 ::from_data ( data ) ;
2019-05-05 19:54:26 +02:00
fs ::create_dir_all ( & self . texture_base_path ( ) ) ? ;
let file_path = self . texture_file_path ( & hash ) ;
2019-05-05 19:16:52 +02:00
let mut file = fs ::File ::create ( & file_path ) ? ;
file . write_all ( data )
}
2019-05-06 09:34:12 +02:00
pub fn flush_metadata ( & self ) -> io ::Result < ( ) > {
2019-05-05 19:16:52 +02:00
let f = metadata_file ::MetadataFile ::from_iterator ( self . textures . iter ( ) ) ;
f . store ( self . index_file_path ( ) . as_path ( ) )
}
2019-05-05 19:39:08 +02:00
pub fn get_texture_preview (
& mut self ,
2019-05-07 17:46:17 +02:00
_hash : & Sha256 ,
_desired_format : TextureFormat ,
2019-05-05 19:39:08 +02:00
) -> TextureFileResult {
2019-04-20 01:24:19 +02:00
unimplemented! ( ) ;
}
2019-05-04 18:35:49 +02:00
2019-05-07 22:43:14 +02:00
pub fn borrow_textures ( & self ) -> impl Iterator < Item = & Texture > {
self . textures . iter ( )
2019-05-05 19:16:52 +02:00
}
2019-05-08 16:34:55 +02:00
pub fn extract_hash ( filename : & std ::ffi ::OsStr ) -> Option < Sha256 > {
// directly return None for invalidly encoded file names
let str_name = filename . to_str ( ) ? ;
let hash = Sha256 ::from_hex ( str_name ) ? ;
// check back to ignore names with lowercase letters
if hash . as_hex_string ( ) = = str_name {
Some ( hash )
} else {
None
}
}
2019-05-07 22:43:14 +02:00
pub fn garbage_collect ( & mut self ) -> io ::Result < ( ) > {
2019-05-08 16:34:55 +02:00
let texture_dir = std ::fs ::read_dir ( self . texture_base_path ( ) ) ? ;
let mut hashs_on_disk = HashSet ::new ( ) ;
for result_direntry in texture_dir {
let texture_path = result_direntry ? . path ( ) ;
let filename = match texture_path . file_name ( ) {
Some ( name ) = > name ,
None = > continue ,
} ;
match Self ::extract_hash ( filename ) {
Some ( hash ) = > {
hashs_on_disk . insert ( hash ) ;
}
None = > ( ) , // ignore other files
} ;
}
let mut unused_files = hashs_on_disk ;
for texture in & self . textures {
unused_files . remove ( & texture . texture_hash ) ;
}
2019-05-07 22:43:14 +02:00
2019-05-08 16:34:55 +02:00
// remove what is still contained in the HashSet
for entry in unused_files {
let path = self . texture_file_path ( & entry ) ;
std ::fs ::remove_file ( path ) ? ;
}
2019-05-07 22:43:14 +02:00
Ok ( ( ) )
2019-05-04 18:35:49 +02:00
}
2019-04-20 01:24:19 +02:00
}