From 03e43b1543c4328b5bb56b01f278cc8ce1e57112 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 9 Dec 2025 16:44:36 +0100 Subject: [PATCH 01/10] Implement copy on schema --- welds-macros/src/blocks/define_schema.rs | 1 + welds/src/query/builder/mod.rs | 2 +- welds/src/query/clause/basic.rs | 15 ++++++++------- welds/src/query/clause/basicopt.rs | 15 ++++++++------- welds/src/query/clause/mod.rs | 16 ++++++++-------- welds/src/query/clause/numeric.rs | 15 ++++++++------- welds/src/query/clause/numericopt.rs | 15 ++++++++------- welds/src/query/clause/text.rs | 16 +++++++++------- welds/src/query/clause/textopt.rs | 18 ++++++++++-------- welds/src/query/update/bulk/mod.rs | 2 +- 10 files changed, 62 insertions(+), 53 deletions(-) diff --git a/welds-macros/src/blocks/define_schema.rs b/welds-macros/src/blocks/define_schema.rs index 91276bb9..e647332e 100644 --- a/welds-macros/src/blocks/define_schema.rs +++ b/welds-macros/src/blocks/define_schema.rs @@ -23,6 +23,7 @@ pub(crate) fn write(info: &Info) -> TokenStream { quote! { + #[derive(Copy,Clone)] pub struct #name { #(#fields),* } diff --git a/welds/src/query/builder/mod.rs b/welds/src/query/builder/mod.rs index 48af6fcc..b7c72654 100644 --- a/welds/src/query/builder/mod.rs +++ b/welds/src/query/builder/mod.rs @@ -150,7 +150,7 @@ where FN: AsFieldName, { let field = col(Default::default()); - let colname = field.colname().to_string(); + let colname = field.colname(); let params: ManualParam = params.into(); let c = clause::ClauseColManual { col: Some(colname), diff --git a/welds/src/query/clause/basic.rs b/welds/src/query/clause/basic.rs index 7798ef2f..07a430e9 100644 --- a/welds/src/query/clause/basic.rs +++ b/welds/src/query/clause/basic.rs @@ -2,18 +2,19 @@ use super::{AsFieldName, ClauseColVal, ClauseColValEqual, ClauseColValIn}; use std::marker::PhantomData; use welds_connections::Param; +#[derive(Copy,Clone)] pub struct Basic { - col: String, - field: String, + col: &'static str, + field: &'static str, _t: PhantomData, } impl AsFieldName for Basic { - fn colname(&self) -> &str { - self.col.as_str() + fn colname(&self) -> &'static str { + self.col } - fn fieldname(&self) -> &str { - self.field.as_str() + fn fieldname(&self) -> &'static str { + self.field } } @@ -21,7 +22,7 @@ impl Basic where T: 'static + Clone + Send + Sync, { - pub fn new(col: impl Into, field: impl Into) -> Self { + pub fn new(col: &'static str, field: &'static str) -> Self { Self { col: col.into(), field: field.into(), diff --git a/welds/src/query/clause/basicopt.rs b/welds/src/query/clause/basicopt.rs index 97936c4f..a1deb4df 100644 --- a/welds/src/query/clause/basicopt.rs +++ b/welds/src/query/clause/basicopt.rs @@ -4,18 +4,19 @@ use crate::query::optional::Optional; use std::marker::PhantomData; use welds_connections::Param; +#[derive(Copy,Clone)] pub struct BasicOpt { - col: String, - field: String, + col: &'static str, + field: &'static str, _t: PhantomData, } impl AsFieldName for BasicOpt { - fn colname(&self) -> &str { - self.col.as_str() + fn colname(&self) -> &'static str { + self.col } - fn fieldname(&self) -> &str { - self.field.as_str() + fn fieldname(&self) -> &'static str { + self.field } } @@ -25,7 +26,7 @@ impl BasicOpt where T: 'static + Clone + Send + Sync, { - pub fn new(col: impl Into, field: impl Into) -> Self { + pub fn new(col: &'static str, field: &'static str) -> Self { Self { col: col.into(), field: field.into(), diff --git a/welds/src/query/clause/mod.rs b/welds/src/query/clause/mod.rs index 9f5e38f2..019610c5 100644 --- a/welds/src/query/clause/mod.rs +++ b/welds/src/query/clause/mod.rs @@ -35,7 +35,7 @@ pub use assignment_adder::AssignmentAdder; pub struct ClauseColVal { pub null_clause: bool, pub not_clause: bool, - pub col: String, + pub col: &'static str, pub operator: &'static str, pub val: Option, } @@ -43,31 +43,31 @@ pub struct ClauseColVal { pub struct ClauseColValEqual { pub null_clause: bool, pub not_clause: bool, - pub col: String, + pub col: &'static str, pub operator: &'static str, pub val: Option, } pub struct ClauseColValIn { - pub col: String, + pub col: &'static str, pub operator: &'static str, pub list: Vec, } pub struct ClauseColValList { - pub col: String, + pub col: &'static str, pub operator: &'static str, pub list: Vec, } pub struct ClauseColManual { - pub(crate) col: Option, + pub(crate) col: Option<&'static str>, pub(crate) sql: String, pub(crate) params: Vec>, } pub struct AssignmentManual { - pub(crate) col: String, + pub(crate) col: &'static str, pub(crate) sql: String, pub(crate) params: Vec>, } @@ -77,8 +77,8 @@ pub struct AssignmentManual { // fieldname refers to what we want to get the column out as. // for example: select id as ids from bla. pub trait AsFieldName { - fn colname(&self) -> &str; - fn fieldname(&self) -> &str; + fn colname(&self) -> &'static str; + fn fieldname(&self) -> &'static str; } // marker trait to make sure a field is nullable diff --git a/welds/src/query/clause/numeric.rs b/welds/src/query/clause/numeric.rs index cb28d10f..d159cef3 100644 --- a/welds/src/query/clause/numeric.rs +++ b/welds/src/query/clause/numeric.rs @@ -5,18 +5,19 @@ use std::marker::PhantomData; use welds_connections::Param; /// Clauses for numeric types such as int, float, etc +#[derive(Copy,Clone)] pub struct Numeric { - col: String, - field: String, + col: &'static str, + field: &'static str, _t: PhantomData, } impl AsFieldName for Numeric { - fn colname(&self) -> &str { - self.col.as_str() + fn colname(&self) -> &'static str { + self.col } - fn fieldname(&self) -> &str { - self.field.as_str() + fn fieldname(&self) -> &'static str { + self.field } } @@ -24,7 +25,7 @@ impl Numeric where T: 'static + Clone + Send + Sync, { - pub fn new(col: impl Into, field: impl Into) -> Self { + pub fn new(col: &'static str, field: &'static str) -> Self { Self { col: col.into(), field: field.into(), diff --git a/welds/src/query/clause/numericopt.rs b/welds/src/query/clause/numericopt.rs index c722bcd7..561021ba 100644 --- a/welds/src/query/clause/numericopt.rs +++ b/welds/src/query/clause/numericopt.rs @@ -6,18 +6,19 @@ use crate::query::optional::Optional; use std::marker::PhantomData; use welds_connections::Param; +#[derive(Copy,Clone)] pub struct NumericOpt { - col: String, - field: String, + col: &'static str, + field: &'static str, _t: PhantomData, } impl AsFieldName for NumericOpt { - fn colname(&self) -> &str { - self.col.as_str() + fn colname(&self) -> &'static str { + self.col } - fn fieldname(&self) -> &str { - self.field.as_str() + fn fieldname(&self) -> &'static str { + self.field } } @@ -27,7 +28,7 @@ impl NumericOpt where T: 'static + Clone + Send + Sync, { - pub fn new(col: impl Into, field: impl Into) -> Self { + pub fn new(col: &'static str, field: &'static str) -> Self { Self { col: col.into(), field: field.into(), diff --git a/welds/src/query/clause/text.rs b/welds/src/query/clause/text.rs index 49639a44..0d262664 100644 --- a/welds/src/query/clause/text.rs +++ b/welds/src/query/clause/text.rs @@ -2,26 +2,28 @@ use super::{AsFieldName, ClauseColVal, ClauseColValEqual, ClauseColValIn}; use std::marker::PhantomData; use welds_connections::Param; +#[derive(Clone)] pub struct Text { - col: String, - field: String, + col: &'static str, + field: &'static str, _t: PhantomData, } impl AsFieldName for Text { - fn colname(&self) -> &str { - self.col.as_str() + fn colname(&self) -> &'static str { + self.col } - fn fieldname(&self) -> &str { - self.field.as_str() + fn fieldname(&self) -> &'static str { + self.field } } +impl Copy for Text {} impl Text where T: 'static + Clone + Send + Sync, { - pub fn new(col: impl Into, field: impl Into) -> Self { + pub fn new(col: &'static str, field: &'static str) -> Self { Self { col: col.into(), field: field.into(), diff --git a/welds/src/query/clause/textopt.rs b/welds/src/query/clause/textopt.rs index 6f6bbfe4..81373352 100644 --- a/welds/src/query/clause/textopt.rs +++ b/welds/src/query/clause/textopt.rs @@ -1,23 +1,25 @@ -use super::{AsFieldName, AsOptField, ClauseColVal, ClauseColValEqual, ClauseColValIn}; +use super::{AsFieldName, AsOptField, ClauseColVal, ClauseColValEqual, ClauseColValIn, Text}; use crate::query::optional::HasSomeNone; use crate::query::optional::Optional; use std::marker::PhantomData; use welds_connections::Param; +#[derive(Clone)] pub struct TextOpt { - col: String, - field: String, + col: &'static str, + field: &'static str, _t: PhantomData, } impl AsFieldName for TextOpt { - fn colname(&self) -> &str { - self.col.as_str() + fn colname(&self) -> &'static str { + self.col } - fn fieldname(&self) -> &str { - self.field.as_str() + fn fieldname(&self) -> &'static str { + self.field } } +impl Copy for TextOpt {} impl AsOptField for TextOpt {} @@ -25,7 +27,7 @@ impl TextOpt where T: 'static + Clone + Send + Sync, { - pub fn new(col: impl Into, field: impl Into) -> Self { + pub fn new(col: &'static str, field: &'static str) -> Self { Self { col: col.into(), field: field.into(), diff --git a/welds/src/query/update/bulk/mod.rs b/welds/src/query/update/bulk/mod.rs index 23495a8c..f3cb76c9 100644 --- a/welds/src/query/update/bulk/mod.rs +++ b/welds/src/query/update/bulk/mod.rs @@ -168,7 +168,7 @@ where { let params: ManualParam = params.into(); let field = lam(Default::default()); - let col_raw = field.colname().to_string(); + let col_raw = field.colname(); let adder = AssignmentManual { col: col_raw, From 53053e1030c88695274cb49475e59e0fc3f44d46 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 9 Dec 2025 16:47:55 +0100 Subject: [PATCH 02/10] fix --- welds/src/query/clause/basic.rs | 5 +++-- welds/src/query/clause/basicopt.rs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/welds/src/query/clause/basic.rs b/welds/src/query/clause/basic.rs index 07a430e9..bdaacf26 100644 --- a/welds/src/query/clause/basic.rs +++ b/welds/src/query/clause/basic.rs @@ -1,8 +1,8 @@ -use super::{AsFieldName, ClauseColVal, ClauseColValEqual, ClauseColValIn}; +use super::{AsFieldName, ClauseColVal, ClauseColValEqual, ClauseColValIn, Text}; use std::marker::PhantomData; use welds_connections::Param; -#[derive(Copy,Clone)] +#[derive(Clone)] pub struct Basic { col: &'static str, field: &'static str, @@ -17,6 +17,7 @@ impl AsFieldName for Basic { self.field } } +impl Copy for Basic {} impl Basic where diff --git a/welds/src/query/clause/basicopt.rs b/welds/src/query/clause/basicopt.rs index a1deb4df..ffee673c 100644 --- a/welds/src/query/clause/basicopt.rs +++ b/welds/src/query/clause/basicopt.rs @@ -1,10 +1,10 @@ -use super::{AsFieldName, AsOptField, ClauseColVal, ClauseColValEqual, ClauseColValIn}; +use super::{AsFieldName, AsOptField, ClauseColVal, ClauseColValEqual, ClauseColValIn, Text}; use crate::query::optional::HasSomeNone; use crate::query::optional::Optional; use std::marker::PhantomData; use welds_connections::Param; -#[derive(Copy,Clone)] +#[derive(Clone)] pub struct BasicOpt { col: &'static str, field: &'static str, @@ -19,6 +19,7 @@ impl AsFieldName for BasicOpt { self.field } } +impl Copy for BasicOpt {} impl AsOptField for BasicOpt {} From 0c2d0a0fbb38a7566ed153a5c2f424dd3d1ae0bd Mon Sep 17 00:00:00 2001 From: = Date: Tue, 9 Dec 2025 17:17:29 +0100 Subject: [PATCH 03/10] Try BitOr BitAnd (don't works) --- welds/src/query/clause/basic.rs | 2 +- welds/src/query/clause/basicopt.rs | 2 +- welds/src/query/clause/clause_adder.rs | 18 +++++++ welds/src/query/clause/mod.rs | 2 + welds/src/query/clause/or_and.rs | 67 ++++++++++++++++++++++++++ welds/src/query/clause/textopt.rs | 2 +- 6 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 welds/src/query/clause/or_and.rs diff --git a/welds/src/query/clause/basic.rs b/welds/src/query/clause/basic.rs index bdaacf26..5c4d1946 100644 --- a/welds/src/query/clause/basic.rs +++ b/welds/src/query/clause/basic.rs @@ -1,4 +1,4 @@ -use super::{AsFieldName, ClauseColVal, ClauseColValEqual, ClauseColValIn, Text}; +use super::{AsFieldName, ClauseColVal, ClauseColValEqual, ClauseColValIn}; use std::marker::PhantomData; use welds_connections::Param; diff --git a/welds/src/query/clause/basicopt.rs b/welds/src/query/clause/basicopt.rs index ffee673c..b4dffd75 100644 --- a/welds/src/query/clause/basicopt.rs +++ b/welds/src/query/clause/basicopt.rs @@ -1,4 +1,4 @@ -use super::{AsFieldName, AsOptField, ClauseColVal, ClauseColValEqual, ClauseColValIn, Text}; +use super::{AsFieldName, AsOptField, ClauseColVal, ClauseColValEqual, ClauseColValIn}; use crate::query::optional::HasSomeNone; use crate::query::optional::Optional; use std::marker::PhantomData; diff --git a/welds/src/query/clause/clause_adder.rs b/welds/src/query/clause/clause_adder.rs index 259bdfe0..9ae11fa6 100644 --- a/welds/src/query/clause/clause_adder.rs +++ b/welds/src/query/clause/clause_adder.rs @@ -1,3 +1,4 @@ +use crate::query::clause::or_and::LogicalClause; use super::{ClauseColManual, ClauseColVal, ClauseColValEqual, ClauseColValIn, ClauseColValList}; use super::{Param, ParamArgs}; use crate::Syntax; @@ -201,3 +202,20 @@ impl ClauseAdder for ClauseColManual { Some(clause) } } + +impl std::ops::BitOr for Box +{ + type Output = Box; + + fn bitor(self, rhs: Box) -> Box { + Box::new(LogicalClause::or(self,rhs)) + } +} + +impl std::ops::BitAnd for Box { + type Output = Box; + + fn bitand(self, rhs: Box) -> Box { + Box::new(LogicalClause::and(self,rhs)) + } +} \ No newline at end of file diff --git a/welds/src/query/clause/mod.rs b/welds/src/query/clause/mod.rs index 019610c5..7f211c2e 100644 --- a/welds/src/query/clause/mod.rs +++ b/welds/src/query/clause/mod.rs @@ -30,6 +30,8 @@ pub use clause_adder::ClauseAdder; // trait used to write assignments in a sql statement mod assignment_adder; +mod or_and; + pub use assignment_adder::AssignmentAdder; pub struct ClauseColVal { diff --git a/welds/src/query/clause/or_and.rs b/welds/src/query/clause/or_and.rs new file mode 100644 index 00000000..2f5fce90 --- /dev/null +++ b/welds/src/query/clause/or_and.rs @@ -0,0 +1,67 @@ +use welds_connections::Syntax; +use crate::query::clause::{ClauseAdder, ParamArgs}; +use crate::writers::NextParam; + +enum LogicalOp { + And, + Or, +} + +impl LogicalOp { + pub fn to_str(&self) -> &'static str + { + match self { + LogicalOp::And => "AND", + LogicalOp::Or => "OR", + } + } +} + +pub struct LogicalClause { + left_clause: Box, + operator: LogicalOp, + right_clause: Box, +} + +impl LogicalClause { + pub fn or( + left_clause: Box, + right_clause: Box, + ) -> Self { + Self { + left_clause, + operator: LogicalOp::Or, + right_clause, + } + } + + pub fn and( + left_clause: Box, + right_clause: Box, + ) -> Self { + Self { + left_clause, + operator: LogicalOp::And, + right_clause, + } + } +} + +impl ClauseAdder for LogicalClause { + fn bind<'lam, 'args, 'p>(&'lam self, args: &'args mut ParamArgs<'p>) + where + 'lam: 'p, + { + self.left_clause.bind(args); + self.right_clause.bind(args); + } + + fn clause(&self, syntax: Syntax, alias: &str, next_params: &NextParam) -> Option { + + format!("({} {} {}", + self.left_clause.clause(syntax, alias, next_params)?, + self.operator.to_str(), + self.right_clause.clause(syntax, alias, next_params)?, + ).into() + } +} diff --git a/welds/src/query/clause/textopt.rs b/welds/src/query/clause/textopt.rs index 81373352..3963b697 100644 --- a/welds/src/query/clause/textopt.rs +++ b/welds/src/query/clause/textopt.rs @@ -1,4 +1,4 @@ -use super::{AsFieldName, AsOptField, ClauseColVal, ClauseColValEqual, ClauseColValIn, Text}; +use super::{AsFieldName, AsOptField, ClauseColVal, ClauseColValEqual, ClauseColValIn}; use crate::query::optional::HasSomeNone; use crate::query::optional::Optional; use std::marker::PhantomData; From 8b7cb323e66d83fe88dcb0e73acc20a600255d59 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 9 Dec 2025 17:43:20 +0100 Subject: [PATCH 04/10] Fix 2nd solution with or / and function for weldsorm#158 --- welds-macros/src/blocks/define_schema.rs | 1 + welds-macros/src/relation/basic.rs | 2 +- welds/src/query/clause/clause_adder.rs | 17 ------------ welds/src/query/clause/mod.rs | 13 ++++++++++ welds/src/query/clause/or_and.rs | 33 +++++++++++++++++------- 5 files changed, 38 insertions(+), 28 deletions(-) diff --git a/welds-macros/src/blocks/define_schema.rs b/welds-macros/src/blocks/define_schema.rs index e647332e..58c20797 100644 --- a/welds-macros/src/blocks/define_schema.rs +++ b/welds-macros/src/blocks/define_schema.rs @@ -76,6 +76,7 @@ mod tests { let code = ts.to_string(); let expected: &str = r#" + #[derive(Copy,Clone)] pub struct MockSchema { pub id: welds::query::clause::Numeric } diff --git a/welds-macros/src/relation/basic.rs b/welds-macros/src/relation/basic.rs index 23929835..3dbd6fcb 100644 --- a/welds-macros/src/relation/basic.rs +++ b/welds-macros/src/relation/basic.rs @@ -1,7 +1,7 @@ use super::Relation; use super::{read_as_ident, read_as_path, read_as_string}; use crate::errors::Result; -use syn::{Expr, Ident, Token}; +use syn::{Expr, Ident}; use syn::MetaList; use syn::parse::Parser; use syn::punctuated::Punctuated; diff --git a/welds/src/query/clause/clause_adder.rs b/welds/src/query/clause/clause_adder.rs index 9ae11fa6..e3b8a6af 100644 --- a/welds/src/query/clause/clause_adder.rs +++ b/welds/src/query/clause/clause_adder.rs @@ -1,4 +1,3 @@ -use crate::query::clause::or_and::LogicalClause; use super::{ClauseColManual, ClauseColVal, ClauseColValEqual, ClauseColValIn, ClauseColValList}; use super::{Param, ParamArgs}; use crate::Syntax; @@ -203,19 +202,3 @@ impl ClauseAdder for ClauseColManual { } } -impl std::ops::BitOr for Box -{ - type Output = Box; - - fn bitor(self, rhs: Box) -> Box { - Box::new(LogicalClause::or(self,rhs)) - } -} - -impl std::ops::BitAnd for Box { - type Output = Box; - - fn bitand(self, rhs: Box) -> Box { - Box::new(LogicalClause::and(self,rhs)) - } -} \ No newline at end of file diff --git a/welds/src/query/clause/mod.rs b/welds/src/query/clause/mod.rs index 7f211c2e..d2616f64 100644 --- a/welds/src/query/clause/mod.rs +++ b/welds/src/query/clause/mod.rs @@ -31,6 +31,8 @@ pub use clause_adder::ClauseAdder; // trait used to write assignments in a sql statement mod assignment_adder; mod or_and; +pub use or_and::or; +pub use or_and::and; pub use assignment_adder::AssignmentAdder; @@ -96,3 +98,14 @@ pub struct SetColVal { pub struct SetColNull { pub col_raw: String, } + +enum LogicalOp { + And, + Or, +} + +pub struct LogicalClause { + left_clause: Box, + operator: LogicalOp, + right_clause: Box, +} \ No newline at end of file diff --git a/welds/src/query/clause/or_and.rs b/welds/src/query/clause/or_and.rs index 2f5fce90..8bf9328c 100644 --- a/welds/src/query/clause/or_and.rs +++ b/welds/src/query/clause/or_and.rs @@ -1,11 +1,7 @@ use welds_connections::Syntax; -use crate::query::clause::{ClauseAdder, ParamArgs}; +use crate::query::clause::{ClauseAdder, LogicalClause, LogicalOp, ParamArgs}; use crate::writers::NextParam; -enum LogicalOp { - And, - Or, -} impl LogicalOp { pub fn to_str(&self) -> &'static str @@ -17,11 +13,6 @@ impl LogicalOp { } } -pub struct LogicalClause { - left_clause: Box, - operator: LogicalOp, - right_clause: Box, -} impl LogicalClause { pub fn or( @@ -47,6 +38,28 @@ impl LogicalClause { } } +pub fn or( + left_clause: Box, + right_clause: Box, +) -> LogicalClause { + LogicalClause { + left_clause, + operator: LogicalOp::Or, + right_clause, + } +} + +pub fn and( + left_clause: Box, + right_clause: Box, +) -> LogicalClause { + LogicalClause { + left_clause, + operator: LogicalOp::Or, + right_clause, + } +} + impl ClauseAdder for LogicalClause { fn bind<'lam, 'args, 'p>(&'lam self, args: &'args mut ParamArgs<'p>) where From e8713e7f3a5340c1e3168101a22ccbeef97d816d Mon Sep 17 00:00:00 2001 From: = Date: Fri, 12 Dec 2025 23:44:18 +0100 Subject: [PATCH 05/10] Fix of the previous implementation --- welds/src/query/clause/or_and.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/welds/src/query/clause/or_and.rs b/welds/src/query/clause/or_and.rs index 8bf9328c..1b1a60af 100644 --- a/welds/src/query/clause/or_and.rs +++ b/welds/src/query/clause/or_and.rs @@ -41,23 +41,23 @@ impl LogicalClause { pub fn or( left_clause: Box, right_clause: Box, -) -> LogicalClause { - LogicalClause { +) -> Box { + Box::new(LogicalClause { left_clause, operator: LogicalOp::Or, right_clause, - } + }) } pub fn and( left_clause: Box, right_clause: Box, -) -> LogicalClause { - LogicalClause { +) -> Box { + Box::new(LogicalClause { left_clause, - operator: LogicalOp::Or, + operator: LogicalOp::And, right_clause, - } + }) } impl ClauseAdder for LogicalClause { @@ -71,7 +71,7 @@ impl ClauseAdder for LogicalClause { fn clause(&self, syntax: Syntax, alias: &str, next_params: &NextParam) -> Option { - format!("({} {} {}", + format!("({} {} {})", self.left_clause.clause(syntax, alias, next_params)?, self.operator.to_str(), self.right_clause.clause(syntax, alias, next_params)?, From 6b24cea52f6a7da7dab4060160bc3c04eb3e5c2a Mon Sep 17 00:00:00 2001 From: = Date: Sat, 13 Dec 2025 00:08:40 +0100 Subject: [PATCH 06/10] Add chained or / and condition --- welds/src/query/clause/mod.rs | 1 + welds/src/query/clause/or_and.rs | 100 ++++++++++++++++++++++--------- 2 files changed, 74 insertions(+), 27 deletions(-) diff --git a/welds/src/query/clause/mod.rs b/welds/src/query/clause/mod.rs index d2616f64..81db1fde 100644 --- a/welds/src/query/clause/mod.rs +++ b/welds/src/query/clause/mod.rs @@ -33,6 +33,7 @@ mod assignment_adder; mod or_and; pub use or_and::or; pub use or_and::and; +pub use or_and::AndOrClauseTrait; pub use assignment_adder::AssignmentAdder; diff --git a/welds/src/query/clause/or_and.rs b/welds/src/query/clause/or_and.rs index 1b1a60af..30ab492a 100644 --- a/welds/src/query/clause/or_and.rs +++ b/welds/src/query/clause/or_and.rs @@ -1,5 +1,5 @@ -use welds_connections::Syntax; -use crate::query::clause::{ClauseAdder, LogicalClause, LogicalOp, ParamArgs}; +use welds_connections::{Param, Syntax}; +use crate::query::clause::{ClauseAdder, ClauseColManual, ClauseColVal, ClauseColValEqual, ClauseColValIn, ClauseColValList, LogicalClause, LogicalOp, ParamArgs}; use crate::writers::NextParam; @@ -13,31 +13,6 @@ impl LogicalOp { } } - -impl LogicalClause { - pub fn or( - left_clause: Box, - right_clause: Box, - ) -> Self { - Self { - left_clause, - operator: LogicalOp::Or, - right_clause, - } - } - - pub fn and( - left_clause: Box, - right_clause: Box, - ) -> Self { - Self { - left_clause, - operator: LogicalOp::And, - right_clause, - } - } -} - pub fn or( left_clause: Box, right_clause: Box, @@ -78,3 +53,74 @@ impl ClauseAdder for LogicalClause { ).into() } } + +pub trait AndOrClauseTrait { + fn and(self: Box, other: Box) -> Box; + fn or(self: Box, other: Box) -> Box; +} + +impl AndOrClauseTrait for ClauseColVal +where + for<'a> T: 'a, + T: Clone + Send + Sync + Param, +{ + fn and(self: Box, other: Box) -> Box { + and(self, other) + } + + fn or(self: Box, other: Box) -> Box { + or(self, other) + } +} + +impl AndOrClauseTrait for ClauseColValEqual +where + for<'a> T: 'a, + T: Clone + Send + Sync + Param, +{ + fn and(self: Box, other: Box) -> Box { + and(self, other) + } + + fn or(self: Box, other: Box) -> Box { + or(self, other) + } +} + +impl AndOrClauseTrait for ClauseColValList< T > +where + for<'a> T: 'a, + Vec: Clone + Send + Sync + Param, +{ + fn and(self: Box, other: Box) -> Box { + and(self, other) + } + + fn or(self: Box, other: Box) -> Box { + or(self, other) + } +} + +impl AndOrClauseTrait for ClauseColValIn +where + for<'a> T: 'a + Clone + Send + Sync + Param, +{ + fn and(self: Box, other: Box) -> Box { + and(self, other) + } + + fn or(self: Box, other: Box) -> Box { + or(self, other) + } +} + +impl AndOrClauseTrait for ClauseColManual { + fn and(self: Box, other: Box) -> Box { + and(self, other) + } + + fn or(self: Box, other: Box) -> Box { + or(self, other) + } +} + From 198b913e667d913fc8ec2705c04c4222de93f17b Mon Sep 17 00:00:00 2001 From: = Date: Sat, 13 Dec 2025 09:01:34 +0100 Subject: [PATCH 07/10] Add unit tests. --- welds/src/query/clause/or_and.rs | 84 ++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/welds/src/query/clause/or_and.rs b/welds/src/query/clause/or_and.rs index 30ab492a..6c99d18d 100644 --- a/welds/src/query/clause/or_and.rs +++ b/welds/src/query/clause/or_and.rs @@ -59,6 +59,17 @@ pub trait AndOrClauseTrait { fn or(self: Box, other: Box) -> Box; } +impl AndOrClauseTrait for LogicalClause +{ + fn and(self: Box, other: Box) -> Box { + and(self, other) + } + + fn or(self: Box, other: Box) -> Box { + or(self, other) + } +} + impl AndOrClauseTrait for ClauseColVal where for<'a> T: 'a, @@ -124,3 +135,76 @@ impl AndOrClauseTrait for ClauseColManual { } } +#[cfg(test)] +mod tests { + use super::*; + use crate::WeldsModel; + use welds_connections::Syntax; + + #[derive(Debug, Default, WeldsModel)] + #[welds(table = "test_table")] + #[welds_path(crate)] + struct TestModel { + #[welds(primary_key)] + pub id: i32, + #[welds(rename = "name_column")] + pub name: String, + pub is_active: bool, + pub score: f64, + } + + #[test] + fn test_and_logical_clause() { + let a = TestModelSchema::default(); + + let and_clause = and(a.id.equal(1), a.is_active.equal(true)); + + let sql = and_clause.clause(Syntax::Postgres, "t1", &NextParam::new(Syntax::Postgres)); + assert!(sql.is_some()); + assert_eq!(sql.unwrap(), "(t1.id = $1 AND t1.is_active = $2)"); + } + + #[test] + fn test_or_logical_clause() { + let a = TestModelSchema::default(); + + let or_clause = or(a.score.gt(0.5), a.name.equal("test".to_string())); + + let sql = or_clause.clause(Syntax::Postgres, "t1", &NextParam::new(Syntax::Postgres)); + assert!(sql.is_some()); + assert_eq!(sql.unwrap(), "(t1.score > $1 OR t1.name_column = $2)"); + } + + #[test] + fn test_nested_logical_clauses() { + let a = TestModelSchema::default(); + + // (id = 1 AND is_active = true) OR score > 0.5 + let and_clause = and(a.id.equal(1), a.is_active.equal(true)); + let nested = or(and_clause, a.score.gte(0.5)); + + let sql = nested.clause(Syntax::Postgres, "t1", &NextParam::new(Syntax::Postgres)); + assert!(sql.is_some()); + let sql_str = sql.unwrap(); + assert!(sql_str.contains("AND")); + assert!(sql_str.contains("OR")); + assert_eq!(sql_str, "((t1.id = $1 AND t1.is_active = $2) OR t1.score >= $3)"); + } + + #[test] + fn test_nested_linked_logical_clauses() { + let a = TestModelSchema::default(); + + // ((id != 0 OR name_column == 'empty) AND is_active != true) OR score > 0.5 + let or_clause = a.id.not_equal(0).or( a.name.equal("empty")); + let and_clause = or_clause.and( a.is_active.not_equal(true)); + let nested = and_clause.or( a.score.gte(0.5)); + + let sql = nested.clause(Syntax::Postgres, "t1", &NextParam::new(Syntax::Postgres)); + assert!(sql.is_some()); + let sql_str = sql.unwrap(); + assert!(sql_str.contains("AND")); + assert!(sql_str.contains("OR")); + assert_eq!(sql_str, "(((t1.id != $1 OR t1.name_column = $2) AND t1.is_active != $3) OR t1.score >= $4)"); + } +} From 3a2681aafea943ccfe8ff789ae39f7708b677c0b Mon Sep 17 00:00:00 2001 From: Lex Childs Date: Sat, 13 Dec 2025 11:40:13 -0600 Subject: [PATCH 08/10] rustfmt and moved to unstable-api --- welds/src/query/clause/mod.rs | 11 ++++-- welds/src/query/clause/or_and.rs | 68 +++++++++++++++++++++----------- 2 files changed, 52 insertions(+), 27 deletions(-) diff --git a/welds/src/query/clause/mod.rs b/welds/src/query/clause/mod.rs index 81db1fde..805d53c7 100644 --- a/welds/src/query/clause/mod.rs +++ b/welds/src/query/clause/mod.rs @@ -30,10 +30,15 @@ pub use clause_adder::ClauseAdder; // trait used to write assignments in a sql statement mod assignment_adder; + +#[cfg(feature = "unstable-api")] mod or_and; -pub use or_and::or; -pub use or_and::and; +#[cfg(feature = "unstable-api")] pub use or_and::AndOrClauseTrait; +#[cfg(feature = "unstable-api")] +pub use or_and::and; +#[cfg(feature = "unstable-api")] +pub use or_and::or; pub use assignment_adder::AssignmentAdder; @@ -109,4 +114,4 @@ pub struct LogicalClause { left_clause: Box, operator: LogicalOp, right_clause: Box, -} \ No newline at end of file +} diff --git a/welds/src/query/clause/or_and.rs b/welds/src/query/clause/or_and.rs index 6c99d18d..692e9863 100644 --- a/welds/src/query/clause/or_and.rs +++ b/welds/src/query/clause/or_and.rs @@ -1,11 +1,12 @@ -use welds_connections::{Param, Syntax}; -use crate::query::clause::{ClauseAdder, ClauseColManual, ClauseColVal, ClauseColValEqual, ClauseColValIn, ClauseColValList, LogicalClause, LogicalOp, ParamArgs}; +use crate::query::clause::{ + ClauseAdder, ClauseColManual, ClauseColVal, ClauseColValEqual, ClauseColValIn, + ClauseColValList, LogicalClause, LogicalOp, ParamArgs, +}; use crate::writers::NextParam; - +use welds_connections::{Param, Syntax}; impl LogicalOp { - pub fn to_str(&self) -> &'static str - { + pub fn to_str(&self) -> &'static str { match self { LogicalOp::And => "AND", LogicalOp::Or => "OR", @@ -45,12 +46,26 @@ impl ClauseAdder for LogicalClause { } fn clause(&self, syntax: Syntax, alias: &str, next_params: &NextParam) -> Option { - - format!("({} {} {})", - self.left_clause.clause(syntax, alias, next_params)?, - self.operator.to_str(), - self.right_clause.clause(syntax, alias, next_params)?, - ).into() + let left = self.left_clause.clause(syntax, alias, next_params); + let right = self.right_clause.clause(syntax, alias, next_params); + let operator = self.operator.to_str(); + + // Both have some + if let Some(left) = &left + && let Some(right) = &right + { + return Some(format!("({left} {operator} {right})")); + } + // Left has some + if left.is_some() { + return left; + } + // Right has some + if right.is_some() { + return right; + } + // Both are none + None } } @@ -59,8 +74,7 @@ pub trait AndOrClauseTrait { fn or(self: Box, other: Box) -> Box; } -impl AndOrClauseTrait for LogicalClause -{ +impl AndOrClauseTrait for LogicalClause { fn and(self: Box, other: Box) -> Box { and(self, other) } @@ -72,8 +86,8 @@ impl AndOrClauseTrait for LogicalClause impl AndOrClauseTrait for ClauseColVal where - for<'a> T: 'a, - T: Clone + Send + Sync + Param, + for<'a> T: 'a, + T: Clone + Send + Sync + Param, { fn and(self: Box, other: Box) -> Box { and(self, other) @@ -86,7 +100,7 @@ where impl AndOrClauseTrait for ClauseColValEqual where - for<'a> T: 'a, + for<'a> T: 'a, T: Clone + Send + Sync + Param, { fn and(self: Box, other: Box) -> Box { @@ -98,10 +112,10 @@ where } } -impl AndOrClauseTrait for ClauseColValList< T > +impl AndOrClauseTrait for ClauseColValList where - for<'a> T: 'a, - Vec: Clone + Send + Sync + Param, + for<'a> T: 'a, + Vec: Clone + Send + Sync + Param, { fn and(self: Box, other: Box) -> Box { and(self, other) @@ -188,7 +202,10 @@ mod tests { let sql_str = sql.unwrap(); assert!(sql_str.contains("AND")); assert!(sql_str.contains("OR")); - assert_eq!(sql_str, "((t1.id = $1 AND t1.is_active = $2) OR t1.score >= $3)"); + assert_eq!( + sql_str, + "((t1.id = $1 AND t1.is_active = $2) OR t1.score >= $3)" + ); } #[test] @@ -196,15 +213,18 @@ mod tests { let a = TestModelSchema::default(); // ((id != 0 OR name_column == 'empty) AND is_active != true) OR score > 0.5 - let or_clause = a.id.not_equal(0).or( a.name.equal("empty")); - let and_clause = or_clause.and( a.is_active.not_equal(true)); - let nested = and_clause.or( a.score.gte(0.5)); + let or_clause = a.id.not_equal(0).or(a.name.equal("empty")); + let and_clause = or_clause.and(a.is_active.not_equal(true)); + let nested = and_clause.or(a.score.gte(0.5)); let sql = nested.clause(Syntax::Postgres, "t1", &NextParam::new(Syntax::Postgres)); assert!(sql.is_some()); let sql_str = sql.unwrap(); assert!(sql_str.contains("AND")); assert!(sql_str.contains("OR")); - assert_eq!(sql_str, "(((t1.id != $1 OR t1.name_column = $2) AND t1.is_active != $3) OR t1.score >= $4)"); + assert_eq!( + sql_str, + "(((t1.id != $1 OR t1.name_column = $2) AND t1.is_active != $3) OR t1.score >= $4)" + ); } } From 3477ade3e33d397ba3f68687492441662ca242fc Mon Sep 17 00:00:00 2001 From: Lex Childs Date: Sun, 14 Dec 2025 07:55:59 -0600 Subject: [PATCH 09/10] fix: added integration tests --- tests/mssql/tests/all_tests.rs | 21 ++++ tests/mysql/tests/all_tests.rs | 23 ++++ tests/postgres/tests/all_tests.rs | 23 ++++ tests/sqlite/tests/all_tests.rs | 23 ++++ welds/src/query/clause/mod.rs | 2 +- welds/src/query/clause/or_and.rs | 200 ++++++++++++++++-------------- 6 files changed, 196 insertions(+), 96 deletions(-) diff --git a/tests/mssql/tests/all_tests.rs b/tests/mssql/tests/all_tests.rs index cc54520b..1f988197 100644 --- a/tests/mssql/tests/all_tests.rs +++ b/tests/mssql/tests/all_tests.rs @@ -349,3 +349,24 @@ async fn should_be_able_to_write_mapquery_with_a_column_rename() { eprintln!("SQL: {}", sql); q.run(&conn).await.unwrap(); } + +#[tokio::test] +async fn should_be_able_to_select_hourse_or_dog() { + use welds::query::clause::or; + let conn = get_conn().await; + use mssql_test::models::product::ProductSchema; + + // verify pulling out lambda into variable + let clause = |x: ProductSchema| or(x.name.like("horse"), x.name.like("dog")); + let q = Product::all().where_col(clause); + + eprintln!("SQL: {}", q.to_sql(Syntax::Sqlite)); + let data = q.run(&conn).await.unwrap(); + assert_eq!(data.len(), 2, "Expected horse and dog",); + + // verify inline clause + let q2 = Product::all().where_col(|x| or(x.name.like("horse"), x.name.like("dog"))); + eprintln!("SQL: {}", q2.to_sql(Syntax::Sqlite)); + let data = q2.run(&conn).await.unwrap(); + assert_eq!(data.len(), 2, "Expected horse and dog",); +} diff --git a/tests/mysql/tests/all_tests.rs b/tests/mysql/tests/all_tests.rs index effb2827..f2e6038b 100644 --- a/tests/mysql/tests/all_tests.rs +++ b/tests/mysql/tests/all_tests.rs @@ -374,3 +374,26 @@ fn should_be_able_to_write_a_custom_set() { q.run(&conn).await.unwrap(); }) } + +#[test] +fn should_be_able_to_select_hourse_or_dog() { + async_std::task::block_on(async { + use welds::query::clause::or; + let conn = get_conn().await; + use mysql_test::models::product::ProductSchema; + + // verify pulling out lambda into variable + let clause = |x: ProductSchema| or(x.name.like("horse"), x.name.like("dog")); + let q = Product::all().where_col(clause); + + eprintln!("SQL: {}", q.to_sql(Syntax::Mysql)); + let data = q.run(&conn).await.unwrap(); + assert_eq!(data.len(), 2, "Expected horse and dog",); + + // verify inline clause + let q2 = Product::all().where_col(|x| or(x.name.like("horse"), x.name.like("dog"))); + eprintln!("SQL: {}", q2.to_sql(Syntax::Mysql)); + let data = q2.run(&conn).await.unwrap(); + assert_eq!(data.len(), 2, "Expected horse and dog",); + }) +} diff --git a/tests/postgres/tests/all_tests.rs b/tests/postgres/tests/all_tests.rs index 94a8fc6b..c5e12f28 100644 --- a/tests/postgres/tests/all_tests.rs +++ b/tests/postgres/tests/all_tests.rs @@ -786,3 +786,26 @@ fn should_be_able_to_select_all_orders_with_there_products() { assert_eq!(o3_products[0].product_id, 1); }) } + +#[test] +fn should_be_able_to_select_hourse_or_dog() { + async_std::task::block_on(async { + use welds::query::clause::or; + let conn = get_conn().await; + use postgres_test::models::product::ProductSchema; + + // verify pulling out lambda into variable + let clause = |x: ProductSchema| or(x.name.like("horse"), x.name.like("dog")); + let q = Product::all().where_col(clause); + + eprintln!("SQL: {}", q.to_sql(Syntax::Postgres)); + let data = q.run(&conn).await.unwrap(); + assert_eq!(data.len(), 2, "Expected horse and dog",); + + // verify inline clause + let q2 = Product::all().where_col(|x| or(x.name.like("horse"), x.name.like("dog"))); + eprintln!("SQL: {}", q2.to_sql(Syntax::Postgres)); + let data = q2.run(&conn).await.unwrap(); + assert_eq!(data.len(), 2, "Expected horse and dog",); + }) +} diff --git a/tests/sqlite/tests/all_tests.rs b/tests/sqlite/tests/all_tests.rs index 858be250..2b815f4e 100644 --- a/tests/sqlite/tests/all_tests.rs +++ b/tests/sqlite/tests/all_tests.rs @@ -606,3 +606,26 @@ fn should_be_able_to_fetch_a_single_object() { .unwrap(); }) } + +#[test] +fn should_be_able_to_select_hourse_or_dog() { + async_std::task::block_on(async { + use welds::query::clause::or; + let conn = get_conn().await; + use sqlite_test::models::product::ProductSchema; + + // verify pulling out lambda into variable + let clause = |x: ProductSchema| or(x.name.like("horse"), x.name.like("dog")); + let q = Product::all().where_col(clause); + + eprintln!("SQL: {}", q.to_sql(Syntax::Sqlite)); + let data = q.run(&conn).await.unwrap(); + assert_eq!(data.len(), 2, "Expected horse and dog",); + + // verify inline clause + let q2 = Product::all().where_col(|x| or(x.name.like("horse"), x.name.like("dog"))); + eprintln!("SQL: {}", q2.to_sql(Syntax::Sqlite)); + let data = q2.run(&conn).await.unwrap(); + assert_eq!(data.len(), 2, "Expected horse and dog",); + }) +} diff --git a/welds/src/query/clause/mod.rs b/welds/src/query/clause/mod.rs index 805d53c7..d7f456b3 100644 --- a/welds/src/query/clause/mod.rs +++ b/welds/src/query/clause/mod.rs @@ -34,7 +34,7 @@ mod assignment_adder; #[cfg(feature = "unstable-api")] mod or_and; #[cfg(feature = "unstable-api")] -pub use or_and::AndOrClauseTrait; +pub use or_and::ClauseAdderAndOrExt; #[cfg(feature = "unstable-api")] pub use or_and::and; #[cfg(feature = "unstable-api")] diff --git a/welds/src/query/clause/or_and.rs b/welds/src/query/clause/or_and.rs index 692e9863..04685f65 100644 --- a/welds/src/query/clause/or_and.rs +++ b/welds/src/query/clause/or_and.rs @@ -1,9 +1,6 @@ -use crate::query::clause::{ - ClauseAdder, ClauseColManual, ClauseColVal, ClauseColValEqual, ClauseColValIn, - ClauseColValList, LogicalClause, LogicalOp, ParamArgs, -}; +use crate::query::clause::{ClauseAdder, LogicalClause, LogicalOp, ParamArgs}; use crate::writers::NextParam; -use welds_connections::{Param, Syntax}; +use welds_connections::Syntax; impl LogicalOp { pub fn to_str(&self) -> &'static str { @@ -17,7 +14,7 @@ impl LogicalOp { pub fn or( left_clause: Box, right_clause: Box, -) -> Box { +) -> Box { Box::new(LogicalClause { left_clause, operator: LogicalOp::Or, @@ -28,7 +25,7 @@ pub fn or( pub fn and( left_clause: Box, right_clause: Box, -) -> Box { +) -> Box { Box::new(LogicalClause { left_clause, operator: LogicalOp::And, @@ -69,85 +66,98 @@ impl ClauseAdder for LogicalClause { } } -pub trait AndOrClauseTrait { - fn and(self: Box, other: Box) -> Box; - fn or(self: Box, other: Box) -> Box; +/// Extensions on ClauseAdder to add builder style (and/or) methods +pub trait ClauseAdderAndOrExt { + fn and(self: Box, other: Box) -> Box; + fn or(self: Box, other: Box) -> Box; } -impl AndOrClauseTrait for LogicalClause { - fn and(self: Box, other: Box) -> Box { - and(self, other) - } - - fn or(self: Box, other: Box) -> Box { - or(self, other) - } -} - -impl AndOrClauseTrait for ClauseColVal -where - for<'a> T: 'a, - T: Clone + Send + Sync + Param, -{ - fn and(self: Box, other: Box) -> Box { - and(self, other) - } - - fn or(self: Box, other: Box) -> Box { - or(self, other) - } -} - -impl AndOrClauseTrait for ClauseColValEqual -where - for<'a> T: 'a, - T: Clone + Send + Sync + Param, -{ - fn and(self: Box, other: Box) -> Box { - and(self, other) - } - - fn or(self: Box, other: Box) -> Box { - or(self, other) - } -} - -impl AndOrClauseTrait for ClauseColValList -where - for<'a> T: 'a, - Vec: Clone + Send + Sync + Param, -{ - fn and(self: Box, other: Box) -> Box { - and(self, other) - } - - fn or(self: Box, other: Box) -> Box { - or(self, other) - } -} - -impl AndOrClauseTrait for ClauseColValIn +impl ClauseAdderAndOrExt for CA where - for<'a> T: 'a + Clone + Send + Sync + Param, + CA: ClauseAdder + 'static, { - fn and(self: Box, other: Box) -> Box { + fn and(self: Box, other: Box) -> Box { and(self, other) } - - fn or(self: Box, other: Box) -> Box { + fn or(self: Box, other: Box) -> Box { or(self, other) } } -impl AndOrClauseTrait for ClauseColManual { - fn and(self: Box, other: Box) -> Box { - and(self, other) - } - - fn or(self: Box, other: Box) -> Box { - or(self, other) - } -} +// impl ClauseAdderAndOrExt for LogicalClause { +// fn and(self: Box, other: Box) -> Box { +// and(self, other) +// } +// +// fn or(self: Box, other: Box) -> Box { +// or(self, other) +// } +// } +// +// impl ClauseAdderAndOrExt for ClauseColVal +// where +// for<'a> T: 'a, +// T: Clone + Send + Sync + Param, +// { +// fn and(self: Box, other: Box) -> Box { +// and(self, other) +// } +// +// fn or(self: Box, other: Box) -> Box { +// or(self, other) +// } +// } +// +// impl ClauseAdderAndOrExt for ClauseColValEqual +// where +// for<'a> T: 'a, +// T: Clone + Send + Sync + Param, +// { +// fn and(self: Box, other: Box) -> Box { +// and(self, other) +// } +// +// fn or(self: Box, other: Box) -> Box { +// or(self, other) +// } +// } +// +// impl ClauseAdderAndOrExt for ClauseColValList +// where +// for<'a> T: 'a, +// Vec: Clone + Send + Sync + Param, +// { +// fn and(self: Box, other: Box) -> Box { +// and(self, other) +// } +// +// fn or(self: Box, other: Box) -> Box { +// or(self, other) +// } +// } +// +// impl ClauseAdderAndOrExt for ClauseColValIn +// where +// for<'a> T: 'a + Clone + Send + Sync + Param, +// { +// fn and(self: Box, other: Box) -> Box { +// and(self, other) +// } +// +// fn or(self: Box, other: Box) -> Box { +// or(self, other) +// } +// } +// +// impl ClauseAdderAndOrExt for ClauseColManual { +// fn and(self: Box, other: Box) -> Box { +// and(self, other) +// } +// +// fn or(self: Box, other: Box) -> Box { +// or(self, other) +// } +// } #[cfg(test)] mod tests { @@ -208,23 +218,23 @@ mod tests { ); } - #[test] - fn test_nested_linked_logical_clauses() { - let a = TestModelSchema::default(); - - // ((id != 0 OR name_column == 'empty) AND is_active != true) OR score > 0.5 - let or_clause = a.id.not_equal(0).or(a.name.equal("empty")); - let and_clause = or_clause.and(a.is_active.not_equal(true)); - let nested = and_clause.or(a.score.gte(0.5)); - - let sql = nested.clause(Syntax::Postgres, "t1", &NextParam::new(Syntax::Postgres)); - assert!(sql.is_some()); - let sql_str = sql.unwrap(); - assert!(sql_str.contains("AND")); - assert!(sql_str.contains("OR")); - assert_eq!( - sql_str, - "(((t1.id != $1 OR t1.name_column = $2) AND t1.is_active != $3) OR t1.score >= $4)" - ); - } + // #[test] + // fn test_nested_linked_logical_clauses() { + // let a = TestModelSchema::default(); + + // // ((id != 0 OR name_column == 'empty) AND is_active != true) OR score > 0.5 + // let or_clause = a.id.not_equal(0).or(a.name.equal("empty")); + // let and_clause = or_clause.and(a.is_active.not_equal(true)); + // let nested = and_clause.or(a.score.gte(0.5)); + + // let sql = nested.clause(Syntax::Postgres, "t1", &NextParam::new(Syntax::Postgres)); + // assert!(sql.is_some()); + // let sql_str = sql.unwrap(); + // assert!(sql_str.contains("AND")); + // assert!(sql_str.contains("OR")); + // assert_eq!( + // sql_str, + // "(((t1.id != $1 OR t1.name_column = $2) AND t1.is_active != $3) OR t1.score >= $4)" + // ); + // } } From f3d74785052685e42a5c167af689a220c50ed903 Mon Sep 17 00:00:00 2001 From: Lex Childs Date: Sat, 3 Jan 2026 06:29:57 -0600 Subject: [PATCH 10/10] Small code cleanup --- welds-macros/src/hook.rs | 24 ++--- welds/src/query/clause/assignment_adder.rs | 4 +- welds/src/query/clause/mod.rs | 11 --- welds/src/query/clause/or_and.rs | 108 +++------------------ 4 files changed, 26 insertions(+), 121 deletions(-) diff --git a/welds-macros/src/hook.rs b/welds-macros/src/hook.rs index 9424a2e2..8cae64d8 100644 --- a/welds-macros/src/hook.rs +++ b/welds-macros/src/hook.rs @@ -1,7 +1,6 @@ +use crate::errors::Result; use proc_macro2::{TokenStream, TokenTree}; use quote::ToTokens; -use crate::errors::Result; -//use syn::Ident; use syn::{MetaList, Path}; /// User has defined a Hook on the model @@ -32,7 +31,7 @@ impl Hook { .to_owned()) }; - let list= &list.tokens.clone().into_iter().collect::>(); + let list = &list.tokens.clone().into_iter().collect::>(); if list.len() > 5 { return badformat(); } @@ -41,15 +40,16 @@ impl Hook { if list.len() == 5 { match &list[3] { - TokenTree::Punct(punct)=> - if punct.as_char() != '=' { - return badformat(); + TokenTree::Punct(punct) => { + if punct.as_char() != '=' { + return badformat(); + } } _ => return badformat(), } match &list[2] { - TokenTree::Ident(ident)=> { - if ident.to_string() != "async" { + TokenTree::Ident(ident) => { + if *ident != "async" { return badformat(); } } @@ -58,14 +58,14 @@ impl Hook { match &list[4] { TokenTree::Ident(ident) => { - if ident.to_string()=="true" { + if *ident == "true" { is_async = true; - } else if ident.to_string()=="false" { + } else if *ident == "false" { is_async = false; } else { return badformat(); } - }, + } _ => return badformat(), } } @@ -77,7 +77,7 @@ impl Hook { tokens }; - let callback: syn::Result =syn::parse2(token_stream); + let callback: syn::Result = syn::parse2(token_stream); let callback = match callback { Ok(path) => path, diff --git a/welds/src/query/clause/assignment_adder.rs b/welds/src/query/clause/assignment_adder.rs index 0ff61443..fdd8fa32 100644 --- a/welds/src/query/clause/assignment_adder.rs +++ b/welds/src/query/clause/assignment_adder.rs @@ -31,7 +31,7 @@ where fn clause(&self, syntax: Syntax, _alias: &str, next_params: &NextParam) -> Option { // build the column name - let colname = ColumnWriter::new(syntax).excape(&self.col); + let colname = ColumnWriter::new(syntax).excape(self.col); let mut parts = vec![colname.as_str()]; // handle null clones @@ -104,7 +104,7 @@ impl AssignmentAdder for AssignmentManual { // build the column name let mut parts = vec![]; - let colname = ColumnWriter::new(syntax).excape(&self.col); + let colname = ColumnWriter::new(syntax).excape(self.col); parts.push(colname); parts.push(" = ( ".to_string()); diff --git a/welds/src/query/clause/mod.rs b/welds/src/query/clause/mod.rs index d7f456b3..b35d6723 100644 --- a/welds/src/query/clause/mod.rs +++ b/welds/src/query/clause/mod.rs @@ -104,14 +104,3 @@ pub struct SetColVal { pub struct SetColNull { pub col_raw: String, } - -enum LogicalOp { - And, - Or, -} - -pub struct LogicalClause { - left_clause: Box, - operator: LogicalOp, - right_clause: Box, -} diff --git a/welds/src/query/clause/or_and.rs b/welds/src/query/clause/or_and.rs index 04685f65..8344f438 100644 --- a/welds/src/query/clause/or_and.rs +++ b/welds/src/query/clause/or_and.rs @@ -1,7 +1,18 @@ -use crate::query::clause::{ClauseAdder, LogicalClause, LogicalOp, ParamArgs}; +use crate::query::clause::{ClauseAdder, ParamArgs}; use crate::writers::NextParam; use welds_connections::Syntax; +enum LogicalOp { + And, + Or, +} + +pub struct LogicalClause { + left_clause: Box, + operator: LogicalOp, + right_clause: Box, +} + impl LogicalOp { pub fn to_str(&self) -> &'static str { match self { @@ -84,81 +95,6 @@ where } } -// impl ClauseAdderAndOrExt for LogicalClause { -// fn and(self: Box, other: Box) -> Box { -// and(self, other) -// } -// -// fn or(self: Box, other: Box) -> Box { -// or(self, other) -// } -// } -// -// impl ClauseAdderAndOrExt for ClauseColVal -// where -// for<'a> T: 'a, -// T: Clone + Send + Sync + Param, -// { -// fn and(self: Box, other: Box) -> Box { -// and(self, other) -// } -// -// fn or(self: Box, other: Box) -> Box { -// or(self, other) -// } -// } -// -// impl ClauseAdderAndOrExt for ClauseColValEqual -// where -// for<'a> T: 'a, -// T: Clone + Send + Sync + Param, -// { -// fn and(self: Box, other: Box) -> Box { -// and(self, other) -// } -// -// fn or(self: Box, other: Box) -> Box { -// or(self, other) -// } -// } -// -// impl ClauseAdderAndOrExt for ClauseColValList -// where -// for<'a> T: 'a, -// Vec: Clone + Send + Sync + Param, -// { -// fn and(self: Box, other: Box) -> Box { -// and(self, other) -// } -// -// fn or(self: Box, other: Box) -> Box { -// or(self, other) -// } -// } -// -// impl ClauseAdderAndOrExt for ClauseColValIn -// where -// for<'a> T: 'a + Clone + Send + Sync + Param, -// { -// fn and(self: Box, other: Box) -> Box { -// and(self, other) -// } -// -// fn or(self: Box, other: Box) -> Box { -// or(self, other) -// } -// } -// -// impl ClauseAdderAndOrExt for ClauseColManual { -// fn and(self: Box, other: Box) -> Box { -// and(self, other) -// } -// -// fn or(self: Box, other: Box) -> Box { -// or(self, other) -// } -// } - #[cfg(test)] mod tests { use super::*; @@ -217,24 +153,4 @@ mod tests { "((t1.id = $1 AND t1.is_active = $2) OR t1.score >= $3)" ); } - - // #[test] - // fn test_nested_linked_logical_clauses() { - // let a = TestModelSchema::default(); - - // // ((id != 0 OR name_column == 'empty) AND is_active != true) OR score > 0.5 - // let or_clause = a.id.not_equal(0).or(a.name.equal("empty")); - // let and_clause = or_clause.and(a.is_active.not_equal(true)); - // let nested = and_clause.or(a.score.gte(0.5)); - - // let sql = nested.clause(Syntax::Postgres, "t1", &NextParam::new(Syntax::Postgres)); - // assert!(sql.is_some()); - // let sql_str = sql.unwrap(); - // assert!(sql_str.contains("AND")); - // assert!(sql_str.contains("OR")); - // assert_eq!( - // sql_str, - // "(((t1.id != $1 OR t1.name_column = $2) AND t1.is_active != $3) OR t1.score >= $4)" - // ); - // } }