diff options
| author | Mroik <mroik@delayed.space> | 2026-04-09 23:54:57 +0200 |
|---|---|---|
| committer | Mroik <mroik@delayed.space> | 2026-04-13 06:56:11 +0200 |
| commit | 0ff548961a68213487faa85b7b01cf717bb27268 (patch) | |
| tree | 01c711002904b29e3ca829c17d55d79d20a0fa6b /src/list.rs | |
| parent | 3cedb01c7982c5a505a510f7af9a9601c39a5b97 (diff) | |
Rename modules to more accurate names
Signed-off-by: Mroik <mroik@delayed.space>
Diffstat (limited to 'src/list.rs')
| -rw-r--r-- | src/list.rs | 774 |
1 files changed, 0 insertions, 774 deletions
diff --git a/src/list.rs b/src/list.rs deleted file mode 100644 index 43a9819..0000000 --- a/src/list.rs +++ /dev/null @@ -1,774 +0,0 @@ -use anyhow::Result; -use rusqlite::fallible_iterator::FallibleIterator; - -use crate::database::{Query, QueryResult}; - -#[derive(PartialEq, Debug)] -struct User { - name: Option<String>, - email: String, -} - -impl User { - fn new(email: &str) -> Self { - Self { - name: None, - email: String::from(email), - } - } - - fn insert(&self) -> UserQuery<'_> { - let name = self.name.as_deref(); - UserQuery::Insert(name, self.email.as_str()) - } - - fn delete(&self) -> UserQuery<'_> { - UserQuery::Delete(&self.email) - } - - fn query_by_email(&self) -> UserQuery<'_> { - UserQuery::QueryByEmail(&self.email) - } - - fn query_all<'a>() -> UserQuery<'a> { - UserQuery::QueryAll - } - - fn subscribe<'a>(&'a self, list: &'a List) -> SubscriptionQuery<'a> { - SubscriptionQuery::Insert(&self.email, &list.name) - } - - fn unsubscribe<'a>(&'a self, list: &'a List) -> SubscriptionQuery<'a> { - SubscriptionQuery::Delete(&self.email, &list.name) - } - - fn subscribed_to(&self) -> SubscriptionQuery<'_> { - SubscriptionQuery::Lists(&self.email) - } -} - -enum UserQuery<'a> { - Insert(Option<&'a str>, &'a str), - Delete(&'a str), - QueryByEmail(&'a str), - QueryAll, -} - -impl Query for UserQuery<'_> { - type T = User; - fn callback(&self, tx: &rusqlite::Transaction) -> Result<QueryResult<Self::T>> { - match self { - UserQuery::Insert(name, email) => self.db_insert(tx, name, email), - UserQuery::Delete(email) => self.db_delete(tx, email), - UserQuery::QueryByEmail(email) => self.db_query_by_email(tx, email), - UserQuery::QueryAll => self.db_query_all(tx), - } - } -} - -impl UserQuery<'_> { - fn db_insert( - &self, - tx: &rusqlite::Transaction<'_>, - name: &Option<&str>, - email: &str, - ) -> Result<QueryResult<User>> { - if let Some(name) = name { - let q = "INSERT INTO user (name, email) VALUES (?, ?)"; - tx.execute(q, [name, email])?; - return Ok(QueryResult::Empty); - } - - let q = "INSERT INTO user (email) VALUES (?)"; - tx.execute(q, [email])?; - Ok(QueryResult::Empty) - } - - fn db_delete(&self, tx: &rusqlite::Transaction<'_>, email: &str) -> Result<QueryResult<User>> { - let q = "DELETE FROM user WHERE email = ?"; - tx.execute(q, [email])?; - Ok(QueryResult::Empty) - } - - fn db_query_by_email( - &self, - tx: &rusqlite::Transaction<'_>, - email: &str, - ) -> Result<QueryResult<User>> { - let q = "SELECT * FROM user WHERE email LIKE ?"; - let mut stmt = tx.prepare(q)?; - let ris = stmt - .query([email])? - .map(|row| { - Ok(User { - name: row.get(1).unwrap(), - email: row.get(2).unwrap(), - }) - }) - .collect()?; - Ok(QueryResult::Vec(ris)) - } - - fn db_query_all(&self, tx: &rusqlite::Transaction<'_>) -> Result<QueryResult<User>> { - let q = "SELECT * FROM user"; - let mut stmt = tx.prepare(q)?; - let ris = stmt - .query(())? - .map(|row| { - Ok(User { - name: row.get(1).unwrap(), - email: row.get(2).unwrap(), - }) - }) - .collect()?; - Ok(QueryResult::Vec(ris)) - } -} - -#[derive(Debug, PartialEq)] -struct List { - name: String, - desc: Option<String>, -} - -impl List { - fn new(name: &str) -> Self { - List { - name: String::from(name), - desc: None, - } - } - - fn insert(&self) -> ListQuery<'_> { - ListQuery::Insert(&self.name, self.desc.as_deref()) - } - - fn delete(&self) -> ListQuery<'_> { - ListQuery::Delete(&self.name) - } - - fn query_all<'a>() -> ListQuery<'a> { - ListQuery::QueryAll - } - - fn subscribers(&self) -> SubscriptionQuery<'_> { - SubscriptionQuery::Subscribers(&self.name) - } -} - -enum ListQuery<'a> { - Insert(&'a str, Option<&'a str>), - Delete(&'a str), - QueryByName(&'a str), - QueryAll, -} - -impl Query for ListQuery<'_> { - type T = List; - - fn callback(&self, tx: &rusqlite::Transaction) -> Result<QueryResult<Self::T>> { - match self { - ListQuery::Insert(name, desc) => self.db_insert(tx, name, desc), - ListQuery::Delete(name) => self.db_delete(tx, name), - ListQuery::QueryByName(name) => self.db_query_by_address(tx, name), - ListQuery::QueryAll => self.db_query_all(tx), - } - } -} - -impl ListQuery<'_> { - fn db_insert( - &self, - tx: &rusqlite::Transaction, - name: &str, - desc: &Option<&str>, - ) -> Result<QueryResult<List>> { - 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, name: &str) -> Result<QueryResult<List>> { - let q = "DELETE FROM list WHERE name = ?"; - tx.execute(q, [name])?; - Ok(QueryResult::Empty) - } - - fn db_query_by_address( - &self, - tx: &rusqlite::Transaction, - name: &str, - ) -> Result<QueryResult<List>> { - let q = "SELECT * FROM list WHERE name = ?"; - let mut stmt = tx.prepare(q)?; - let ris = stmt - .query([name])? - .map(|row| { - Ok(List { - name: 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 { - name: row.get(1).unwrap(), - desc: row.get(2).unwrap(), - }) - }) - .collect()?; - Ok(QueryResult::Vec(ris)) - } -} - -enum SubscriptionResult { - List(List), - Subscriber(User), - Count(i64), -} - -enum SubscriptionQuery<'a> { - Insert(&'a str, &'a str), - Delete(&'a str, &'a str), - /// This is just for debug - Count, - Lists(&'a str), - Subscribers(&'a str), -} - -impl Query for SubscriptionQuery<'_> { - type T = SubscriptionResult; - - fn callback(&self, tx: &rusqlite::Transaction) -> Result<QueryResult<Self::T>> { - match self { - SubscriptionQuery::Insert(email, list_name) => self.db_insert(tx, email, list_name), - SubscriptionQuery::Count => self.db_query_all(tx), - SubscriptionQuery::Delete(email, list_name) => self.db_delete(tx, email, list_name), - SubscriptionQuery::Lists(email) => self.db_lists(tx, email), - SubscriptionQuery::Subscribers(list_name) => self.db_subscribers(tx, list_name), - } - } -} - -impl SubscriptionQuery<'_> { - fn db_insert( - &self, - tx: &rusqlite::Transaction, - email: &str, - list_name: &str, - ) -> Result<QueryResult<SubscriptionResult>> { - let q = "INSERT INTO subscription - SELECT user.id, list.id FROM user CROSS JOIN list - WHERE user.email = ? AND list.name = ?"; - tx.execute(q, [email, list_name])?; - - Ok(QueryResult::Empty) - } - - fn db_query_all(&self, tx: &rusqlite::Transaction) -> Result<QueryResult<SubscriptionResult>> { - let q = "SELECT COUNT(*) FROM subscription"; - let ris = tx.query_one(q, (), |row| Ok(row.get(0).unwrap()))?; - Ok(QueryResult::Single(SubscriptionResult::Count(ris))) - } - - fn db_delete( - &self, - tx: &rusqlite::Transaction, - email: &str, - list_name: &str, - ) -> Result<QueryResult<SubscriptionResult>> { - let q = "WITH helper AS (SELECT user.id AS user_id, list.id AS list_id FROM user CROSS JOIN list - WHERE user.email = ? AND list.name = ?) - DELETE FROM subscription WHERE EXISTS ( - SELECT 1 FROM helper - WHERE helper.user_id = subscription.user_id AND helper.list_id = subscription.list_id)"; - tx.execute(q, [email, list_name])?; - Ok(QueryResult::Empty) - } - - fn db_lists( - &self, - tx: &rusqlite::Transaction, - email: &str, - ) -> Result<QueryResult<SubscriptionResult>> { - let q = "SELECT list.name, list.description - FROM subscription JOIN list ON list.id = subscription.list_id - JOIN user ON user.id = subscription.user_id - WHERE user.email = ?"; - let ris = tx - .prepare(q)? - .query([email])? - .map(|row| { - Ok(SubscriptionResult::List(List { - name: row.get(0).unwrap(), - desc: row.get(1).unwrap(), - })) - }) - .collect()?; - - Ok(QueryResult::Vec(ris)) - } - - fn db_subscribers( - &self, - tx: &rusqlite::Transaction, - list_name: &str, - ) -> Result<QueryResult<SubscriptionResult>> { - let q = "SELECT user.name, user.email - FROM subscription JOIN list ON list.id = subscription.list_id - JOIN user ON user.id = subscription.user_id - WHERE list.name = ?"; - let ris = tx - .prepare(q)? - .query([list_name])? - .map(|row| { - Ok(SubscriptionResult::Subscriber(User { - name: row.get(0).unwrap(), - email: row.get(1).unwrap(), - })) - }) - .collect()?; - - Ok(QueryResult::Vec(ris)) - } -} - -#[cfg(test)] -mod tests { - use anyhow::Result; - - use crate::{ - database::{DB_NAME, Database, QueryResult}, - list::{List, SubscriptionQuery, SubscriptionResult, User}, - }; - - fn setup() -> Result<Database> { - std::fs::create_dir("test")?; - Database::new(&format!("test/{}", DB_NAME)) - } - - fn cleanup() -> Result<()> { - std::fs::remove_dir_all("test")?; - Ok(()) - } - - #[test] - fn user_insert_wo_name() { - let mut database = setup().expect("Failed setup"); - - let user = User { - name: None, - email: String::from("mroik@poul.org"), - }; - database.execute(user.insert()).expect("Failed insert"); - let user2 = database - .execute(user.query_by_email()) - .expect("Failed query"); - match user2 { - QueryResult::Vec(items) => { - assert_eq!(items.len(), 1); - assert_eq!(items[0], user); - } - _ => assert!(false), - } - - drop(database); - cleanup().expect("Failed cleanup"); - } - - #[test] - fn user_insert_with_name() { - let mut database = setup().expect("Failed setup"); - - let user = User { - name: Some(String::from("Mirko Faina")), - email: String::from("mroik@poul.org"), - }; - database.execute(user.insert()).expect("Failed insert"); - let user2 = database - .execute(user.query_by_email()) - .expect("Failed query"); - match &user2 { - QueryResult::Vec(items) => { - assert_eq!(items.len(), 1); - assert_eq!(items[0], user); - } - _ => assert!(false), - } - - drop(database); - cleanup().expect("Failed cleanup"); - } - - #[test] - fn user_insert_twice() { - let mut database = setup().expect("Failed setup"); - - let user = User { - name: Some(String::from("Mirko Faina")), - email: String::from("mroik@poul.org"), - }; - database.execute(user.insert()).expect("Failed insert"); - let ris = database.execute(user.insert()); - assert!(ris.is_err()); - - drop(database); - cleanup().expect("Failed cleanup"); - } - - #[test] - fn user_delete() { - let mut database = setup().expect("Failed setup"); - - let user = User { - name: Some(String::from("Mirko Faina")), - email: String::from("mroik@poul.org"), - }; - database.execute(user.insert()).expect("Failed insert"); - database.execute(user.delete()).expect("Failed delete"); - - let ris = database - .execute(User::query_all()) - .expect("Failed query all"); - match ris { - QueryResult::Vec(items) => assert!(items.is_empty()), - _ => assert!(false), - } - - drop(database); - cleanup().expect("Failed cleanup"); - } - - #[test] - fn list_insert_wo_desc() { - let mut database = setup().expect("failed setup"); - - let list = List { - name: String::from("poul"), - desc: None, - }; - database.execute(list.insert()).expect("failed insert"); - let ris = database.execute(List::query_all()).expect("failed query"); - match ris { - QueryResult::Vec(items) => { - assert_eq!(items.len(), 1); - assert_eq!(items[0], list); - } - _ => assert!(false), - } - - cleanup().expect("failed cleanup"); - } - - #[test] - fn list_insert_with_desc() { - let mut database = setup().expect("failed setup"); - - let list = List { - name: 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 { - QueryResult::Vec(items) => { - assert_eq!(items.len(), 1); - assert_eq!(items[0], list); - } - _ => assert!(false), - } - - drop(database); - cleanup().expect("failed cleanup"); - } - - #[test] - fn list_insert_twice() { - let mut database = setup().expect("failed setup"); - - let list = List { - name: 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 { - name: 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 { - QueryResult::Vec(items) => assert!(items.is_empty()), - _ => assert!(false), - } - - drop(database); - cleanup().expect("failed cleanup"); - } - - #[test] - fn subscription_insert() { - let mut database = setup().expect("failed setup"); - - let list = List { - name: String::from("poul"), - desc: None, - }; - let list2 = List { - name: String::from("dev"), - desc: None, - }; - - database.execute(list.insert()).expect("failed insert"); - database.execute(list2.insert()).expect("failed insert"); - - let user = User { - name: None, - email: String::from("mroik@delayed.space"), - }; - let user2 = User { - name: None, - email: String::from("mroik@poul.org"), - }; - - database.execute(user.insert()).expect("failed insert"); - database.execute(user2.insert()).expect("failed insert"); - - database - .execute(user.subscribe(&list)) - .expect("failed insert"); - - let ris = database - .execute(SubscriptionQuery::Count) - .expect("failed query"); - match ris { - QueryResult::Single(SubscriptionResult::Count(c)) => { - assert_eq!(c, 1) - } - _ => assert!(false), - } - - drop(database); - cleanup().expect("failed cleanup"); - } - - #[test] - fn subscription_delete() { - let mut database = setup().expect("failed setup"); - - let list = List { - name: String::from("poul"), - desc: None, - }; - let list2 = List { - name: String::from("dev"), - desc: None, - }; - - database.execute(list.insert()).expect("failed insert"); - database.execute(list2.insert()).expect("failed insert"); - - let user = User { - name: None, - email: String::from("mroik@delayed.space"), - }; - let user2 = User { - name: None, - email: String::from("mroik@poul.org"), - }; - - database.execute(user.insert()).expect("failed insert"); - database.execute(user2.insert()).expect("failed insert"); - - database - .execute(user.subscribe(&list)) - .expect("failed insert"); - database - .execute(user.subscribe(&list2)) - .expect("failed insert"); - database - .execute(user2.subscribe(&list)) - .expect("failed insert"); - database - .execute(user2.subscribe(&list2)) - .expect("failed insert"); - - let mut ris = database - .execute(SubscriptionQuery::Count) - .expect("failed query"); - match ris { - QueryResult::Single(SubscriptionResult::Count(c)) => { - assert_eq!(c, 4) - } - _ => assert!(false), - } - - database - .execute(user.unsubscribe(&list)) - .expect("failed delete"); - - ris = database - .execute(SubscriptionQuery::Count) - .expect("failed query"); - match ris { - QueryResult::Single(SubscriptionResult::Count(c)) => { - assert_eq!(c, 3) - } - _ => assert!(false), - } - - drop(database); - cleanup().expect("failed cleanup"); - } - - #[test] - fn subscription_lists() { - let mut database = setup().expect("failed setup"); - - let list = List { - name: String::from("poul"), - desc: None, - }; - let list2 = List { - name: String::from("dev"), - desc: None, - }; - - database.execute(list.insert()).expect("failed insert"); - database.execute(list2.insert()).expect("failed insert"); - - let user = User { - name: None, - email: String::from("mroik@delayed.space"), - }; - let user2 = User { - name: None, - email: String::from("mroik@poul.org"), - }; - - database.execute(user.insert()).expect("failed insert"); - database.execute(user2.insert()).expect("failed insert"); - - database - .execute(user.subscribe(&list)) - .expect("failed insert"); - database - .execute(user.subscribe(&list2)) - .expect("failed insert"); - database - .execute(user2.subscribe(&list)) - .expect("failed insert"); - database - .execute(user2.subscribe(&list2)) - .expect("failed insert"); - - let mut ris = database - .execute(SubscriptionQuery::Count) - .expect("failed query"); - match ris { - QueryResult::Single(SubscriptionResult::Count(c)) => { - assert_eq!(c, 4) - } - _ => assert!(false), - } - - ris = database - .execute(user.subscribed_to()) - .expect("failed query"); - match ris { - QueryResult::Vec(items) => assert_eq!(items.len(), 2), - _ => assert!(false), - } - - drop(database); - cleanup().expect("failed cleanup"); - } - - #[test] - fn subscription_subscribers() { - let mut database = setup().expect("failed setup"); - - let list = List { - name: String::from("poul"), - desc: None, - }; - let list2 = List { - name: String::from("dev"), - desc: None, - }; - - database.execute(list.insert()).expect("failed insert"); - database.execute(list2.insert()).expect("failed insert"); - - let user = User { - name: None, - email: String::from("mroik@delayed.space"), - }; - let user2 = User { - name: None, - email: String::from("mroik@poul.org"), - }; - - database.execute(user.insert()).expect("failed insert"); - database.execute(user2.insert()).expect("failed insert"); - - database - .execute(user.subscribe(&list)) - .expect("failed insert"); - database - .execute(user.subscribe(&list2)) - .expect("failed insert"); - database - .execute(user2.subscribe(&list)) - .expect("failed insert"); - database - .execute(user2.subscribe(&list2)) - .expect("failed insert"); - - let mut ris = database - .execute(SubscriptionQuery::Count) - .expect("failed query"); - match ris { - QueryResult::Single(SubscriptionResult::Count(c)) => { - assert_eq!(c, 4) - } - _ => assert!(false), - } - - ris = database.execute(list.subscribers()).expect("failed query"); - match ris { - QueryResult::Vec(items) => assert_eq!(items.len(), 2), - _ => assert!(false), - } - - drop(database); - cleanup().expect("failed cleanup"); - } -} |
