dodo 0.3.1

Basic persistence library designed to be a quick and easy way to create a persistent storage.
Documentation
use uuid::Uuid;

use dodo::storage::{LockedDirectory, Storage};

/*
 * ==================================================================
 * ========================> PLEASE READ!!! <========================
 * ==================================================================
 *
 * LockedDirectory is a wrapper around the Directory structure.
 * We only test the added lock feature.
 */

#[test]
fn should_lock_new_entry() {
    let folder = fixture::create_folder();
    let mut storage = LockedDirectory::new(&folder).expect("could not create storage");

    let id = {
        let (id, _write) = storage.new().expect("could not create new entry");
        fixture::assert_entry_locked_exclusive(&folder, id);
        id
    };

    fixture::assert_entry_unlocked(&folder, id);
}

#[test]
fn should_lock_while_reading_entry() {
    let folder = fixture::create_folder();
    let id = Uuid::parse_str("78190929-3d84-4735-9e40-80e3cd5530e9").unwrap();
    fixture::create_entry_in(&folder, id, "content");
    let storage = LockedDirectory::new(&folder).expect("could not create storage");

    {
        let _read = storage.read(id).expect("could not read entry");
        fixture::assert_entry_locked_shared(&folder, id);
    };

    fixture::assert_entry_unlocked(&folder, id);
}

#[test]
fn should_lock_while_writing_entry() {
    let folder = fixture::create_folder();
    let id = Uuid::parse_str("78190929-3d84-4735-9e40-80e3cd5530e9").unwrap();
    let mut storage = LockedDirectory::new(&folder).expect("could not create storage");

    {
        let _write = storage.write(id).expect("could not write to storage");
        fixture::assert_entry_locked_exclusive(&folder, id);
    };

    fixture::assert_entry_unlocked(&folder, id);
}

#[test]
fn should_lock_while_overwriting_entry() {
    let folder = fixture::create_folder();
    let id = Uuid::parse_str("78190929-3d84-4735-9e40-80e3cd5530e9").unwrap();
    fixture::create_entry_in(&folder, id, "content");
    let mut storage = LockedDirectory::new(&folder).expect("could not create storage");

    {
        let _write = storage.overwrite(id).expect("could not write to storage");
        fixture::assert_entry_locked_exclusive(&folder, id);
    };

    fixture::assert_entry_unlocked(&folder, id);
}

mod fixture {
    use std::{fs::{self, File}, path::{Path, PathBuf}};

    use fs2::{self, FileExt};
    use uuid::Uuid;

    pub fn create_folder() -> tempfile::TempDir {
        tempfile::tempdir().expect("could not create folder")
    }

    pub fn create_file_in<P: AsRef<Path>>(folder: P, name: &str, content: &str) -> PathBuf {
        let path = folder.as_ref().join(name);
        fs::write(&path, content).expect("could not create file");
        path
    }

    pub fn create_entry_in<P: AsRef<Path>>(folder: P, id: Uuid, content: &str) {
        create_file_in(folder, &id.to_string(), content);
    }

    pub fn assert_entry_locked_shared<P: AsRef<Path>>(folder: P, id: Uuid) {
        let path = folder.as_ref().join(id.to_string());
        let file = File::open(path).expect("entry does not exist");
        assert!(file.try_lock_shared().is_ok(), "entry should be lockable");
        assert_eq!(file.try_lock_exclusive().err().expect("entry should have been locked").kind(),
                   fs2::lock_contended_error().kind(),
                   "entry should have been locked");
    }

    pub fn assert_entry_locked_exclusive<P: AsRef<Path>>(folder: P, id: Uuid) {
        let path = folder.as_ref().join(id.to_string());
        let file = File::open(path).expect("entry does not exist");
        assert_eq!(file.try_lock_exclusive().err().expect("entry should have been locked").kind(),
                   fs2::lock_contended_error().kind(),
                   "entry should have been locked");
    }

    pub fn assert_entry_unlocked<P: AsRef<Path>>(folder: P, id: Uuid) {
        let path = folder.as_ref().join(id.to_string());
        let file = File::open(path).expect("entry does not exist");
        assert!(file.try_lock_exclusive().is_ok(), "entry should have been unlocked");
    }
}