use std::collections::HashMap;
use crate::{Todo, TodoId};
pub trait Store {
fn next_id(&mut self) -> TodoId;
fn insert(&mut self, todo: Todo);
fn get(&self, id: TodoId) -> Option<Todo>;
fn list(&self) -> Vec<Todo>;
fn update(&mut self, todo: Todo);
fn remove(&mut self, id: TodoId);
}
#[derive(Default)]
pub struct InMemoryStore {
next_id: u64,
items: HashMap<TodoId, Todo>,
}
impl InMemoryStore {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn from_todos(todos: Vec<Todo>) -> Self {
let next_id = todos.iter().map(|t| t.id.as_u64()).max().unwrap_or(0);
let items = todos.into_iter().map(|t| (t.id, t)).collect();
Self { next_id, items }
}
}
impl Store for InMemoryStore {
fn next_id(&mut self) -> TodoId {
self.next_id += 1;
TodoId::from_raw(self.next_id).expect("id overflow")
}
fn insert(&mut self, todo: Todo) {
self.items.insert(todo.id, todo);
}
fn get(&self, id: TodoId) -> Option<Todo> {
self.items.get(&id).cloned()
}
fn list(&self) -> Vec<Todo> {
let mut out: Vec<Todo> = self.items.values().cloned().collect();
out.sort_by_key(|t| t.id);
out
}
fn update(&mut self, todo: Todo) {
if self.items.contains_key(&todo.id) {
self.items.insert(todo.id, todo);
}
}
fn remove(&mut self, id: TodoId) {
self.items.remove(&id);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::TodoList;
use std::time::SystemTime;
#[test]
fn from_todos_empty() {
let store = InMemoryStore::from_todos(vec![]);
let mut list = TodoList::with_store(store);
let id = list.create("first").unwrap();
assert_eq!(id.as_u64(), 1);
assert_eq!(list.list().len(), 1);
}
#[test]
fn from_todos_with_existing() {
let created_at = SystemTime::now();
let id1 = TodoId::from_raw(1).unwrap();
let id2 = TodoId::from_raw(2).unwrap();
let todos = vec![
Todo {
id: id1,
title: "a".into(),
completed: false,
created_at,
completed_at: None,
description: None,
due_date: None,
priority: None,
tags: Vec::new(),
repeat_rule: None,
repeat_until: None,
repeat_count: None,
},
Todo {
id: id2,
title: "b".into(),
completed: true,
created_at,
completed_at: Some(created_at),
description: None,
due_date: None,
priority: None,
tags: Vec::new(),
repeat_rule: None,
repeat_until: None,
repeat_count: None,
},
];
let store = InMemoryStore::from_todos(todos);
let mut list = TodoList::with_store(store);
let id3 = list.create("c").unwrap();
assert_eq!(id3.as_u64(), 3);
let items = list.list();
assert_eq!(items.len(), 3);
assert_eq!(items[0].title, "a");
assert_eq!(items[1].title, "b");
assert_eq!(items[2].title, "c");
}
}