okbot_testkit/
memory_tasks.rs1use std::sync::{Arc, Mutex};
4
5use okbot_core::{
6 Clock, NewTask, Requester, Task, TaskChange, TaskError, TaskStatus, TaskStore, async_trait,
7 sort_tasks,
8};
9
10pub struct MemoryTasks {
13 tasks: Mutex<Vec<Task>>,
14 clock: Arc<dyn Clock>,
15}
16
17impl std::fmt::Debug for MemoryTasks {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 f.debug_struct("MemoryTasks").finish_non_exhaustive()
20 }
21}
22
23impl MemoryTasks {
24 pub fn new(clock: Arc<dyn Clock>) -> Self {
26 Self {
27 tasks: Mutex::new(Vec::new()),
28 clock,
29 }
30 }
31
32 fn lock(&self) -> std::sync::MutexGuard<'_, Vec<Task>> {
33 self.tasks
34 .lock()
35 .unwrap_or_else(std::sync::PoisonError::into_inner)
36 }
37
38 pub fn all(&self) -> Vec<Task> {
40 let mut tasks = self.lock().clone();
41 sort_tasks(&mut tasks);
42 tasks
43 }
44
45 pub fn insert(&self, task: Task) {
47 self.lock().push(task);
48 }
49
50 fn change(&self, id: &str, change: TaskChange) -> Result<Task, TaskError> {
51 let mut tasks = self.lock();
52 let task = tasks
53 .iter_mut()
54 .find(|t| t.id == id || t.id.rsplit('/').next() == Some(id))
55 .ok_or_else(|| TaskError::NotFound(id.to_string()))?;
56 if let Some(title) = change.title {
57 task.title = title;
58 }
59 if let Some(notes) = change.notes {
60 task.notes = notes;
61 }
62 if let Some(status) = change.status {
63 task.status = status;
64 }
65 if let Some(due) = change.due {
66 task.due = due;
67 }
68 if let Some(at) = change.remind_at {
69 task.remind_at = at;
70 }
71 if let Some(repeat) = change.repeat {
72 task.repeat = repeat;
73 }
74 Ok(task.clone())
75 }
76}
77
78#[async_trait]
79impl TaskStore for MemoryTasks {
80 async fn add(&self, who: &Requester, new: NewTask) -> Result<Task, TaskError> {
81 let mut tasks = self.lock();
82 let task = Task {
83 id: format!("tasks/test/t-{}", tasks.len() + 1),
84 kind: new.kind,
85 status: if new.paused {
86 TaskStatus::Paused
87 } else {
88 TaskStatus::Open
89 },
90 title: new.title,
91 notes: new.notes,
92 scope: new.scope,
93 due: new.due,
94 remind_at: new.remind_at,
95 repeat: new.repeat,
96 deliver_to: new.deliver_to,
97 origin: Some(who.conversation.clone()),
98 author: format!("{}/{}", who.conversation.channel, who.user.id),
99 created: self.clock.now(),
100 };
101 tasks.push(task.clone());
102 Ok(task)
103 }
104
105 async fn list(&self, _who: &Requester) -> Result<Vec<Task>, TaskError> {
106 Ok(self.all())
107 }
108
109 async fn update(
110 &self,
111 _who: &Requester,
112 id: &str,
113 change: TaskChange,
114 ) -> Result<Task, TaskError> {
115 self.change(id, change)
116 }
117
118 async fn delete(&self, _who: &Requester, id: &str) -> Result<Task, TaskError> {
119 let mut tasks = self.lock();
120 let at = tasks
121 .iter()
122 .position(|t| t.id == id || t.id.rsplit('/').next() == Some(id))
123 .ok_or_else(|| TaskError::NotFound(id.to_string()))?;
124 Ok(tasks.remove(at))
125 }
126
127 async fn scheduled(&self) -> Result<Vec<Task>, TaskError> {
128 Ok(self
129 .all()
130 .into_iter()
131 .filter(|t| t.status.is_active() && t.remind_at.is_some())
132 .collect())
133 }
134
135 async fn after_fire(&self, id: &str, change: TaskChange) -> Result<Task, TaskError> {
136 self.change(id, change)
137 }
138}