mtgjson_sdk/
sql_builder.rs1pub struct SqlBuilder {
23 select_cols: Vec<String>,
24 is_distinct: bool,
25 from_table: String,
26 joins: Vec<String>,
27 where_clauses: Vec<String>,
28 params: Vec<String>,
29 group_by_cols: Vec<String>,
30 having_clauses: Vec<String>,
31 order_by_cols: Vec<String>,
32 limit_val: Option<usize>,
33 offset_val: Option<usize>,
34}
35
36impl SqlBuilder {
37 pub fn new(table: &str) -> Self {
39 Self {
40 select_cols: vec!["*".to_string()],
41 is_distinct: false,
42 from_table: table.to_string(),
43 joins: Vec::new(),
44 where_clauses: Vec::new(),
45 params: Vec::new(),
46 group_by_cols: Vec::new(),
47 having_clauses: Vec::new(),
48 order_by_cols: Vec::new(),
49 limit_val: None,
50 offset_val: None,
51 }
52 }
53
54 pub fn select(&mut self, cols: &[&str]) -> &mut Self {
56 self.select_cols = cols.iter().map(|c| c.to_string()).collect();
57 self
58 }
59
60 pub fn distinct(&mut self) -> &mut Self {
62 self.is_distinct = true;
63 self
64 }
65
66 pub fn join(&mut self, clause: &str) -> &mut Self {
71 self.joins.push(clause.to_string());
72 self
73 }
74
75 pub fn where_clause(&mut self, condition: &str, params: &[&str]) -> &mut Self {
80 self.where_clauses.push(condition.to_string());
81 self.params.extend(params.iter().map(|p| p.to_string()));
82 self
83 }
84
85 pub fn where_like(&mut self, column: &str, value: &str) -> &mut Self {
89 self.where_clauses
90 .push(format!("LOWER({}) LIKE LOWER(?)", column));
91 self.params.push(value.to_string());
92 self
93 }
94
95 pub fn where_in(&mut self, column: &str, values: &[&str]) -> &mut Self {
99 if values.is_empty() {
100 self.where_clauses.push("FALSE".to_string());
101 return self;
102 }
103 let placeholders: Vec<&str> = values.iter().map(|_| "?").collect();
104 self.where_clauses
105 .push(format!("{} IN ({})", column, placeholders.join(", ")));
106 self.params.extend(values.iter().map(|v| v.to_string()));
107 self
108 }
109
110 pub fn where_eq(&mut self, column: &str, value: &str) -> &mut Self {
112 self.where_clauses
113 .push(format!("{} = ?", column));
114 self.params.push(value.to_string());
115 self
116 }
117
118 pub fn where_gte(&mut self, column: &str, value: &str) -> &mut Self {
120 self.where_clauses
121 .push(format!("{} >= ?", column));
122 self.params.push(value.to_string());
123 self
124 }
125
126 pub fn where_lte(&mut self, column: &str, value: &str) -> &mut Self {
128 self.where_clauses
129 .push(format!("{} <= ?", column));
130 self.params.push(value.to_string());
131 self
132 }
133
134 pub fn where_regex(&mut self, column: &str, pattern: &str) -> &mut Self {
138 self.where_clauses
139 .push(format!("regexp_matches({}, ?)", column));
140 self.params.push(pattern.to_string());
141 self
142 }
143
144 pub fn where_fuzzy(&mut self, column: &str, value: &str, threshold: f64) -> &mut Self {
150 self.where_clauses.push(format!(
151 "jaro_winkler_similarity({}, ?) > {}",
152 column, threshold
153 ));
154 self.params.push(value.to_string());
155 self
156 }
157
158 pub fn where_or(&mut self, conditions: &[(&str, &str)]) -> &mut Self {
172 if conditions.is_empty() {
173 return self;
174 }
175 let mut or_parts = Vec::with_capacity(conditions.len());
176 for (cond, param) in conditions {
177 or_parts.push(cond.to_string());
178 self.params.push(param.to_string());
179 }
180 self.where_clauses
181 .push(format!("({})", or_parts.join(" OR ")));
182 self
183 }
184
185 pub fn group_by(&mut self, cols: &[&str]) -> &mut Self {
187 self.group_by_cols
188 .extend(cols.iter().map(|c| c.to_string()));
189 self
190 }
191
192 pub fn having(&mut self, condition: &str, params: &[&str]) -> &mut Self {
194 self.having_clauses.push(condition.to_string());
195 self.params.extend(params.iter().map(|p| p.to_string()));
196 self
197 }
198
199 pub fn order_by(&mut self, clauses: &[&str]) -> &mut Self {
201 self.order_by_cols
202 .extend(clauses.iter().map(|c| c.to_string()));
203 self
204 }
205
206 pub fn limit(&mut self, n: usize) -> &mut Self {
208 self.limit_val = Some(n);
209 self
210 }
211
212 pub fn offset(&mut self, n: usize) -> &mut Self {
214 self.offset_val = Some(n);
215 self
216 }
217
218 pub fn build(&self) -> (String, Vec<String>) {
222 let distinct = if self.is_distinct { "DISTINCT " } else { "" };
223 let cols = self.select_cols.join(", ");
224 let mut parts = vec![
225 format!("SELECT {}{}", distinct, cols),
226 format!("FROM {}", self.from_table),
227 ];
228
229 for j in &self.joins {
230 parts.push(j.clone());
231 }
232
233 if !self.where_clauses.is_empty() {
234 parts.push(format!("WHERE {}", self.where_clauses.join(" AND ")));
235 }
236
237 if !self.group_by_cols.is_empty() {
238 parts.push(format!("GROUP BY {}", self.group_by_cols.join(", ")));
239 }
240
241 if !self.having_clauses.is_empty() {
242 parts.push(format!("HAVING {}", self.having_clauses.join(" AND ")));
243 }
244
245 if !self.order_by_cols.is_empty() {
246 parts.push(format!("ORDER BY {}", self.order_by_cols.join(", ")));
247 }
248
249 if let Some(n) = self.limit_val {
250 parts.push(format!("LIMIT {}", n));
251 }
252
253 if let Some(n) = self.offset_val {
254 parts.push(format!("OFFSET {}", n));
255 }
256
257 (parts.join("\n"), self.params.clone())
258 }
259}