aboutsummaryrefslogtreecommitdiff
path: root/src/list.rs
diff options
context:
space:
mode:
authorMroik <mroik@delayed.space>2026-04-09 23:54:57 +0200
committerMroik <mroik@delayed.space>2026-04-13 06:56:11 +0200
commit0ff548961a68213487faa85b7b01cf717bb27268 (patch)
tree01c711002904b29e3ca829c17d55d79d20a0fa6b /src/list.rs
parent3cedb01c7982c5a505a510f7af9a9601c39a5b97 (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.rs774
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");
- }
-}
XMR address: 854DmXNrxULU3ZFJVs4Wc8PFhbq29RhqHhY8W6cdWrtFN3qmooKyyeYPcDzZTNRxphhJ5UzASQfAdEMwSteVqymk28aLhqj