use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct TaskList {
pub created_at: DateTime<Utc>,
pub name: String,
pub task_ids: Vec<u32>,
pub updated_at: DateTime<Utc>,
}
impl TaskList {
pub fn new(name: impl Into<String>) -> Self {
let now = Utc::now();
Self {
created_at: now,
name: name.into(),
task_ids: Vec::new(),
updated_at: now,
}
}
pub fn add_task(&mut self, task_id: u32) {
self.task_ids.push(task_id);
self.touch();
}
pub fn contains_task(&self, task_id: u32) -> bool {
self.task_ids.contains(&task_id)
}
pub fn is_empty(&self) -> bool {
self.task_ids.is_empty()
}
pub fn len(&self) -> usize {
self.task_ids.len()
}
pub fn remove_task(&mut self, task_id: u32) {
self.task_ids.retain(|id| *id != task_id);
self.touch();
}
pub fn touch(&mut self) {
self.updated_at = Utc::now();
}
#[must_use]
pub fn with_tasks(mut self, task_ids: Vec<u32>) -> Self {
self.task_ids = task_ids;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
mod new {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_creates_a_new_task_list_with_the_given_name() {
let name = "test list";
let list = TaskList::new(name);
assert_eq!(list.name, name);
assert!(list.created_at.timestamp() > 0);
assert!(list.updated_at.timestamp() > 0);
assert!(list.task_ids.is_empty());
}
}
mod add_task {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_adds_a_task_to_the_list() {
let mut list = TaskList::new("test list");
let task_id = 1;
list.add_task(task_id);
assert_eq!(list.task_ids, vec![task_id]);
}
}
mod contains_task {
use super::*;
#[test]
fn it_returns_true_if_the_list_contains_the_given_task_id() {
let mut list = TaskList::new("test list");
let task_id = 1;
list.add_task(task_id);
assert!(list.contains_task(task_id));
}
#[test]
fn it_returns_false_if_the_list_does_not_contain_the_given_task_id() {
let mut list = TaskList::new("test list");
let task_id = 1;
list.add_task(task_id);
assert!(!list.contains_task(2));
}
}
mod is_empty {
use super::*;
#[test]
fn it_returns_true_if_the_list_is_empty() {
let list = TaskList::new("test list");
assert!(list.is_empty());
}
#[test]
fn it_returns_false_if_the_list_is_not_empty() {
let mut list = TaskList::new("test list");
list.add_task(1);
assert!(!list.is_empty());
}
}
mod len {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_returns_the_number_of_tasks_in_the_list() {
let mut list = TaskList::new("test list");
list.add_task(1);
list.add_task(2);
assert_eq!(list.len(), 2);
}
}
mod remove_task {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_removes_a_task_from_the_list() {
let mut list = TaskList::new("test list");
let task_id_1 = 1;
let task_id_2 = 2;
list.add_task(task_id_1);
list.add_task(task_id_2);
assert_eq!(list.task_ids, vec![task_id_1, task_id_2]);
list.remove_task(task_id_1);
assert_eq!(list.task_ids, vec![task_id_2]);
}
}
mod touch {
use pretty_assertions::assert_ne;
use super::*;
#[test]
fn it_updates_the_lists_updated_at_timestamp() {
let mut list = TaskList::new("test list");
let old_updated_at = list.updated_at;
list.updated_at = old_updated_at - chrono::Duration::seconds(1);
let very_old_time = list.updated_at;
list.touch();
assert_ne!(list.updated_at, very_old_time);
assert!(list.updated_at > very_old_time);
}
}
mod with_tasks {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_creates_a_task_list_with_task_ids() {
let task_ids = vec![1, 2, 3];
let list = TaskList::new("test list").with_tasks(task_ids.clone());
assert_eq!(list.task_ids, task_ids);
}
}
}