extern crate custom_error;
extern crate serde;
use custom_error::custom_error;
use serde::{Deserialize, Serialize};
use std::collections::VecDeque;
pub type WorkflowResult<T> = Result<T, WorkflowError>;
custom_error! { pub WorkflowError
WorkflowNotFound{workflow: String} = "Workflow `{workflow}` not found. It has to be one of: \
backlog, todo, doing or done",
TaskNotFoundInWorkflow{task: u16, workflow: String} = "Task `{task}`, was not found in the \
workflow `{workflow}`",
DeserializationError{ string: String, error: String } = "Error deserializing from string: {string}: \
{error}"
}
#[derive(Deserialize, Serialize, Debug)]
pub struct Workflows {
backlog: VecDeque<u16>,
todo: VecDeque<u16>,
doing: VecDeque<u16>,
done: VecDeque<u16>,
last_task: u16,
}
impl Workflows {
pub fn from_str(config_str: &str) -> WorkflowResult<Self> {
match toml::from_str(&config_str) {
Ok(workflow) => Ok(workflow),
Err(err) => Err(WorkflowError::DeserializationError {
string: config_str.to_owned(),
error: err.to_string(),
}),
}
}
pub fn last_task(&self) -> u16 {
self.last_task
}
pub fn workflow(&self, workflow_name: &str) -> WorkflowResult<&VecDeque<u16>> {
match workflow_name {
"backlog" => Ok(&self.backlog),
"todo" => Ok(&self.todo),
"doing" => Ok(&self.doing),
"done" => Ok(&self.done),
_ => Err(WorkflowError::WorkflowNotFound {
workflow: workflow_name.to_owned(),
}),
}
}
pub fn workflow_mut(&mut self, workflow_name: &str) -> WorkflowResult<&mut VecDeque<u16>> {
match workflow_name {
"backlog" => Ok(&mut self.backlog),
"todo" => Ok(&mut self.todo),
"doing" => Ok(&mut self.doing),
"done" => Ok(&mut self.done),
_ => Err(WorkflowError::WorkflowNotFound {
workflow: workflow_name.to_owned(),
}),
}
}
pub fn find_workflow_name(&self, task: &u16) -> Option<&str> {
if self.backlog.contains(task) {
Some("backlog")
} else if self.todo.contains(task) {
Some("todo")
} else if self.doing.contains(task) {
Some("doing")
} else if self.done.contains(task) {
Some("done")
} else {
None
}
}
pub fn main_max_len(&self) -> usize {
match vec![self.todo.len(), self.doing.len(), self.done.len()]
.iter()
.max()
{
None => 0,
Some(max_len) => max_len.clone(),
}
}
pub fn remove_task(&mut self, task: u16, workflow_name: &str) -> WorkflowResult<()> {
match self.workflow_mut(workflow_name) {
Ok(workflow) => match workflow.iter().position(|t| t == &task) {
Some(pos) => {
workflow.remove(pos).expect(&format!(
"The index `{}` is out of bounds from: `{:?}.",
pos, workflow,
));
Ok(())
}
None => {
return Err(WorkflowError::TaskNotFoundInWorkflow {
task: task,
workflow: workflow_name.to_owned(),
})
}
},
Err(_) => {
return Err(WorkflowError::WorkflowNotFound {
workflow: workflow_name.to_owned(),
})
}
}
}
fn add_task(&mut self, task: u16, workflow_name: &str) -> WorkflowResult<Option<u16>> {
match workflow_name {
"done" => {
self.done.push_front(task);
if self.done.len() > 5 {
return Ok(self.done.pop_back());
}
}
"backlog" | "todo" | "doing" => {
self.workflow_mut(workflow_name).unwrap().push_front(task)
}
_ => {
return Err(WorkflowError::WorkflowNotFound {
workflow: workflow_name.to_owned(),
})
}
}
Ok(None)
}
pub fn add_new_task(&mut self, workflow_name: &str) -> WorkflowResult<Option<u16>> {
self.last_task += 1;
self.add_task(self.last_task, workflow_name)
}
pub fn move_task(&mut self, task: u16, from: &str, to: &str) -> WorkflowResult<Option<u16>> {
self.remove_task(task, from)?;
self.add_task(task, to)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn last_task() {
let w: Workflows = Workflows {
backlog: vec![12, 11, 10, 9].into_iter().collect(),
todo: vec![8].into_iter().collect(),
doing: vec![7, 6].into_iter().collect(),
done: vec![5, 4, 3, 2, 1].into_iter().collect(),
last_task: 12,
};
assert_eq!(w.last_task(), 12u16);
}
#[test]
fn workflow() {
let w: Workflows = Workflows {
backlog: vec![12, 11, 10, 9].into_iter().collect(),
todo: vec![8].into_iter().collect(),
doing: vec![7, 6].into_iter().collect(),
done: vec![5, 4, 3, 2, 1].into_iter().collect(),
last_task: 12,
};
assert_eq!(
w.workflow("backlog").unwrap().as_slices().0,
&[12u16, 11u16, 10u16, 9u16][..]
);
assert!(w.workflow("haha greetings").is_err());
}
#[test]
fn workflow_mut() {
let mut w: Workflows = Workflows {
backlog: vec![12, 11, 10, 9].into_iter().collect(),
todo: vec![8].into_iter().collect(),
doing: vec![7, 6].into_iter().collect(),
done: vec![5, 4, 3, 2, 1].into_iter().collect(),
last_task: 12,
};
let backlog = w.workflow_mut("backlog").unwrap();
assert_eq!(backlog.as_slices().0, &[12u16, 11u16, 10u16, 9u16][..]);
backlog.push_back(1000u16);
assert_eq!(
backlog.as_slices().0,
&[12u16, 11u16, 10u16, 9u16, 1000u16][..]
);
assert!(w.workflow_mut("haha greetings").is_err());
}
#[test]
fn find_workflow_name() {
let w = Workflows {
backlog: vec![12, 11, 10, 9].into_iter().collect(),
todo: vec![8].into_iter().collect(),
doing: vec![7, 6].into_iter().collect(),
done: vec![5, 4, 3, 2, 1].into_iter().collect(),
last_task: 12,
};
assert_eq!(w.find_workflow_name(&4u16), Some("done"));
assert_eq!(w.find_workflow_name(&200u16), None);
}
#[test]
fn main_max_len() {
let w = Workflows {
backlog: vec![12, 11, 10, 9].into_iter().collect(),
todo: vec![8].into_iter().collect(),
doing: vec![7, 6].into_iter().collect(),
done: vec![5, 4, 3, 2, 1].into_iter().collect(),
last_task: 12,
};
assert_eq!(w.main_max_len(), 5);
let w = Workflows {
backlog: VecDeque::new(),
todo: VecDeque::new(),
doing: VecDeque::new(),
done: VecDeque::new(),
last_task: 0,
};
assert_eq!(w.main_max_len(), 0);
}
#[test]
fn remove_task() {
let mut w = Workflows {
backlog: vec![12, 11, 10, 9].into_iter().collect(),
todo: vec![8].into_iter().collect(),
doing: vec![7, 6].into_iter().collect(),
done: vec![5, 4, 3, 2, 1].into_iter().collect(),
last_task: 12,
};
assert!(w.remove_task(4, "done").is_ok());
assert!(!w.done.contains(&4u16));
assert!(w.remove_task(4, "none").is_err());
assert!(w.remove_task(2000, "backlog").is_err());
}
#[test]
fn add_task() {
let mut w = Workflows {
backlog: vec![12, 11, 10, 9].into_iter().collect(),
todo: vec![8].into_iter().collect(),
doing: vec![7, 6].into_iter().collect(),
done: vec![5, 4, 3, 2, 1].into_iter().collect(),
last_task: 12,
};
assert!(w.add_task(1000, "doing").is_ok());
assert!(w.doing.contains(&1000u16));
}
#[test]
fn add_new_task() {
let mut w = Workflows {
backlog: vec![12, 11, 10, 9].into_iter().collect(),
todo: vec![8].into_iter().collect(),
doing: vec![7, 6].into_iter().collect(),
done: vec![5, 4, 3, 2, 1].into_iter().collect(),
last_task: 12,
};
assert!(!w.done.contains(&13u16));
assert!(w.add_new_task("done").is_ok());
assert!(w.done.contains(&13u16));
assert_eq!(w.last_task, 13u16);
}
#[test]
fn move_task() {
let mut w = Workflows {
backlog: vec![12, 11, 10, 9].into_iter().collect(),
todo: vec![8].into_iter().collect(),
doing: vec![7, 6].into_iter().collect(),
done: vec![5, 4, 3, 2, 1].into_iter().collect(),
last_task: 12,
};
assert!(w.move_task(10u16, "backlog", "todo").is_ok());
assert!(!w.backlog.contains(&10u16));
assert!(w.todo.contains(&10u16));
assert!(w.move_task(1000u16, "todo", "done").is_err());
}
}