1use std::collections::HashMap;
4
5use crate::{Todo, TodoId};
6
7pub trait Store {
9 fn next_id(&mut self) -> TodoId;
11 fn insert(&mut self, todo: Todo);
13 fn get(&self, id: TodoId) -> Option<Todo>;
15 fn list(&self) -> Vec<Todo>;
17 fn update(&mut self, todo: Todo);
19 fn remove(&mut self, id: TodoId);
21}
22
23#[derive(Default)]
25pub struct InMemoryStore {
26 next_id: u64,
27 items: HashMap<TodoId, Todo>,
28}
29
30impl InMemoryStore {
31 #[must_use]
32 pub fn new() -> Self {
33 Self::default()
34 }
35
36 #[must_use]
38 pub fn from_todos(todos: Vec<Todo>) -> Self {
39 let next_id = todos.iter().map(|t| t.id.as_u64()).max().unwrap_or(0);
40 let items = todos.into_iter().map(|t| (t.id, t)).collect();
41 Self { next_id, items }
42 }
43}
44
45impl Store for InMemoryStore {
46 fn next_id(&mut self) -> TodoId {
47 self.next_id += 1;
48 TodoId::from_raw(self.next_id).expect("id overflow")
49 }
50
51 fn insert(&mut self, todo: Todo) {
52 self.items.insert(todo.id, todo);
53 }
54
55 fn get(&self, id: TodoId) -> Option<Todo> {
56 self.items.get(&id).cloned()
57 }
58
59 fn list(&self) -> Vec<Todo> {
60 let mut out: Vec<Todo> = self.items.values().cloned().collect();
61 out.sort_by_key(|t| t.id);
62 out
63 }
64
65 fn update(&mut self, todo: Todo) {
66 if self.items.contains_key(&todo.id) {
67 self.items.insert(todo.id, todo);
68 }
69 }
70
71 fn remove(&mut self, id: TodoId) {
72 self.items.remove(&id);
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 use crate::TodoList;
80 use std::time::SystemTime;
81
82 #[test]
83 fn from_todos_empty() {
84 let store = InMemoryStore::from_todos(vec![]);
85 let mut list = TodoList::with_store(store);
86 let id = list.create("first").unwrap();
87 assert_eq!(id.as_u64(), 1);
88 assert_eq!(list.list().len(), 1);
89 }
90
91 #[test]
92 fn from_todos_with_existing() {
93 let created_at = SystemTime::now();
94 let id1 = TodoId::from_raw(1).unwrap();
95 let id2 = TodoId::from_raw(2).unwrap();
96 let todos = vec![
97 Todo {
98 id: id1,
99 title: "a".into(),
100 completed: false,
101 created_at,
102 completed_at: None,
103 description: None,
104 due_date: None,
105 priority: None,
106 tags: Vec::new(),
107 repeat_rule: None,
108 repeat_until: None,
109 repeat_count: None,
110 },
111 Todo {
112 id: id2,
113 title: "b".into(),
114 completed: true,
115 created_at,
116 completed_at: Some(created_at),
117 description: None,
118 due_date: None,
119 priority: None,
120 tags: Vec::new(),
121 repeat_rule: None,
122 repeat_until: None,
123 repeat_count: None,
124 },
125 ];
126 let store = InMemoryStore::from_todos(todos);
127 let mut list = TodoList::with_store(store);
128 let id3 = list.create("c").unwrap();
129 assert_eq!(id3.as_u64(), 3);
130 let items = list.list();
131 assert_eq!(items.len(), 3);
132 assert_eq!(items[0].title, "a");
133 assert_eq!(items[1].title, "b");
134 assert_eq!(items[2].title, "c");
135 }
136}