use uuid::Uuid;
use std::collections::{HashMap, VecDeque};
use chrono::prelude::*;
use serde::{Serialize, Deserialize};
#[derive(PartialEq, Debug, Serialize, Deserialize)]
pub enum RecurState {
Dead,
Active
}
#[typetag::serde(tag = "type")]
pub trait Recur {
fn current(&self) -> Option<DateTime<Local>>;
fn next(&mut self) -> ();
fn active(&self) -> RecurState;
}
#[typetag::serde(tag = "type")]
pub trait Dependency {
fn available(&self, space: &Workspace, task: (Uuid, &Task)) -> Result<bool, TaskError>;
}
#[derive(Serialize, Deserialize)]
pub struct Task {
pub title: String,
pub date: Box<dyn Recur>,
pub dependencies: Vec<Box<dyn Dependency>>,
pub children: Vec<Uuid>,
pub metadata: HashMap<String, String>,
}
#[derive(Serialize, Deserialize)]
pub struct Workspace {
pub tasks: HashMap<Uuid, Task>,
}
#[derive(Debug)]
pub enum TaskError {
NonexistentError,
NonexistentKeyError,
DuplicateError,
UnreachableError,
}
impl std::fmt::Display for TaskError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
TaskError::DuplicateError => write!(f, "Task already inserted"),
TaskError::NonexistentError => write!(f, "Task doesn't exist"),
TaskError::NonexistentKeyError => write!(f, "Metadata key doesn't exist"),
TaskError::UnreachableError => write!(f, "You've somehow reached an unreachable state. Congrats?"),
}
}
}
impl std::error::Error for TaskError {}
impl Workspace {
pub fn new() -> Workspace {
Workspace {tasks: HashMap::new()}
}
pub fn add_task(&mut self, title: &str, date: Box<dyn Recur>, deps: Vec<Box<dyn Dependency>>) -> Uuid {
let id = Uuid::new_v4();
self.tasks.insert(id, Task {
title: String::from(title),
date,
children: Vec::new(),
metadata: HashMap::new(),
dependencies: deps,
});
id
}
pub fn task_available(&self, id: Uuid) -> Result<bool, TaskError> {
let task = self.tasks.get(&id).ok_or(TaskError::NonexistentError)?;
for dependency in &task.dependencies {
if !dependency.available(&self, (id, task))? {
return Ok(false);
}
}
Ok(true)
}
pub fn task_done(&self, id: Uuid) -> Result<bool, TaskError> {
let task = self.tasks.get(&id).ok_or(TaskError::NonexistentError)?;
Ok(task.date.active() == RecurState::Dead)
}
pub fn task_complete(&mut self, id: Uuid) -> Result<(), TaskError> {
let task = self.tasks.get_mut(&id).ok_or(TaskError::NonexistentError)?;
task.date.next();
Ok(())
}
pub fn task_add_child(&mut self, parent_id: Uuid, child_id: Uuid) -> Result<(), TaskError> {
self.tasks.get(&child_id).ok_or(TaskError::NonexistentError)?;
let task = self.tasks.get_mut(&parent_id).ok_or(TaskError::NonexistentError)?;
for child in &task.children {
if *child == child_id { return Err(TaskError::DuplicateError) }
}
task.children.push(child_id);
Ok(())
}
pub fn task_add_metadata(&mut self, id: Uuid, key: String, val: String) -> Result<(), TaskError> {
let task = self.tasks.get_mut(&id).ok_or(TaskError::NonexistentError)?;
task.metadata.insert(key, val);
Ok(())
}
pub fn task_get_metadata(&self, id: Uuid, key: String) -> Result<Option<&String>, TaskError> {
let task = self.tasks.get(&id).ok_or(TaskError::NonexistentError)?;
Ok(task.metadata.get(&key))
}
pub fn task_set_metadata(&mut self, id: Uuid, key: String, val: String) -> Result<(), TaskError> {
let task = self.tasks.get_mut(&id).ok_or(TaskError::NonexistentError)?;
let metadata = task.metadata.get_mut(&key).ok_or(TaskError::NonexistentKeyError)?;
*metadata = val;
Ok(())
}
pub fn task_get_parent(&self, id: Uuid) -> Result<Uuid, TaskError> {
let mut stack: VecDeque<Uuid> = VecDeque::new();
for task in self.tasks.iter() {
for i in &task.1.children {
if *i == id { return Ok(*task.0); }
stack.push_front(*i);
}
}
while !stack.is_empty() {
let current_id = stack.pop_front().ok_or(TaskError::UnreachableError)?;
let current_task = self.tasks.get(¤t_id).ok_or(TaskError::NonexistentError)?;
for i in ¤t_task.children {
if *i == id { return Ok(current_id); }
stack.push_front(*i);
}
}
Err(TaskError::NonexistentError)
}
}