summaryrefslogtreecommitdiff
path: root/server/src/db_utils.rs
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/db_utils.rs')
-rw-r--r--server/src/db_utils.rs138
1 files changed, 138 insertions, 0 deletions
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<String>,
+ names: Option<Vec<(String, String)>>,
+ where_: Option<Vec<(String, String, String)>>,
+ values: Option<<DB as HasArguments<'args>>::Arguments>,
+}
+
+impl<'args, DB: Database> UpdateBuilder<'args, DB>
+where
+ DB: Database,
+{
+ pub fn new() -> Self
+ where
+ <DB as HasArguments<'args>>::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<T>(&mut self, name: impl Display, op: impl Display, value: T) -> &mut Self
+ where
+ T: 'args + Encode<'args, DB> + Send + Type<DB>,
+ {
+ 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<T>(&mut self, name: impl Display, value: T) -> &mut Self
+ where
+ T: 'args + Encode<'args, DB> + Send + Type<DB>,
+ {
+ 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, <DB as HasArguments<'args>>::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
+ }
+}