use bson::oid::ObjectId;
use chrono::prelude::*;
use serde_json;
use std::cmp::{Ord, Ordering, PartialOrd};
use std::fmt;
use std::str;
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct Task {
pub id: String,
pub title: String,
pub context: String,
pub created_date: DateTime<Local>,
pub priority: i64,
pub notes: Vec<Note>,
pub completed_date: Option<DateTime<Local>>,
pub body: Option<String>,
}
impl Task {
pub fn new(title: &str) -> Task {
Task {
id: ObjectId::new().unwrap().to_hex(),
title: title.to_string(),
context: "default".to_string(),
created_date: Local::now(),
priority: 1,
completed_date: None,
body: None,
notes: Vec::new(),
}
}
pub fn with_context(mut self, context: &str) -> Task {
self.context = context.to_string();
self
}
pub fn with_priority(mut self, priority: i64) -> Task {
self.priority = priority;
self
}
pub fn with_body(mut self, body: &str) -> Task {
self.body = Some(body.to_string());
self
}
pub fn precomplete(mut self) -> Task {
self.complete();
self
}
pub fn complete(&mut self) {
self.completed_date = Some(Local::now());
}
pub fn completed(&self) -> bool {
match self.completed_date {
Some(_) => true,
None => false,
}
}
pub fn add_note(&mut self, message: &str) {
self.notes.push(Note::new(message));
}
pub fn to_json(&self) -> Result<String, serde_json::Error> {
serde_json::to_string_pretty(self)
}
pub fn update(&mut self, mut other: Task) {
self.title = other.title;
self.context = other.context;
self.priority = other.priority;
self.completed_date = other.completed_date;
self.body = other.body;
self.notes.append(&mut other.notes);
}
}
impl fmt::Display for Task {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match serde_json::to_string_pretty(self) {
Ok(jsn) => write!(f, "{}", jsn),
Err(_) => write!(f, "{}", self.title),
}
}
}
impl Eq for Task {}
impl Ord for Task {
fn cmp(&self, other: &Task) -> Ordering {
match self.partial_cmp(other) {
Some(order) => order,
None => Ordering::Less,
}
}
}
impl PartialOrd for Task {
fn partial_cmp(&self, other: &Task) -> Option<Ordering> {
match self.priority.partial_cmp(&other.priority) {
Some(Ordering::Equal) => Some(self.created_date.date().cmp(&other.created_date.date())),
Some(order) => Some(order.reverse()),
None => None,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct Note {
pub id: String,
pub created_date: DateTime<Local>,
pub body: String,
}
impl Note {
pub fn new(body: &str) -> Note {
Note {
id: ObjectId::new().unwrap().to_hex(),
created_date: Local::now(),
body: body.to_string(),
}
}
}
impl fmt::Display for Note {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match serde_json::to_string_pretty(self) {
Ok(jsn) => write!(f, "{}", jsn),
Err(_) => write!(f, "{}", self.body),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::Duration;
#[test]
fn test_new_task() {
let task = Task::new("Some title");
assert!(task.title == "Some title".to_string());
assert!(task.completed_date.is_none());
assert!(!task.completed());
}
#[test]
fn test_complete_task() {
let mut task = Task::new("Test");
task.complete();
assert!(task.completed_date.is_some());
assert!(task.completed());
}
#[test]
fn test_simple_task_ordering() {
let mut list = vec![
Task::new("test1").with_priority(1),
Task::new("test3").with_priority(3),
Task::new("test2").with_priority(2),
Task::new("test0").with_priority(0),
];
list.sort();
let mut priority = 3;
for task in list {
println!("Task: {}", task.title);
assert_eq!(task.priority, priority);
priority = priority - 1;
}
}
#[test]
fn test_multi_day_task_ordering() {
let mut yesterday = Task::new("test2").with_priority(2);
yesterday.created_date = Local::now() - Duration::days(1);
let tasks = vec![
Task::new("test1").with_priority(1),
yesterday,
Task::new("test3").with_priority(3),
Task::new("test0").with_priority(2),
];
let mut list = tasks.clone();
list.sort();
let mut iter = list.into_iter();
assert_eq!(iter.next().unwrap(), tasks[2]);
assert_eq!(iter.next().unwrap(), tasks[1]);
assert_eq!(iter.next().unwrap(), tasks[3]);
assert_eq!(iter.next().unwrap(), tasks[0]);
}
#[test]
fn test_unique_ids() {
let t1 = Task::new("Test task");
let t2 = Task::new("Test task");
assert_ne!(t1.id, t2.id)
}
}