dodo 0.3.1

Basic persistence library designed to be a quick and easy way to create a persistent storage.
Documentation
use std::io::{Read, Write};

use uuid::Uuid;

use dodo::storage::{Memory, Result, Storage};

#[test]
fn can_create_memory_storage() {
    Memory::new();
}

#[test]
fn can_create_new_entry() {
    let mut storage = Memory::new();

    let id = {
        let (id, mut write) = storage.new().expect("could not create new entry");
        write!(&mut write, "{}", "content").expect("could not write to new entry");
        id
    };

    fixture::assert_entry_equals(&storage, id, "content");
}

#[test]
fn new_entry_created_when_writer_is_dropped() {
    let mut storage = Memory::new();

    let id = {
        let (id, mut write) = storage.new().expect("could not create new entry");
        write!(&mut write, "{}", "content").expect("could not write to new entry");
        fixture::assert_entry_not_exists(&storage, id);
        id
    };

    fixture::assert_entry_equals(&storage, id, "content");
}

#[test]
fn can_read_existing_entry() {
    let mut storage = Memory::new();
    let id = fixture::create_entry(&mut storage, "content");

    let bytes = {
        let mut read = storage.read(id).expect("could not read storage");
        let mut buffer = String::new();
        read.read_to_string(&mut buffer).expect("could not read entry");
        buffer
    };

    assert_eq!(bytes, "content", "entry was read incorrectly");
}

#[test]
fn cant_read_absent_entry() {
    let storage = Memory::new();

    let entry = storage.read(Uuid::parse_str("78190929-3d84-4735-9e40-80e3cd5530e9").unwrap());

    assert!(entry.expect_err("entry should not exist").is_not_found(), "entry should not have been readable");
}

#[test]
fn can_write_to_absent_entry() {
    let mut storage = Memory::new();
    let id = Uuid::parse_str("78190929-3d84-4735-9e40-80e3cd5530e9").unwrap();

    {
        let mut write = storage.write(id).expect("could not write to storage");
        write!(&mut write, "{}", "content").expect("could not write entry");
    };

    fixture::assert_entry_equals(&storage, id, "content");
}

#[test]
fn can_write_to_existing_entry() {
    let mut storage = Memory::new();
    let id = fixture::create_entry(&mut storage, "content");

    {
        let mut write = storage.write(id).expect("could not write to storage");
        write!(&mut write, "{}", "new content").expect("could not write entry");
    };

    fixture::assert_entry_equals(&storage, id, "new content");
}

#[test]
fn writen_entry_created_when_writer_is_dropped() {
    let mut storage = Memory::new();
    let id = Uuid::parse_str("78190929-3d84-4735-9e40-80e3cd5530e9").unwrap();

    {
        let mut write = storage.write(id).expect("could not write to storage");
        write!(&mut write, "{}", "content").expect("could not write entry");

        fixture::assert_entry_not_exists(&storage, id);
    };

    fixture::assert_entry_equals(&storage, id, "content");
}

#[test]
fn should_not_modify_entry_while_writing() {
    let mut storage = Memory::new();
    let id = fixture::create_entry(&mut storage, "content");

    let mut read = storage.read(id).expect("entry should have been readable");
    {
        let mut write = storage.write(id).expect("could not write to storage");
        write!(&mut write, "{}", "new content").expect("could not write entry");
    }
    let mut bytes = String::new();
    read.read_to_string(&mut bytes).expect("could not read entry");

    assert_eq!(bytes, "content", "entry was read incorrectly");
}

#[test]
fn can_overwrite_existing_entry() {
    let mut storage = Memory::new();
    let id = fixture::create_entry(&mut storage, "content");

    {
        let mut write = storage.overwrite(id).expect("could not write to storage");
        write!(&mut write, "{}", "new content").expect("could not write entry");
    };

    fixture::assert_entry_equals(&storage, id, "new content");
}

#[test]
fn cant_overwrite_absent_entry() {
    let mut storage = Memory::new();
    let id = Uuid::parse_str("78190929-3d84-4735-9e40-80e3cd5530e9").unwrap();

    let entry = storage.overwrite(id);

    assert!(entry.is_err(), "entry should not have been writen");
}

#[test]
fn should_not_modify_entry_while_overwriting() {
    let mut storage = Memory::new();
    let id = fixture::create_entry(&mut storage, "content");

    let mut read = storage.read(id).expect("could not read storage");
    {
        let mut write = storage.overwrite(id).expect("could not write to storage");
        write!(&mut write, "{}", "new content").expect("could not write entry");
    }
    let mut bytes = String::new();
    read.read_to_string(&mut bytes).expect("could not read entry");

    assert_eq!(bytes, "content", "entry was read incorrectly");
}

#[test]
fn can_delete_existing_entry() {
    let mut storage = Memory::new();
    let id = fixture::create_entry(&mut storage, "content");

    let deleted = storage.delete(id).expect("could not delete from storage");

    assert!(deleted, "something should have been deleted");
    fixture::assert_entry_not_exists(&storage, id);
}

#[test]
fn should_not_modify_entry_while_deleting() {
    let mut storage = Memory::new();
    let id = fixture::create_entry(&mut storage, "content");

    let mut read = storage.read(id).expect("could not read storage");
    storage.delete(id).expect("could not delete from storage");
    let mut bytes = String::new();
    read.read_to_string(&mut bytes).expect("could not read entry");

    assert_eq!(bytes, "content", "entry was read incorrectly");
}

#[test]
fn can_delete_absent_entry() {
    let mut storage = Memory::new();
    let id = Uuid::parse_str("78190929-3d84-4735-9e40-80e3cd5530e9").unwrap();

    let deleted = storage.delete(id).expect("could not delete from storage");

    assert!(!deleted, "nothing should have been deleted");
    fixture::assert_entry_not_exists(&storage, id);
}

#[test]
fn can_clear_storage() {
    let mut storage = Memory::new();
    let id1 = fixture::create_entry(&mut storage, "content");
    let id2 = fixture::create_entry(&mut storage, "content");

    storage.clear().expect("could not clear storage");

    fixture::assert_entry_not_exists(&storage, id1);
    fixture::assert_entry_not_exists(&storage, id2);
}

#[test]
fn can_clear_empty_storage() {
    let mut storage = Memory::new();

    storage.clear().expect("could not clear storage");
}

#[test]
fn can_list_existing_entries() {
    let mut storage = Memory::new();
    let id1 = fixture::create_entry(&mut storage, "content");
    let id2 = fixture::create_entry(&mut storage, "content");

    let entries: Vec<Uuid> = storage
        .iter().expect("could not read storage")
        .collect::<Result<Vec<Uuid>>>().expect("could not read storage");

    assert!(entries.contains(&id1), "entry 1 should exist");
    assert!(entries.contains(&id2), "entry 2 should exist.");
}

mod fixture {
    use std::io::{Read, Write};

    use uuid::Uuid;

    use dodo::{Memory, Storage};

    pub fn create_entry(storage: &mut Memory, content: &str) -> Uuid {
        let (id, mut write) = storage.new().expect("could not write to storage");
        write!(&mut write, "{}", content).expect("could not write entry");
        id
    }

    pub fn assert_entry_equals(storage: &Memory, id: Uuid, expected_content: &str) {
        let mut reader = storage.read(id).expect("entry does not exist");
        let mut actual_content = String::new();
        reader.read_to_string(&mut actual_content).expect("entry could not be read");
        assert_eq!(expected_content, actual_content, "entry has the wrong contents");
    }

    pub fn assert_entry_not_exists(storage: &Memory, id: Uuid) {
        assert!(storage.read(id).expect_err("entry should not exist").is_not_found(), "entry should not exist");
    }
}