add a Replica

This commit is contained in:
Dustin J. Mitchell 2020-01-01 18:22:01 -05:00
parent f6ffcc7039
commit b898ec1fde
3 changed files with 103 additions and 3 deletions

View file

@ -3,6 +3,7 @@
mod errors;
mod operation;
mod replica;
mod server;
mod taskdb;

99
src/replica.rs Normal file
View file

@ -0,0 +1,99 @@
use crate::errors::Error;
use crate::operation::Operation;
use crate::taskdb::DB;
use chrono::Utc;
use std::collections::HashMap;
use uuid::Uuid;
/// A replica represents an instance of a user's task data.
struct Replica {
taskdb: Box<DB>,
}
impl Replica {
pub fn new(taskdb: Box<DB>) -> Replica {
return Replica { taskdb };
}
/// Create a new task. The task must not already exist.
pub fn create_task(&mut self, uuid: Uuid) -> Result<(), Error> {
self.taskdb.apply(Operation::Create { uuid })
}
/// Delete a task. The task must exist.
pub fn delete_task(&mut self, uuid: Uuid) -> Result<(), Error> {
self.taskdb.apply(Operation::Delete { uuid })
}
/// Update an existing task. If the value is Some, the property is added or updated. If the
/// value is None, the property is deleted. It is not an error to delete a nonexistent
/// property.
pub fn update_task<S1, S2>(
&mut self,
uuid: Uuid,
property: S1,
value: Option<S2>,
) -> Result<(), Error>
where
S1: Into<String>,
S2: Into<String>,
{
self.taskdb.apply(Operation::Update {
uuid,
property: property.into(),
value: value.map(|v| v.into()),
timestamp: Utc::now(),
})
}
/// Get an existing task by its UUID
pub fn get_task(&self, uuid: &Uuid) -> Option<&HashMap<String, String>> {
self.taskdb.tasks().get(&uuid)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::taskdb::DB;
use uuid::Uuid;
#[test]
fn create() {
let mut rep = Replica::new(DB::new().into());
let uuid = Uuid::new_v4();
rep.create_task(uuid.clone()).unwrap();
assert_eq!(rep.get_task(&uuid), Some(&HashMap::new()));
}
#[test]
fn delete() {
let mut rep = Replica::new(DB::new().into());
let uuid = Uuid::new_v4();
rep.create_task(uuid.clone()).unwrap();
rep.delete_task(uuid.clone()).unwrap();
assert_eq!(rep.get_task(&uuid), None);
}
#[test]
fn update() {
let mut rep = Replica::new(DB::new().into());
let uuid = Uuid::new_v4();
rep.create_task(uuid.clone()).unwrap();
rep.update_task(uuid.clone(), "title", Some("snarsblat"))
.unwrap();
let mut task = HashMap::new();
task.insert("title".into(), "snarsblat".into());
assert_eq!(rep.get_task(&uuid), Some(&task));
}
#[test]
fn get_does_not_exist() {
let rep = Replica::new(DB::new().into());
let uuid = Uuid::new_v4();
assert_eq!(rep.get_task(&uuid), None);
}
}

View file

@ -39,9 +39,9 @@ impl DB {
}
}
/// Apply an operation to the DB. Aside from synchronization operations, this
/// is the only way to modify the DB. In cases where an operation does not
/// make sense, this function will ignore the operation.
/// Apply an operation to the DB. Aside from synchronization operations, this is the only way
/// to modify the DB. In cases where an operation does not make sense, this function will do
/// nothing and return an error (but leave the DB in a consistent state).
pub fn apply(&mut self, op: Operation) -> Result<(), Error> {
if let err @ Err(_) = self.apply_op(&op) {
return err;