aboutsummaryrefslogtreecommitdiff
path: root/src/list.rs
diff options
context:
space:
mode:
authorMroik <mroik@delayed.space>2026-04-02 02:28:52 +0200
committerMroik <mroik@delayed.space>2026-04-13 06:55:04 +0200
commit590ffd8dc5c34a88951c6c92e1a806caa8a785d8 (patch)
treee8cafe92cdb5b15a8b1b09f91eac11bea48edfe5 /src/list.rs
parenta2c4cbcba928709dbe8ec0d6f8fc59add864aae1 (diff)
Add List model
We ideally want to be able to handle multiple mailing lists without having to run a new instance for each one. To do this we need to be able to create new lists. Add List model with its DB interactions. Signed-off-by: Mroik <mroik@delayed.space>
Diffstat (limited to 'src/list.rs')
-rw-r--r--src/list.rs205
1 files changed, 200 insertions, 5 deletions
diff --git a/src/list.rs b/src/list.rs
index 36cd492..a015da5 100644
--- a/src/list.rs
+++ b/src/list.rs
@@ -122,13 +122,127 @@ impl UserQuery<'_> {
}
}
+#[derive(Debug, PartialEq)]
+struct List {
+ address: String,
+ desc: Option<String>,
+}
+
+impl List {
+ fn new(name: &str) -> Self {
+ List {
+ address: String::from(name),
+ desc: None,
+ }
+ }
+
+ fn insert(&self) -> ListQuery<'_> {
+ ListQuery::Insert(&self.address, self.desc.as_deref())
+ }
+
+ fn delete(&self) -> ListQuery<'_> {
+ ListQuery::Delete(&self.address)
+ }
+
+ fn query_all<'a>() -> ListQuery<'a> {
+ ListQuery::QueryAll
+ }
+}
+
+enum ListQuery<'a> {
+ Insert(&'a str, Option<&'a str>),
+ Delete(&'a str),
+ QueryByAddress(&'a str),
+ QueryAll,
+}
+
+impl DBExecutable for ListQuery<'_> {
+ type T = List;
+
+ fn execute(&self, tx: &rusqlite::Transaction) -> Result<QueryResult<Self::T>> {
+ match self {
+ ListQuery::Insert(_, _) => self.db_insert(tx),
+ ListQuery::Delete(_) => self.db_delete(tx),
+ ListQuery::QueryByAddress(_) => self.db_query_by_address(tx),
+ ListQuery::QueryAll => self.db_query_all(tx),
+ }
+ }
+}
+
+impl ListQuery<'_> {
+ fn db_insert(&self, tx: &rusqlite::Transaction) -> Result<QueryResult<List>> {
+ let (name, desc) = if let ListQuery::Insert(name, desc) = self {
+ (name, desc)
+ } else {
+ unreachable!("this should only be called by ListQuery::Insert");
+ };
+
+ if let Some(desc) = desc {
+ let q = "INSERT INTO list (name, description) VALUES (?, ?)";
+ tx.execute(q, [name, desc])?;
+ } else {
+ let q = "INSERT INTO list (name) VALUES (?)";
+ tx.execute(q, [name])?;
+ }
+ Ok(QueryResult::Empty)
+ }
+
+ fn db_delete(&self, tx: &rusqlite::Transaction) -> Result<QueryResult<List>> {
+ let name = if let ListQuery::Delete(name) = self {
+ name
+ } else {
+ unreachable!("this should only be called by ListQuery::Delete")
+ };
+
+ let q = "DELETE FROM list WHERE name = ?";
+ tx.execute(q, [name])?;
+ Ok(QueryResult::Empty)
+ }
+
+ fn db_query_by_address(&self, tx: &rusqlite::Transaction) -> Result<QueryResult<List>> {
+ let name = if let ListQuery::QueryByAddress(name) = self {
+ name
+ } else {
+ unreachable!("this should only be called by ListQuery::QueryByName")
+ };
+
+ let q = "SELECT * FROM list WHERE name = ?";
+ let mut stmt = tx.prepare(q)?;
+ let ris = stmt
+ .query([name])?
+ .map(|row| {
+ Ok(List {
+ address: row.get(1).unwrap(),
+ desc: row.get(2).unwrap(),
+ })
+ })
+ .collect()?;
+ Ok(QueryResult::Vec(ris))
+ }
+
+ fn db_query_all(&self, tx: &rusqlite::Transaction) -> Result<QueryResult<List>> {
+ let q = "SELECT * FROM list";
+ let mut stmt = tx.prepare(q)?;
+ let ris = stmt
+ .query(())?
+ .map(|row| {
+ Ok(List {
+ address: row.get(1).unwrap(),
+ desc: row.get(2).unwrap(),
+ })
+ })
+ .collect()?;
+ Ok(QueryResult::Vec(ris))
+ }
+}
+
#[cfg(test)]
mod tests {
use anyhow::Result;
use crate::{
database::{DB_NAME, Database},
- list::User,
+ list::{List, User},
};
fn setup() -> Result<Database> {
@@ -142,7 +256,7 @@ mod tests {
}
#[test]
- fn insert_wo_name() {
+ fn user_insert_wo_name() {
let mut database = setup().expect("Failed setup");
let user = User {
@@ -166,7 +280,7 @@ mod tests {
}
#[test]
- fn insert_with_name() {
+ fn user_insert_with_name() {
let mut database = setup().expect("Failed setup");
let user = User {
@@ -190,7 +304,7 @@ mod tests {
}
#[test]
- fn insert_twice() {
+ fn user_insert_twice() {
let mut database = setup().expect("Failed setup");
let user = User {
@@ -206,7 +320,7 @@ mod tests {
}
#[test]
- fn delete() {
+ fn user_delete() {
let mut database = setup().expect("Failed setup");
let user = User {
@@ -227,4 +341,85 @@ mod tests {
drop(database);
cleanup().expect("Failed cleanup");
}
+
+ #[test]
+ fn list_insert_wo_desc() {
+ let mut database = setup().expect("failed setup");
+
+ let list = List {
+ address: String::from("poul"),
+ desc: None,
+ };
+ database.execute(list.insert()).expect("failed insert");
+ let ris = database.execute(List::query_all()).expect("failed query");
+ match ris {
+ crate::database::QueryResult::Empty => assert!(false),
+ crate::database::QueryResult::Vec(items) => {
+ assert_eq!(items.len(), 1);
+ assert_eq!(items[0], list);
+ }
+ }
+
+ cleanup().expect("failed cleanup");
+ }
+
+ #[test]
+ fn list_insert_with_desc() {
+ let mut database = setup().expect("failed setup");
+
+ let list = List {
+ address: String::from("poul"),
+ desc: Some(String::from("The mailing list of the POuL")),
+ };
+ database.execute(list.insert()).expect("failed insert");
+ let ris = database.execute(List::query_all()).expect("failed query");
+ match ris {
+ crate::database::QueryResult::Empty => assert!(false),
+ crate::database::QueryResult::Vec(items) => {
+ assert_eq!(items.len(), 1);
+ assert_eq!(items[0], list);
+ }
+ }
+
+ drop(database);
+ cleanup().expect("failed cleanup");
+ }
+
+ #[test]
+ fn list_insert_twice() {
+ let mut database = setup().expect("failed setup");
+
+ let list = List {
+ address: String::from("poul"),
+ desc: Some(String::from("The mailing list of the POuL")),
+ };
+ database.execute(list.insert()).expect("failed insert");
+ assert!(database.execute(list.insert()).is_err());
+
+ drop(database);
+ cleanup().expect("failed cleanup");
+ }
+
+ #[test]
+ fn list_delete() {
+ let mut database = setup().expect("failed setup");
+
+ let list = List {
+ address: String::from("poul"),
+ desc: Some(String::from("The mailing list of the POuL")),
+ };
+ database.execute(list.insert()).expect("failed insert");
+ database.execute(list.delete()).expect("failed delete");
+
+ let ris = database
+ .execute(List::query_all())
+ .expect("Failed query all");
+ match ris {
+ crate::database::QueryResult::Empty => assert!(false),
+ crate::database::QueryResult::Vec(items) => assert!(items.is_empty()),
+ }
+
+ drop(database);
+ cleanup().expect("failed cleanup");
+ }
}
XMR address: 854DmXNrxULU3ZFJVs4Wc8PFhbq29RhqHhY8W6cdWrtFN3qmooKyyeYPcDzZTNRxphhJ5UzASQfAdEMwSteVqymk28aLhqj