From 6514efe4a5a9cae3707c334290817f628e9c013d Mon Sep 17 00:00:00 2001 From: Mroik Date: Fri, 3 Apr 2026 17:01:22 +0200 Subject: Refactor to avoid double checks on types The various Query enums limit themselves to checking which variant they are before choosing a which function to call to process the request. Because they are enums with multiple variants, to unpack we have to check again which variant they are. Unpack before the call and modify function signatures to receive the already unpacked values. Signed-off-by: Mroik --- src/list.rs | 142 +++++++++++++++++++++++------------------------------------- 1 file changed, 55 insertions(+), 87 deletions(-) (limited to 'src') diff --git a/src/list.rs b/src/list.rs index 4222cbf..8624650 100644 --- a/src/list.rs +++ b/src/list.rs @@ -58,22 +58,21 @@ impl DBExecutable for UserQuery<'_> { type T = User; fn execute(&self, tx: &rusqlite::Transaction) -> Result> { match self { - UserQuery::Insert(_, _) => self.db_insert(tx), - UserQuery::Delete(_) => self.db_delete(tx), - UserQuery::QueryByEmail(_) => self.db_query_by_email(tx), + 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<'_>) -> Result> { - let (name, email) = if let UserQuery::Insert(name, email) = self { - (name, email) - } else { - unreachable!("this should only be called by a UserQuery::Insert") - }; - + fn db_insert( + &self, + tx: &rusqlite::Transaction<'_>, + name: &Option<&str>, + email: &str, + ) -> Result> { if let Some(name) = name { let q = "INSERT INTO user (name, email) VALUES (?, ?)"; tx.execute(q, [name, email])?; @@ -85,25 +84,17 @@ impl UserQuery<'_> { Ok(QueryResult::Empty) } - fn db_delete(&self, tx: &rusqlite::Transaction<'_>) -> Result> { - let email = if let UserQuery::Delete(email) = self { - email - } else { - unreachable!("this should only be called by a UserQuery::Delete") - }; - + fn db_delete(&self, tx: &rusqlite::Transaction<'_>, email: &str) -> Result> { let q = "DELETE FROM user WHERE email = ?"; tx.execute(q, [email])?; Ok(QueryResult::Empty) } - fn db_query_by_email(&self, tx: &rusqlite::Transaction<'_>) -> Result> { - let email = if let UserQuery::QueryByEmail(email) = self { - email - } else { - unreachable!("this should only be called by a UserQuery::QueryByEmail") - }; - + fn db_query_by_email( + &self, + tx: &rusqlite::Transaction<'_>, + email: &str, + ) -> Result> { let q = "SELECT * FROM user WHERE email LIKE ?"; let mut stmt = tx.prepare(q)?; let ris = stmt @@ -168,7 +159,7 @@ impl List { enum ListQuery<'a> { Insert(&'a str, Option<&'a str>), Delete(&'a str), - QueryByAddress(&'a str), + QueryByName(&'a str), QueryAll, } @@ -177,22 +168,21 @@ impl DBExecutable for ListQuery<'_> { fn execute(&self, tx: &rusqlite::Transaction) -> Result> { match self { - ListQuery::Insert(_, _) => self.db_insert(tx), - ListQuery::Delete(_) => self.db_delete(tx), - ListQuery::QueryByAddress(_) => self.db_query_by_address(tx), + 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) -> Result> { - let (name, desc) = if let ListQuery::Insert(name, desc) = self { - (name, desc) - } else { - unreachable!("this should only be called by ListQuery::Insert"); - }; - + fn db_insert( + &self, + tx: &rusqlite::Transaction, + name: &str, + desc: &Option<&str>, + ) -> Result> { if let Some(desc) = desc { let q = "INSERT INTO list (name, description) VALUES (?, ?)"; tx.execute(q, [name, desc])?; @@ -203,25 +193,17 @@ impl ListQuery<'_> { Ok(QueryResult::Empty) } - fn db_delete(&self, tx: &rusqlite::Transaction) -> Result> { - let name = if let ListQuery::Delete(name) = self { - name - } else { - unreachable!("this should only be called by ListQuery::Delete") - }; - + fn db_delete(&self, tx: &rusqlite::Transaction, name: &str) -> Result> { let q = "DELETE FROM list WHERE name = ?"; tx.execute(q, [name])?; Ok(QueryResult::Empty) } - fn db_query_by_address(&self, tx: &rusqlite::Transaction) -> Result> { - let name = if let ListQuery::QueryByAddress(name) = self { - name - } else { - unreachable!("this should only be called by ListQuery::QueryByName") - }; - + fn db_query_by_address( + &self, + tx: &rusqlite::Transaction, + name: &str, + ) -> Result> { let q = "SELECT * FROM list WHERE name = ?"; let mut stmt = tx.prepare(q)?; let ris = stmt @@ -272,23 +254,22 @@ impl DBExecutable for SubscriptionQuery<'_> { fn execute(&self, tx: &rusqlite::Transaction) -> Result> { match self { - SubscriptionQuery::Insert(_, _) => self.db_insert(tx), + SubscriptionQuery::Insert(email, list_name) => self.db_insert(tx, email, list_name), SubscriptionQuery::Count => self.db_query_all(tx), - SubscriptionQuery::Delete(_, _) => self.db_delete(tx), - SubscriptionQuery::Lists(_) => self.db_lists(tx), - SubscriptionQuery::Subscribers(_) => self.db_subscribers(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) -> Result> { - let (email, list_name) = if let SubscriptionQuery::Insert(a, b) = self { - (a, b) - } else { - unreachable!("this should only be called by SubscriptionQuery::Insert") - }; - + fn db_insert( + &self, + tx: &rusqlite::Transaction, + email: &str, + list_name: &str, + ) -> Result> { let q = "INSERT INTO subscription SELECT user.id, list.id FROM user CROSS JOIN list WHERE user.email = ? AND list.name = ?"; @@ -298,39 +279,31 @@ impl SubscriptionQuery<'_> { } fn db_query_all(&self, tx: &rusqlite::Transaction) -> Result> { - match self { - SubscriptionQuery::Count => (), - _ => unreachable!("this should only be called by SubscriptionQuery::Count"), - }; - 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) -> Result> { - let (email, name) = if let SubscriptionQuery::Delete(e, n) = self { - (e, n) - } else { - unreachable!("this should only be called by SubscriptionQuery::Delete") - }; - + fn db_delete( + &self, + tx: &rusqlite::Transaction, + email: &str, + list_name: &str, + ) -> Result> { 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, name])?; + tx.execute(q, [email, list_name])?; Ok(QueryResult::Empty) } - fn db_lists(&self, tx: &rusqlite::Transaction) -> Result> { - let email = if let SubscriptionQuery::Lists(email) = self { - email - } else { - unreachable!("this should only be called by SubscriptionQuery::Lists") - }; - + fn db_lists( + &self, + tx: &rusqlite::Transaction, + email: &str, + ) -> Result> { 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 @@ -352,20 +325,15 @@ impl SubscriptionQuery<'_> { fn db_subscribers( &self, tx: &rusqlite::Transaction, + list_name: &str, ) -> Result> { - let name = if let SubscriptionQuery::Subscribers(name) = self { - name - } else { - unreachable!("this should only be called by SubscriptionQuery::Subscribers") - }; - 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([name])? + .query([list_name])? .map(|row| { Ok(SubscriptionResult::Subscriber(User { name: row.get(0).unwrap(), -- cgit v1.3