use std::cell::RefCell;
use std::collections::btree_set::Iter as BTreeSetIter;
use std::collections::BTreeSet;
use std::ops::Deref as _;
use std::ops::DerefMut as _;
use std::rc::Rc;
use anyhow::anyhow;
use anyhow::Result;
use uuid::Uuid;
use crate::db::Db;
use crate::db::Entry as DbEntry;
use crate::db::Iter as DbIter;
use crate::ops::Op;
use crate::ops::Ops;
use crate::position::Position;
use crate::ser::tasks::Task as SerTask;
use crate::ser::tasks::Tasks as SerTasks;
use crate::ser::ToSerde;
use crate::tags::Tag;
use crate::tags::Templates;
use crate::tasks::Builder as TaskBuilder;
const MAX_UNDO_STEP_COUNT: usize = 64;
type Id = Uuid;
#[derive(Clone, Debug)]
struct TaskInner {
id: Id,
summary: String,
details: String,
tags: BTreeSet<Tag>,
templates: Rc<Templates>,
}
#[derive(Clone, Debug, Default)]
pub struct Builder {
summary: String,
details: String,
tags: Vec<Tag>,
}
impl Builder {
pub fn set_summary<S>(mut self, summary: S) -> Self
where
S: Into<String>,
{
self.summary = summary.into();
self
}
pub fn set_details(mut self, details: String) -> Self {
self.details = details;
self
}
pub fn set_tags(mut self, tags: Vec<Tag>) -> Self {
self.tags = tags;
self
}
pub fn build(self, templates: Rc<Templates>) -> Task {
let Self {
summary,
details,
tags,
} = self;
let inner = TaskInner {
id: Id::new_v4(),
summary,
details,
tags: tags.into_iter().collect(),
templates,
};
Task(RefCell::new(inner))
}
}
#[derive(Clone, Debug)]
pub struct Task(RefCell<TaskInner>);
impl Task {
#[cfg(test)]
pub fn new(summary: impl Into<String>) -> Self {
Self::builder()
.set_summary(summary)
.build(Rc::new(Templates::new()))
}
pub fn builder() -> Builder {
Builder::default()
}
fn with_serde(task: SerTask, templates: Rc<Templates>) -> Result<Self> {
let mut tags = BTreeSet::new();
for tag in task.tags.into_iter() {
let tag = templates
.instantiate(tag.id)
.ok_or_else(|| anyhow!("encountered invalid tag ID {}", tag.id))?;
tags.insert(tag);
}
let inner = TaskInner {
id: task.id,
summary: task.summary,
details: task.details,
tags,
templates,
};
Ok(Self(RefCell::new(inner)))
}
#[cfg(test)]
#[inline]
pub fn id(&self) -> Id {
self.0.try_borrow().unwrap().id
}
#[inline]
pub fn summary(&self) -> String {
self.0.try_borrow().unwrap().summary.clone()
}
#[inline]
pub fn set_summary(&mut self, summary: String) {
self.0.try_borrow_mut().unwrap().summary = summary
}
#[inline]
pub fn details(&self) -> String {
self.0.try_borrow().unwrap().details.clone()
}
#[inline]
pub fn set_details(&mut self, details: String) {
self.0.try_borrow_mut().unwrap().details = details
}
#[inline]
pub fn tags<F, R>(&self, mut f: F) -> R
where
F: FnMut(BTreeSetIter<'_, Tag>) -> R,
{
f(self.0.try_borrow().unwrap().tags.iter())
}
pub fn set_tags<I>(&mut self, tags: I)
where
I: Iterator<Item = Tag>,
{
self.0.try_borrow_mut().unwrap().tags = tags.collect();
}
#[inline]
pub fn has_tag(&self, tag: &Tag) -> bool {
self.0.try_borrow().unwrap().tags.contains(tag)
}
#[inline]
pub fn set_tag(&mut self, tag: Tag) -> bool {
self.0.try_borrow_mut().unwrap().tags.insert(tag)
}
#[inline]
pub fn unset_tag(&mut self, tag: &Tag) -> bool {
self.0.try_borrow_mut().unwrap().tags.remove(tag)
}
fn update_from(&self, other: Task) {
let mut borrow = self.0.try_borrow_mut().unwrap();
*borrow.deref_mut() = other.0.into_inner();
}
pub fn templates(&self) -> Rc<Templates> {
Rc::clone(&self.0.try_borrow().unwrap().templates)
}
}
fn task_to_serde(task: &Task, position: Option<&Position>) -> SerTask {
let borrow = task.0.try_borrow().unwrap();
let TaskInner {
ref id,
ref summary,
ref details,
ref tags,
..
} = borrow.deref();
let task = SerTask {
id: *id,
summary: summary.clone(),
details: details.clone(),
tags: tags.iter().map(Tag::to_serde).collect(),
position: position.map(Position::to_serde),
};
task
}
#[cfg(test)]
impl ToSerde for Task {
type Output = SerTask;
fn to_serde(&self) -> Self::Output {
task_to_serde(self, None)
}
}
impl ToSerde for (&Task, Position) {
type Output = SerTask;
fn to_serde(&self) -> Self::Output {
let (task, position) = self;
task_to_serde(task, Some(position))
}
}
fn find_position(
before: Option<DbEntry<'_, Task, Position>>,
after: Option<DbEntry<'_, Task, Position>>,
) -> Position {
loop {
let position = Position::between(
before.as_ref().map(DbEntry::aux),
after.as_ref().map(DbEntry::aux),
);
if let Some(position) = position {
break position
}
let mut entry = after.as_ref().unwrap().clone();
let mut next = entry.next();
loop {
let position = Position::between(Some(entry.aux()), next.as_ref().map(DbEntry::aux));
if let Some(position) = position {
let () = entry.set_aux(position);
break
}
entry = next.unwrap();
next = entry.next();
}
}
}
fn add_task(tasks: &mut Db<Task, Position>, task: Rc<Task>, target: Option<Target>) -> Rc<Task> {
let (before, after) = if let Some(target) = target {
let entry = tasks.find(target.task()).unwrap();
match target {
Target::Before(..) => (entry.prev(), Some(entry)),
Target::After(..) => (Some(entry.clone()), entry.next()),
}
} else {
(tasks.last(), None)
};
let idx = after
.as_ref()
.map(DbEntry::index)
.unwrap_or_else(|| tasks.len());
let position = find_position(before, after);
let _entry = tasks
.try_insert_with_aux(idx, Rc::clone(&task), position)
.unwrap();
if cfg!(debug_assertions) {
let positions = tasks
.iter()
.enumerate()
.map(|(idx, _task)| tasks.get(idx).unwrap().aux().get())
.collect::<Vec<_>>();
let mut sorted = positions.clone();
let () = sorted.sort_by(f64::total_cmp);
debug_assert_eq!(positions, sorted);
}
task
}
fn remove_task(tasks: &mut Db<Task, Position>, task: &Rc<Task>) -> (Rc<Task>, Position, usize) {
let idx = tasks.find(task).unwrap().index();
let (task, aux) = tasks.remove(idx);
(task, aux, idx)
}
fn update_task(task: &Rc<Task>, other: Task) -> Task {
let before = Task::clone(task.deref());
let () = task.update_from(other);
before
}
#[derive(Clone, Debug)]
enum Target {
Before(Rc<Task>),
After(Rc<Task>),
}
impl Target {
fn task(&self) -> &Rc<Task> {
match self {
Self::Before(task) | Self::After(task) => task,
}
}
}
#[derive(Debug)]
enum TaskOp {
Add {
task: Rc<Task>,
after: Option<Rc<Task>>,
},
Remove {
task: Rc<Task>,
position: Option<(usize, Position)>,
},
Update {
updated: (Rc<Task>, Task),
before: Option<Task>,
},
Move {
task: Rc<Task>,
to: Target,
position: Option<(usize, Position)>,
},
}
impl TaskOp {
fn add(task: Rc<Task>, after: Option<Rc<Task>>) -> Self {
Self::Add { task, after }
}
fn remove(task: Rc<Task>) -> Self {
Self::Remove {
task,
position: None,
}
}
fn update(task: Rc<Task>, updated: Task) -> Self {
Self::Update {
updated: (task, updated),
before: None,
}
}
fn move_(task: Rc<Task>, to: Target) -> Self {
Self::Move {
task,
to,
position: None,
}
}
}
impl Op<Db<Task, Position>, Option<Rc<Task>>> for TaskOp {
fn exec(&mut self, tasks: &mut Db<Task, Position>) -> Option<Rc<Task>> {
match self {
Self::Add {
ref mut task,
after,
} => {
let added = add_task(tasks, Rc::clone(task), after.clone().map(Target::After));
Some(added)
},
Self::Remove { task, position } => {
let (_task, aux, idx) = remove_task(tasks, task);
*position = Some((idx, aux));
None
},
Self::Update { updated, before } => {
let task = &updated.0;
let _task = update_task(task, updated.1.clone());
*before = Some(_task);
Some(Rc::clone(task))
},
Self::Move { task, to, position } => {
let idx = tasks.find(task).unwrap().index();
let (removed, aux) = tasks.remove(idx);
debug_assert!(!Rc::ptr_eq(&removed, to.task()));
*position = Some((idx, aux));
let task = add_task(tasks, removed, Some(to.clone()));
Some(task)
},
}
}
fn undo(&mut self, tasks: &mut Db<Task, Position>) -> Option<Rc<Task>> {
match self {
Self::Add { task, .. } => {
let (_task, _aux, _idx) = remove_task(tasks, task);
None
},
Self::Remove { task, position } => {
let (idx, aux) = position.unwrap();
tasks
.try_insert_with_aux(idx, Rc::clone(task), aux)
.unwrap();
Some(Rc::clone(task))
},
Self::Update { updated, before } => {
let before = before.clone().unwrap();
let task = &updated.0;
let _task = update_task(task, before);
let entry = tasks.find(task).unwrap();
Some(Rc::clone(&entry))
},
Self::Move { task, position, .. } => {
let (position, aux) = position.unwrap();
let idx = tasks.find(task).unwrap().index();
let (removed, _aux) = tasks.remove(idx);
let _entry = tasks
.try_insert_with_aux(position, Rc::clone(&removed), aux)
.unwrap();
Some(removed)
},
}
}
}
pub type TaskIter<'tasks> = DbIter<'tasks, Task, Position>;
#[derive(Debug)]
struct TasksInner {
templates: Rc<Templates>,
tasks: Db<Task, Position>,
operations: Ops<TaskOp, Db<Task, Position>, Option<Rc<Task>>>,
}
#[derive(Debug)]
pub struct Tasks(RefCell<TasksInner>);
impl Tasks {
pub fn with_serde(mut tasks: SerTasks, templates: Rc<Templates>) -> Result<Self> {
tasks.0.sort_by(|first, second| {
let first = first.position.unwrap_or(f64::MAX);
let second = second.position.unwrap_or(f64::MAX);
first.total_cmp(&second)
});
let len = tasks.0.len();
let tasks =
tasks
.0
.into_iter()
.try_fold(Vec::with_capacity(len), |mut vec, task| -> Result<_> {
let position = task.position;
let task = Task::with_serde(task, Rc::clone(&templates))?;
let position = position.map(Position::new).unwrap_or_else(|| {
let prev_pos = vec.last().map(|(_task, position)| position);
Position::between(prev_pos.cloned(), None).unwrap()
});
let () = vec.push((task, position));
Result::Ok(vec)
})?;
let tasks = Db::from_iter_with_aux(tasks);
let inner = TasksInner {
templates,
tasks,
operations: Ops::new(MAX_UNDO_STEP_COUNT),
};
Ok(Self(RefCell::new(inner)))
}
#[cfg(test)]
pub fn with_serde_tasks(tasks: Vec<SerTask>) -> Result<Self> {
tasks.iter().for_each(|x| assert!(x.tags.is_empty()));
let tasks = SerTasks::from(tasks);
let templates = Rc::new(Templates::new());
Self::with_serde(tasks, templates)
}
pub fn to_serde(&self) -> SerTasks {
let inner = self.0.try_borrow().unwrap();
let tasks = inner
.tasks
.iter()
.enumerate()
.map(|(idx, task)| {
let position = inner.tasks.get(idx).unwrap().aux();
(task.deref(), position).to_serde()
})
.collect();
SerTasks(tasks)
}
#[inline]
pub fn iter<F, R>(&self, mut f: F) -> R
where
F: FnMut(TaskIter<'_>) -> R,
{
f(self.0.try_borrow().unwrap().tasks.iter())
}
pub fn add(&self, task: TaskBuilder, after: Option<Rc<Task>>) -> Rc<Task> {
let mut borrow = self.0.try_borrow_mut().unwrap();
let TasksInner {
ref mut templates,
ref mut operations,
ref mut tasks,
..
} = borrow.deref_mut();
let task = task.build(Rc::clone(templates));
let op = TaskOp::add(Rc::new(task), after);
let task = operations.exec(op, tasks).unwrap();
task
}
pub fn remove(&self, task: Rc<Task>) {
let mut borrow = self.0.try_borrow_mut().unwrap();
let TasksInner {
ref mut operations,
ref mut tasks,
..
} = borrow.deref_mut();
let op = TaskOp::remove(task);
operations.exec(op, tasks);
}
pub fn update(&self, task: Rc<Task>, updated: Task) {
let mut borrow = self.0.try_borrow_mut().unwrap();
let TasksInner {
ref mut operations,
ref mut tasks,
..
} = borrow.deref_mut();
let op = TaskOp::update(task, updated);
operations.exec(op, tasks);
}
pub fn move_before(&self, to_move: Rc<Task>, other: Rc<Task>) {
if !Rc::ptr_eq(&to_move, &other) {
let mut borrow = self.0.try_borrow_mut().unwrap();
let TasksInner {
ref mut operations,
ref mut tasks,
..
} = borrow.deref_mut();
let to = Target::Before(other);
let op = TaskOp::move_(to_move, to);
operations.exec(op, tasks);
}
}
pub fn move_after(&self, to_move: Rc<Task>, other: Rc<Task>) {
if !Rc::ptr_eq(&to_move, &other) {
let mut borrow = self.0.try_borrow_mut().unwrap();
let TasksInner {
ref mut operations,
ref mut tasks,
..
} = borrow.deref_mut();
let to = Target::After(other);
let op = TaskOp::move_(to_move, to);
operations.exec(op, tasks);
}
}
pub fn undo(&self) -> Option<Option<Rc<Task>>> {
let mut borrow = self.0.try_borrow_mut().unwrap();
let TasksInner {
ref mut operations,
ref mut tasks,
..
} = borrow.deref_mut();
operations.undo(tasks)
}
pub fn redo(&self) -> Option<Option<Rc<Task>>> {
let mut borrow = self.0.try_borrow_mut().unwrap();
let TasksInner {
ref mut operations,
ref mut tasks,
..
} = borrow.deref_mut();
operations.redo(tasks)
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use std::num::NonZeroUsize;
use crate::ser::tags::Id as SerTemplateId;
use crate::ser::tags::Template as SerTemplate;
use crate::ser::tags::Templates as SerTemplates;
use crate::test::make_tasks;
use crate::test::COMPLETE_TAG;
#[test]
fn task_tag_query_and_adjustment() {
let templates = vec![SerTemplate {
id: SerTemplateId::new(NonZeroUsize::new(42).unwrap()),
name: COMPLETE_TAG.to_string(),
}];
let templates = Templates::with_serde(SerTemplates(templates)).unwrap();
let complete = templates.instantiate_from_name(COMPLETE_TAG);
let mut task = Task::new("test task");
assert!(!task.has_tag(&complete));
assert!(task.set_tag(complete.clone()));
assert!(task.has_tag(&complete));
assert!(!task.set_tag(complete.clone()));
assert!(task.has_tag(&complete));
assert!(task.unset_tag(&complete));
assert!(!task.has_tag(&complete));
assert!(!task.unset_tag(&complete));
assert!(!task.has_tag(&complete));
}
#[test]
fn exec_undo_task_add_empty() {
let mut tasks = Db::from_iter_with_aux([]);
let mut ops = Ops::new(3);
let task1 = Rc::new(Task::new("task1"));
let op = TaskOp::add(task1, None);
ops.exec(op, &mut tasks);
assert_eq!(tasks.iter().len(), 1);
assert_eq!(tasks.get(0).unwrap().summary(), "task1");
ops.undo(&mut tasks);
assert_eq!(tasks.iter().len(), 0);
ops.redo(&mut tasks);
assert_eq!(tasks.iter().len(), 1);
assert_eq!(tasks.get(0).unwrap().summary(), "task1");
}
#[test]
fn exec_undo_task_add_non_empty() {
let iter = [Task::new("task1")]
.into_iter()
.enumerate()
.map(|(idx, task)| (task, Position::from_int(idx)));
let mut tasks = Db::from_iter_with_aux(iter);
let mut ops = Ops::new(3);
let task2 = Rc::new(Task::new("task2"));
let op = TaskOp::add(task2, None);
ops.exec(op, &mut tasks);
assert_eq!(tasks.iter().len(), 2);
assert_eq!(tasks.get(0).unwrap().summary(), "task1");
assert_eq!(tasks.get(1).unwrap().summary(), "task2");
let task3 = Rc::new(Task::new("task3"));
let after = Rc::clone(&tasks.get(0).unwrap());
let op = TaskOp::add(task3, Some(after));
ops.exec(op, &mut tasks);
assert_eq!(tasks.iter().len(), 3);
assert_eq!(tasks.get(0).unwrap().summary(), "task1");
assert_eq!(tasks.get(1).unwrap().summary(), "task3");
assert_eq!(tasks.get(2).unwrap().summary(), "task2");
ops.undo(&mut tasks);
assert_eq!(tasks.iter().len(), 2);
assert_eq!(tasks.get(0).unwrap().summary(), "task1");
assert_eq!(tasks.get(1).unwrap().summary(), "task2");
ops.undo(&mut tasks);
assert_eq!(tasks.iter().len(), 1);
assert_eq!(tasks.get(0).unwrap().summary(), "task1");
}
#[test]
fn exec_undo_task_remove_single() {
let iter = [Task::new("task1")]
.into_iter()
.enumerate()
.map(|(idx, task)| (task, Position::from_int(idx)));
let mut tasks = Db::from_iter_with_aux(iter);
let mut ops = Ops::new(3);
let task = Rc::clone(&tasks.get(0).unwrap());
let op = TaskOp::remove(task);
ops.exec(op, &mut tasks);
assert_eq!(tasks.iter().len(), 0);
ops.undo(&mut tasks);
assert_eq!(tasks.iter().len(), 1);
assert_eq!(tasks.get(0).unwrap().summary(), "task1");
ops.redo(&mut tasks);
assert_eq!(tasks.iter().len(), 0);
}
#[test]
fn exec_undo_task_remove_multi() {
let iter = [Task::new("task1"), Task::new("task2"), Task::new("task3")]
.into_iter()
.enumerate()
.map(|(idx, task)| (task, Position::from_int(idx)));
let mut tasks = Db::from_iter_with_aux(iter);
let mut ops = Ops::new(3);
let task = Rc::clone(&tasks.get(1).unwrap());
let op = TaskOp::remove(task);
ops.exec(op, &mut tasks);
assert_eq!(tasks.iter().len(), 2);
assert_eq!(tasks.get(0).unwrap().summary(), "task1");
assert_eq!(tasks.get(1).unwrap().summary(), "task3");
ops.undo(&mut tasks);
assert_eq!(tasks.iter().len(), 3);
assert_eq!(tasks.get(0).unwrap().summary(), "task1");
assert_eq!(tasks.get(1).unwrap().summary(), "task2");
assert_eq!(tasks.get(2).unwrap().summary(), "task3");
ops.redo(&mut tasks);
assert_eq!(tasks.iter().len(), 2);
assert_eq!(tasks.get(0).unwrap().summary(), "task1");
assert_eq!(tasks.get(1).unwrap().summary(), "task3");
}
#[test]
fn exec_undo_task_update() {
let iter = [Task::new("task1"), Task::new("task2")]
.into_iter()
.enumerate()
.map(|(idx, task)| (task, Position::from_int(idx)));
let mut tasks = Db::from_iter_with_aux(iter);
let mut ops = Ops::new(3);
let task = Rc::clone(&tasks.get(0).unwrap());
let mut updated = Task::clone(task.deref());
updated.set_summary("foo!".to_string());
let op = TaskOp::update(task, updated);
ops.exec(op, &mut tasks);
assert_eq!(tasks.iter().len(), 2);
assert_eq!(tasks.get(0).unwrap().summary(), "foo!");
assert_eq!(tasks.get(1).unwrap().summary(), "task2");
ops.undo(&mut tasks);
assert_eq!(tasks.iter().len(), 2);
assert_eq!(tasks.get(0).unwrap().summary(), "task1");
assert_eq!(tasks.get(1).unwrap().summary(), "task2");
ops.redo(&mut tasks);
assert_eq!(tasks.iter().len(), 2);
assert_eq!(tasks.get(0).unwrap().summary(), "foo!");
assert_eq!(tasks.get(1).unwrap().summary(), "task2");
}
#[test]
fn exec_undo_task_move() {
let iter = [Task::new("task1"), Task::new("task2")]
.into_iter()
.enumerate()
.map(|(idx, task)| (task, Position::from_int(idx)));
let mut tasks = Db::from_iter_with_aux(iter);
let mut ops = Ops::new(3);
let task = Rc::clone(&tasks.get(1).unwrap());
let before = Rc::clone(&tasks.get(0).unwrap());
let op = TaskOp::move_(task, Target::Before(before));
ops.exec(op, &mut tasks);
assert_eq!(tasks.iter().len(), 2);
assert_eq!(tasks.get(0).unwrap().summary(), "task2");
assert_eq!(tasks.get(1).unwrap().summary(), "task1");
ops.undo(&mut tasks);
assert_eq!(tasks.iter().len(), 2);
assert_eq!(tasks.get(0).unwrap().summary(), "task1");
assert_eq!(tasks.get(1).unwrap().summary(), "task2");
let task = Rc::clone(&tasks.get(1).unwrap());
let after = Rc::clone(&tasks.get(0).unwrap());
let op = TaskOp::move_(task, Target::After(after));
ops.exec(op, &mut tasks);
assert_eq!(tasks.iter().len(), 2);
assert_eq!(tasks.get(0).unwrap().summary(), "task1");
assert_eq!(tasks.get(1).unwrap().summary(), "task2");
ops.undo(&mut tasks);
assert_eq!(tasks.iter().len(), 2);
assert_eq!(tasks.get(0).unwrap().summary(), "task1");
assert_eq!(tasks.get(1).unwrap().summary(), "task2");
}
#[test]
fn add_task() {
let task_vec = make_tasks(3);
let tasks = Tasks::with_serde_tasks(task_vec.clone()).unwrap();
let builder = Task::builder().set_summary("4");
let task = tasks.add(builder, None);
let tasks = tasks.to_serde().into_task_vec();
let mut expected = task_vec;
let () = expected.push(task.to_serde());
assert_eq!(tasks, expected);
}
#[test]
fn add_task_after() {
let task_vec = make_tasks(3);
let tasks = Tasks::with_serde_tasks(task_vec.clone()).unwrap();
let after = Rc::clone(&tasks.0.borrow().tasks.get(0).unwrap());
let builder = Task::builder().set_summary("4");
let task = tasks.add(builder, Some(after));
let tasks = tasks.to_serde().into_task_vec();
let mut expected = task_vec;
let () = expected.insert(1, task.to_serde());
assert_eq!(tasks, expected);
}
#[test]
fn remove_task() {
let task_vec = make_tasks(3);
let tasks = Tasks::with_serde_tasks(task_vec.clone()).unwrap();
let task = tasks.iter(|mut iter| Rc::clone(iter.nth(1).unwrap()));
tasks.remove(task);
let tasks = tasks.to_serde().into_task_vec();
let mut expected = task_vec;
expected.remove(1);
assert_eq!(tasks, expected);
}
#[test]
fn update_task() {
let task_vec = make_tasks(3);
let tasks = Tasks::with_serde_tasks(task_vec.clone()).unwrap();
let task = tasks.iter(|mut iter| Rc::clone(iter.nth(1).unwrap()));
let mut updated = Task::clone(task.deref());
updated.set_summary("amended".to_string());
tasks.update(task, updated);
let tasks = tasks.to_serde().into_task_vec();
let mut expected = task_vec;
expected[1].summary = "amended".to_string();
assert_eq!(tasks, expected);
}
#[test]
fn move_before_for_first() {
let task_vec = make_tasks(3);
let tasks = Tasks::with_serde_tasks(task_vec.clone()).unwrap();
let task1 = tasks.iter(|mut iter| Rc::clone(iter.next().unwrap()));
let task2 = tasks.iter(|mut iter| Rc::clone(iter.nth(1).unwrap()));
tasks.move_before(task1, task2);
let tasks = tasks.to_serde().into_task_vec();
let expected = task_vec;
assert_eq!(tasks, expected);
}
#[test]
fn move_after_for_last() {
let task_vec = make_tasks(3);
let tasks = Tasks::with_serde_tasks(task_vec.clone()).unwrap();
let task1 = tasks.iter(|mut iter| Rc::clone(iter.nth(2).unwrap()));
let task2 = tasks.iter(|mut iter| Rc::clone(iter.nth(1).unwrap()));
tasks.move_after(task1, task2);
let expected = task_vec;
let tasks = tasks.to_serde().into_task_vec();
assert_eq!(tasks, expected);
}
#[test]
fn move_before() {
let task_vec = make_tasks(4);
let tasks = Tasks::with_serde_tasks(task_vec.clone()).unwrap();
let task1 = tasks.iter(|mut iter| Rc::clone(iter.nth(2).unwrap()));
let task2 = tasks.iter(|mut iter| Rc::clone(iter.nth(1).unwrap()));
tasks.move_before(task1, task2);
let tasks = tasks.to_serde().into_task_vec();
let mut expected = task_vec;
expected.swap(2, 1);
assert_eq!(tasks, expected);
}
#[test]
fn move_after() {
let task_vec = make_tasks(4);
let tasks = Tasks::with_serde_tasks(task_vec.clone()).unwrap();
let task1 = tasks.iter(|mut iter| Rc::clone(iter.nth(1).unwrap()));
let task2 = tasks.iter(|mut iter| Rc::clone(iter.nth(2).unwrap()));
tasks.move_after(task1, task2);
let tasks = tasks.to_serde().into_task_vec();
let mut expected = task_vec;
expected.swap(1, 2);
assert_eq!(tasks, expected);
}
#[test]
fn position_finding() {
let task_vec = make_tasks(5);
let tasks = Tasks::with_serde_tasks(task_vec).unwrap();
(0..100_000).for_each(|_| {
let (task1, task2) = {
let tasks = tasks.0.borrow();
let entry1 = tasks.tasks.get(1).unwrap();
let entry2 = tasks.tasks.get(2).unwrap();
(Rc::clone(&entry1), Rc::clone(&entry2))
};
tasks.move_after(task1, task2);
})
}
}