use crate::{storage::TaskMap, Operation, Operations};
use chrono::Utc;
use uuid::Uuid;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct TaskData {
uuid: Uuid,
taskmap: TaskMap,
}
impl TaskData {
pub(crate) fn new(uuid: Uuid, taskmap: TaskMap) -> Self {
Self { uuid, taskmap }
}
pub fn create(uuid: Uuid, operations: &mut Operations) -> Self {
operations.push(Operation::Create { uuid });
Self {
uuid,
taskmap: TaskMap::new(),
}
}
pub fn get_uuid(&self) -> Uuid {
self.uuid
}
pub(in crate::task) fn get_taskmap(&self) -> &TaskMap {
&self.taskmap
}
pub fn get(&self, property: impl AsRef<str>) -> Option<&str> {
self.taskmap.get(property.as_ref()).map(|v| v.as_str())
}
pub fn has(&self, property: impl AsRef<str>) -> bool {
self.taskmap.contains_key(property.as_ref())
}
pub fn properties(&self) -> impl Iterator<Item = &String> {
self.taskmap.keys()
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &String)> {
self.taskmap.iter()
}
pub fn update(
&mut self,
property: impl Into<String>,
value: Option<String>,
operations: &mut Operations,
) {
let property = property.into();
let old_value = self.taskmap.get(&property).cloned();
if let Some(value) = &value {
self.taskmap.insert(property.clone(), value.clone());
} else {
self.taskmap.remove(&property);
}
operations.push(Operation::Update {
uuid: self.uuid,
property,
old_value,
value,
timestamp: Utc::now(),
});
}
pub fn delete(&mut self, operations: &mut Operations) {
operations.push(Operation::Delete {
uuid: self.uuid,
old_task: std::mem::take(&mut self.taskmap),
});
}
}
#[cfg(test)]
mod test {
use super::*;
use chrono::DateTime;
use pretty_assertions::assert_eq;
const TEST_UUID: Uuid = Uuid::from_u128(1234);
fn make_ops(ops: &[Operation]) -> Operations {
let mut res = Operations::new();
for op in ops {
res.push(op.clone());
}
res
}
pub fn set_all_timestamps(ops: &mut Operations, set_to: DateTime<Utc>) {
for op in ops {
if let Operation::Update { timestamp, .. } = op {
*timestamp = set_to;
}
}
}
#[test]
fn create() {
let mut ops = Operations::new();
let t = TaskData::create(TEST_UUID, &mut ops);
assert_eq!(t.uuid, TEST_UUID);
assert_eq!(t.get_uuid(), TEST_UUID);
assert_eq!(t.taskmap, TaskMap::new());
assert_eq!(ops, make_ops(&[Operation::Create { uuid: TEST_UUID }]));
}
#[test]
fn get_uuid() {
let t = TaskData::new(TEST_UUID, TaskMap::new());
assert_eq!(t.get_uuid(), TEST_UUID);
}
#[test]
fn get() {
let t = TaskData::new(TEST_UUID, [("prop".to_string(), "val".to_string())].into());
assert_eq!(t.get("prop"), Some("val"));
assert_eq!(t.get("nosuch"), None)
}
#[test]
fn has() {
let t = TaskData::new(TEST_UUID, [("prop".to_string(), "val".to_string())].into());
assert!(t.has("prop"));
assert!(!t.has("nosuch"));
}
#[test]
fn properties() {
let t = TaskData::new(
TEST_UUID,
[
("prop1".to_string(), "val".to_string()),
("prop2".to_string(), "val".to_string()),
]
.into(),
);
let mut props: Vec<_> = t.properties().collect();
props.sort();
assert_eq!(props, vec!["prop1", "prop2"]);
}
#[test]
fn iter() {
let t = TaskData::new(
TEST_UUID,
[
("prop1".to_string(), "val1".to_string()),
("prop2".to_string(), "val2".to_string()),
]
.into(),
);
let mut props: Vec<_> = t.iter().map(|(p, v)| (p.as_str(), v.as_str())).collect();
props.sort();
assert_eq!(props, vec![("prop1", "val1"), ("prop2", "val2")]);
}
#[test]
fn update_new_prop() {
let mut ops = Operations::new();
let mut t = TaskData::new(TEST_UUID, TaskMap::new());
t.update("prop1", Some("val1".into()), &mut ops);
let now = Utc::now();
set_all_timestamps(&mut ops, now);
assert_eq!(
ops,
make_ops(&[Operation::Update {
uuid: TEST_UUID,
property: "prop1".into(),
old_value: None,
value: Some("val1".into()),
timestamp: now,
}])
);
assert_eq!(t.get("prop1"), Some("val1"));
}
#[test]
fn update_existing_prop() {
let mut ops = Operations::new();
let mut t = TaskData::new(TEST_UUID, [("prop1".to_string(), "val".to_string())].into());
t.update("prop1", Some("new".into()), &mut ops);
let now = Utc::now();
set_all_timestamps(&mut ops, now);
assert_eq!(
ops,
make_ops(&[Operation::Update {
uuid: TEST_UUID,
property: "prop1".into(),
old_value: Some("val".into()),
value: Some("new".into()),
timestamp: now,
}])
);
assert_eq!(t.get("prop1"), Some("new"));
}
#[test]
fn update_remove_prop() {
let mut ops = Operations::new();
let mut t = TaskData::new(TEST_UUID, [("prop1".to_string(), "val".to_string())].into());
t.update("prop1", None, &mut ops);
let now = Utc::now();
set_all_timestamps(&mut ops, now);
assert_eq!(
ops,
make_ops(&[Operation::Update {
uuid: TEST_UUID,
property: "prop1".into(),
old_value: Some("val".into()),
value: None,
timestamp: now,
}])
);
assert_eq!(t.get("prop1"), None);
}
#[test]
fn delete() {
let mut ops = Operations::new();
let mut t = TaskData::new(TEST_UUID, [("prop1".to_string(), "val".to_string())].into());
t.delete(&mut ops);
assert_eq!(
ops,
make_ops(&[Operation::Delete {
uuid: TEST_UUID,
old_task: [("prop1".to_string(), "val".to_string())].into(),
}])
);
}
}