sixth_database 1.0.10

In memory serializing to disk Database. Mimics FourthDatabase(C#) and FifthDatabase(C++), SixthDatabase is for Rust using Serde.
Documentation
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<A: serde::de::DeserializeOwned>(database_name: &'static str) -> Vec<A> {
    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<u8> = 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<u8> = 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<T>(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<T>(obj: &T) -> u64
where
    T: Hash,
{
    let mut hasher = DefaultHasher::new();
    obj.hash(&mut hasher);
    return hasher.finish();
}

pub struct Database<A> {
    pub database_name: &'static str,
    pub data: Vec<A>,
    old_hash: u64,
    thread: Option<thread::JoinHandle<()>>,
    shutdown: bool,
}

impl<A: 'static> Database<A>
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<Mutex<Database<A>>> {
        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);
    }
}