use std::collections::hash_map::DefaultHasher; use std::fs::File; use std::hash::{Hash, Hasher}; use std::io::{BufReader, Read, Write}; use std::path::Path; use std::sync::Arc; use std::sync::Mutex; use std::thread; use std::time::Duration; fn read_from_file(database_name: &'static str) -> Vec { let master_path = format!(".{}{}.{}", "/SixthDatabase/", database_name, "6db"); let backup_path = format!(".{}{}.{}", "/SixthDatabase/", database_name, "6db.bak"); let (master_file, backup_file) = (Path::new(&master_path), Path::new(&backup_path)); if !master_file.exists() && !backup_file.exists() { return vec![]; } { if master_file.exists() { let file = File::open(master_file).expect("Could not open master file for 6DB"); let mut line: Vec = vec![]; let result_of_bufreader = BufReader::new(file).read_to_end(&mut line); if result_of_bufreader.is_ok() && !line.is_empty() { let result_of_bincode = bincode::deserialize(&line); if result_of_bincode.is_ok() { return result_of_bincode.unwrap(); } } } } if backup_file.exists() { let file = File::open(backup_file).expect("Could not open backup file for 6DB"); let mut line: Vec = vec![]; let result_of_bufreader = BufReader::new(file).read_to_end(&mut line); if result_of_bufreader.is_ok() && !line.is_empty() { let result_of_bincode = bincode::deserialize(&line); if result_of_bincode.is_ok() { return result_of_bincode.unwrap(); } } } // both master and backup need to be corrupt to get here, or the data within your database changed in a way that serde dislikes panic!("Both master and backup database files are corrupt. To prevent erasure of data, delete your files manually if you intended this to happen!"); } fn write_to_file(data: &T, database_name: &'static str) where T: serde::ser::Serialize, { let master_path = format!(".{}{}.{}", "/SixthDatabase/", database_name, "6db"); let backup_path = format!(".{}{}.{}", "/SixthDatabase/", database_name, "6db.bak"); let (master_file, backup_file) = (Path::new(&master_path), Path::new(&backup_path)); if !master_file.exists() { if !std::fs::read_dir("./SixthDatabase/").is_ok() { std::fs::create_dir("./SixthDatabase/") .expect("Could not create SixthDatabase directory"); } } let serialized = bincode::serialize(&data).expect("serialization failed"); { let mut f = File::create(master_file).expect("Error opening file"); f.write_all(&serialized) .expect("Could not write serialized data to master_file"); } { let mut f = File::create(backup_file).expect("Error opening file"); f.write_all(&serialized) .expect("Could not write serialized data to backup_file"); } } fn hashme(obj: &T) -> u64 where T: Hash, { let mut hasher = DefaultHasher::new(); obj.hash(&mut hasher); return hasher.finish(); } pub struct Database { pub database_name: &'static str, pub data: Vec, old_hash: u64, thread: Option>, shutdown: bool, } impl Database where A: std::marker::Send, A: serde::de::DeserializeOwned, A: serde::ser::Serialize, A: std::hash::Hash, { pub fn new(db_name: &'static str) -> Arc>> { let from_disk = read_from_file(db_name); let hashed = hashme(&from_disk); let object = Arc::new(Mutex::new(Database { database_name: db_name, data: from_disk, old_hash: hashed, thread: None, shutdown: false, })); let thread_transfer = object.clone(); object.clone().lock().unwrap().thread = Some(thread::spawn(move || { let database_name_for_debug = db_name; let reference = thread_transfer; let mut missed_checks = 0; loop { if missed_checks == 3 { println!("A deadlock situation is detected in 6DB: {} please read the README for information on fixes\n This requires action on your part to fix.", database_name_for_debug); } let lock = reference.lock(); if lock.is_err() { missed_checks += 1; thread::sleep(Duration::from_secs(15)); continue; } else { missed_checks = 0; } let mut lock1 = lock.unwrap(); let current_hash = hashme(&lock1.data); if current_hash != lock1.old_hash { lock1.old_hash = current_hash; write_to_file(&lock1.data, lock1.database_name); } if lock1.shutdown { std::mem::drop(lock1); return; } std::mem::drop(lock1); thread::sleep(Duration::from_secs(15)); } })); return object.clone(); } pub fn drop(mut self) { self.shutdown = true; std::mem::drop(self.thread); } }