1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
use failure::Fallible;
use std::collections::HashMap;
use uuid::Uuid;

#[cfg(test)]
mod inmemory;
mod kv;
mod operation;

pub use self::kv::KVStorage;
#[cfg(test)]
pub use inmemory::InMemoryStorage;

pub use operation::Operation;

/// An in-memory representation of a task as a simple hashmap
pub type TaskMap = HashMap<String, String>;

#[cfg(test)]
fn taskmap_with(mut properties: Vec<(String, String)>) -> TaskMap {
    let mut rv = TaskMap::new();
    for (p, v) in properties.drain(..) {
        rv.insert(p, v);
    }
    rv
}

/// The type of VersionIds
pub use crate::server::VersionId;

/// The default for base_version.
pub(crate) const DEFAULT_BASE_VERSION: Uuid = crate::server::NO_VERSION_ID;

/// A TaskStorage transaction, in which storage operations are performed.
///
/// # Concurrency
///
/// Serializable consistency must be maintained.  Concurrent access is unusual
/// and some implementations may simply apply a mutex to limit access to
/// one transaction at a time.
///
/// # Commiting and Aborting
///
/// A transaction is not visible to other readers until it is committed with
/// [`crate::taskstorage::TaskStorageTxn::commit`].  Transactions are aborted if they are dropped.
/// It is safe and performant to drop transactions that did not modify any data without committing.
pub trait TaskStorageTxn {
    /// Get an (immutable) task, if it is in the storage
    fn get_task(&mut self, uuid: &Uuid) -> Fallible<Option<TaskMap>>;

    /// Create an (empty) task, only if it does not already exist.  Returns true if
    /// the task was created (did not already exist).
    fn create_task(&mut self, uuid: Uuid) -> Fallible<bool>;

    /// Set a task, overwriting any existing task.  If the task does not exist, this implicitly
    /// creates it (use `get_task` to check first, if necessary).
    fn set_task(&mut self, uuid: Uuid, task: TaskMap) -> Fallible<()>;

    /// Delete a task, if it exists.  Returns true if the task was deleted (already existed)
    fn delete_task(&mut self, uuid: &Uuid) -> Fallible<bool>;

    /// Get the uuids and bodies of all tasks in the storage, in undefined order.
    fn all_tasks(&mut self) -> Fallible<Vec<(Uuid, TaskMap)>>;

    /// Get the uuids of all tasks in the storage, in undefined order.
    fn all_task_uuids(&mut self) -> Fallible<Vec<Uuid>>;

    /// Get the current base_version for this storage -- the last version synced from the server.
    fn base_version(&mut self) -> Fallible<VersionId>;

    /// Set the current base_version for this storage.
    fn set_base_version(&mut self, version: VersionId) -> Fallible<()>;

    /// Get the current set of outstanding operations (operations that have not been sync'd to the
    /// server yet)
    fn operations(&mut self) -> Fallible<Vec<Operation>>;

    /// Add an operation to the end of the list of operations in the storage.  Note that this
    /// merely *stores* the operation; it is up to the TaskDB to apply it.
    fn add_operation(&mut self, op: Operation) -> Fallible<()>;

    /// Replace the current list of operations with a new list.
    fn set_operations(&mut self, ops: Vec<Operation>) -> Fallible<()>;

    /// Get the entire working set, with each task UUID at its appropriate (1-based) index.
    /// Element 0 is always None.
    fn get_working_set(&mut self) -> Fallible<Vec<Option<Uuid>>>;

    /// Add a task to the working set and return its (one-based) index.  This index will be one greater
    /// than the highest used index.
    fn add_to_working_set(&mut self, uuid: &Uuid) -> Fallible<usize>;

    /// Clear all tasks from the working set in preparation for a garbage-collection operation.
    /// Note that this is the only way items are removed from the set.
    fn clear_working_set(&mut self) -> Fallible<()>;

    /// Commit any changes made in the transaction.  It is an error to call this more than
    /// once.
    fn commit(&mut self) -> Fallible<()>;
}

/// A trait for objects able to act as task storage.  Most of the interesting behavior is in the
/// [`crate::taskstorage::TaskStorageTxn`] trait.
pub trait TaskStorage {
    /// Begin a transaction
    fn txn<'a>(&'a mut self) -> Fallible<Box<dyn TaskStorageTxn + 'a>>;
}