From 48e199eff5fca8f5e4aa71a4091d3ae7acc82b9b Mon Sep 17 00:00:00 2001 From: Joel Klinghed Date: Mon, 30 Dec 2024 22:54:26 +0100 Subject: Add methods for modifying projects While doing that I realized I had forgotten to declare maintainers for projects. Also added default roles and changed so that review_users only contains overrides, so that changes to the project users is instantly applied to all reviews (unless there is an override). --- server/src/db_utils.rs | 138 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 server/src/db_utils.rs (limited to 'server/src/db_utils.rs') diff --git a/server/src/db_utils.rs b/server/src/db_utils.rs new file mode 100644 index 0000000..e74fd68 --- /dev/null +++ b/server/src/db_utils.rs @@ -0,0 +1,138 @@ +#![allow(dead_code)] + +use sqlx::database::HasArguments; +use sqlx::encode::Encode; +use sqlx::types::Type; +use sqlx::{Arguments, Database}; +use std::fmt::Display; + +pub struct UpdateBuilder<'args, DB> +where + DB: Database, +{ + table: Option, + names: Option>, + where_: Option>, + values: Option<>::Arguments>, +} + +impl<'args, DB: Database> UpdateBuilder<'args, DB> +where + DB: Database, +{ + pub fn new() -> Self + where + >::Arguments: Default, + { + UpdateBuilder { + table: None, + names: Some(Vec::new()), + where_: Some(Vec::new()), + values: Some(Default::default()), + } + } + + #[inline] + fn sanity_check(&self) { + assert!( + self.values.is_some(), + "UpdateBuilder must be reset before reuse after `.build()`" + ); + } + + pub fn table(&mut self, table: impl Display) -> &mut Self { + self.sanity_check(); + + self.table = Some(format!("{table}")); + + self + } + + pub fn and_where(&mut self, name: impl Display, op: impl Display, value: T) -> &mut Self + where + T: 'args + Encode<'args, DB> + Send + Type, + { + self.sanity_check(); + + let values = self.values.as_mut().expect("BUG: Values taken already"); + values.add(value); + + let mut placeholder = String::new(); + values + .format_placeholder(&mut placeholder) + .expect("error in format_placeholder"); + + let where_ = self.where_.as_mut().expect("BUG: Where taken already"); + where_.push((format!("{name}"), format!("{op}"), placeholder)); + + self + } + + pub fn set(&mut self, name: impl Display, value: T) -> &mut Self + where + T: 'args + Encode<'args, DB> + Send + Type, + { + self.sanity_check(); + + assert!( + self.where_.as_ref().unwrap().is_empty(), + "set must not be called after add_where" + ); + + let names = self.names.as_mut().expect("BUG: Names taken already"); + let values = self.values.as_mut().expect("BUG: Values taken already"); + values.add(value); + + let mut placeholder = String::new(); + values + .format_placeholder(&mut placeholder) + .expect("error in format_placeholder"); + names.push((format!("{name}"), placeholder)); + + self + } + + pub fn build(&mut self) -> (String, >::Arguments) { + self.sanity_check(); + + let table = self.table.take().unwrap(); + let mut query = format!("UPDATE {table} SET"); + let mut first = true; + for (name, placeholder) in self.names.take().unwrap() { + if first { + first = false; + } else { + query.push(','); + } + query.push_str(&format!(" {name}={placeholder}")); + } + + let where_ = self.where_.take().unwrap(); + if !where_.is_empty() { + query.push_str(" WHERE"); + + first = true; + for (name, op, placeholder) in where_ { + if first { + first = false; + } else { + query.push_str(" AND"); + } + query.push_str(&format!(" {name}{op}{placeholder}")); + } + } + + // TODO: This method should return a Query, constructed by QueryBuilder, but I can't + // figure out how to create QueryBuilder::with_arguments in this generic method, + // where DB isn't "known". + (query, self.values.take().unwrap()) + } + + pub fn reset(&mut self) -> &mut Self { + self.names = Some(Vec::new()); + self.where_ = Some(Vec::new()); + self.values = Some(Default::default()); + + self + } +} -- cgit v1.2.3-70-g09d2