salesforce_client/
query_builder.rs1use std::marker::PhantomData;
6
7#[derive(Debug, Clone)]
23pub struct QueryBuilder<State = NeedsFrom> {
24 fields: Vec<String>,
25 from: Option<String>,
26 where_clauses: Vec<String>,
27 order_by: Option<String>,
28 limit: Option<u32>,
29 offset: Option<u32>,
30 _state: PhantomData<State>,
31}
32
33#[derive(Debug, Clone)]
35pub struct NeedsFrom;
36
37#[derive(Debug, Clone)]
38pub struct Complete;
39
40impl QueryBuilder<NeedsFrom> {
41 pub fn select(fields: &[&str]) -> Self {
43 Self {
44 fields: fields.iter().map(|s| s.to_string()).collect(),
45 from: None,
46 where_clauses: Vec::new(),
47 order_by: None,
48 limit: None,
49 offset: None,
50 _state: PhantomData,
51 }
52 }
53
54 pub fn from(mut self, sobject: impl Into<String>) -> QueryBuilder<Complete> {
56 self.from = Some(sobject.into());
57 QueryBuilder {
58 fields: self.fields,
59 from: self.from,
60 where_clauses: self.where_clauses,
61 order_by: self.order_by,
62 limit: self.limit,
63 offset: self.offset,
64 _state: PhantomData,
65 }
66 }
67}
68
69impl QueryBuilder<Complete> {
70 pub fn where_clause(mut self, condition: impl Into<String>) -> Self {
72 self.where_clauses.push(condition.into());
73 self
74 }
75
76 pub fn and(mut self, condition: impl Into<String>) -> Self {
78 self.where_clauses.push(condition.into());
79 self
80 }
81
82 pub fn order_by(mut self, field: impl Into<String>) -> Self {
84 self.order_by = Some(field.into());
85 self
86 }
87
88 pub fn order_by_asc(mut self, field: impl Into<String>) -> Self {
90 self.order_by = Some(format!("{} ASC", field.into()));
91 self
92 }
93
94 pub fn order_by_desc(mut self, field: impl Into<String>) -> Self {
96 self.order_by = Some(format!("{} DESC", field.into()));
97 self
98 }
99
100 pub fn limit(mut self, limit: u32) -> Self {
102 self.limit = Some(limit);
103 self
104 }
105
106 pub fn offset(mut self, offset: u32) -> Self {
108 self.offset = Some(offset);
109 self
110 }
111
112 pub fn build(self) -> String {
114 let mut query = format!(
115 "SELECT {} FROM {}",
116 self.fields.join(", "),
117 self.from.unwrap() );
119
120 if !self.where_clauses.is_empty() {
121 query.push_str(" WHERE ");
122 query.push_str(&self.where_clauses.join(" AND "));
123 }
124
125 if let Some(order) = self.order_by {
126 query.push_str(" ORDER BY ");
127 query.push_str(&order);
128 }
129
130 if let Some(limit) = self.limit {
131 query.push_str(&format!(" LIMIT {}", limit));
132 }
133
134 if let Some(offset) = self.offset {
135 query.push_str(&format!(" OFFSET {}", offset));
136 }
137
138 query
139 }
140}
141
142pub struct CountQueryBuilder {
144 from: String,
145 where_clauses: Vec<String>,
146}
147
148impl CountQueryBuilder {
149 pub fn count_from(sobject: impl Into<String>) -> Self {
151 Self {
152 from: sobject.into(),
153 where_clauses: Vec::new(),
154 }
155 }
156
157 pub fn where_clause(mut self, condition: impl Into<String>) -> Self {
159 self.where_clauses.push(condition.into());
160 self
161 }
162
163 pub fn build(self) -> String {
165 let mut query = format!("SELECT COUNT() FROM {}", self.from);
166
167 if !self.where_clauses.is_empty() {
168 query.push_str(" WHERE ");
169 query.push_str(&self.where_clauses.join(" AND "));
170 }
171
172 query
173 }
174}
175
176pub struct SubqueryBuilder {
178 fields: Vec<String>,
179 relationship: String,
180 where_clauses: Vec<String>,
181 order_by: Option<String>,
182 limit: Option<u32>,
183}
184
185impl SubqueryBuilder {
186 pub fn new(relationship: impl Into<String>, fields: &[&str]) -> Self {
188 Self {
189 fields: fields.iter().map(|s| s.to_string()).collect(),
190 relationship: relationship.into(),
191 where_clauses: Vec::new(),
192 order_by: None,
193 limit: None,
194 }
195 }
196
197 pub fn where_clause(mut self, condition: impl Into<String>) -> Self {
199 self.where_clauses.push(condition.into());
200 self
201 }
202
203 pub fn order_by(mut self, field: impl Into<String>) -> Self {
205 self.order_by = Some(field.into());
206 self
207 }
208
209 pub fn limit(mut self, limit: u32) -> Self {
211 self.limit = Some(limit);
212 self
213 }
214
215 pub fn build(self) -> String {
217 let mut query = format!(
218 "(SELECT {} FROM {}",
219 self.fields.join(", "),
220 self.relationship
221 );
222
223 if !self.where_clauses.is_empty() {
224 query.push_str(" WHERE ");
225 query.push_str(&self.where_clauses.join(" AND "));
226 }
227
228 if let Some(order) = self.order_by {
229 query.push_str(" ORDER BY ");
230 query.push_str(&order);
231 }
232
233 if let Some(limit) = self.limit {
234 query.push_str(&format!(" LIMIT {}", limit));
235 }
236
237 query.push(')');
238 query
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 #[test]
247 fn test_basic_query() {
248 let query = QueryBuilder::select(&["Id", "Name"])
249 .from("Account")
250 .build();
251
252 assert_eq!(query, "SELECT Id, Name FROM Account");
253 }
254
255 #[test]
256 fn test_query_with_where() {
257 let query = QueryBuilder::select(&["Id", "Name"])
258 .from("Account")
259 .where_clause("AnnualRevenue > 1000000")
260 .build();
261
262 assert_eq!(
263 query,
264 "SELECT Id, Name FROM Account WHERE AnnualRevenue > 1000000"
265 );
266 }
267
268 #[test]
269 fn test_query_with_multiple_conditions() {
270 let query = QueryBuilder::select(&["Id", "Name"])
271 .from("Account")
272 .where_clause("AnnualRevenue > 1000000")
273 .and("Industry = 'Technology'")
274 .build();
275
276 assert_eq!(
277 query,
278 "SELECT Id, Name FROM Account WHERE AnnualRevenue > 1000000 AND Industry = 'Technology'"
279 );
280 }
281
282 #[test]
283 fn test_query_with_order_and_limit() {
284 let query = QueryBuilder::select(&["Id", "Name"])
285 .from("Account")
286 .order_by("Name")
287 .limit(10)
288 .build();
289
290 assert_eq!(query, "SELECT Id, Name FROM Account ORDER BY Name LIMIT 10");
291 }
292
293 #[test]
294 fn test_query_with_all_clauses() {
295 let query = QueryBuilder::select(&["Id", "Name", "AnnualRevenue"])
296 .from("Account")
297 .where_clause("AnnualRevenue > 1000000")
298 .and("Industry = 'Technology'")
299 .order_by_desc("AnnualRevenue")
300 .limit(10)
301 .offset(5)
302 .build();
303
304 assert_eq!(
305 query,
306 "SELECT Id, Name, AnnualRevenue FROM Account WHERE AnnualRevenue > 1000000 AND Industry = 'Technology' ORDER BY AnnualRevenue DESC LIMIT 10 OFFSET 5"
307 );
308 }
309
310 #[test]
311 fn test_count_query() {
312 let query = CountQueryBuilder::count_from("Account")
313 .where_clause("AnnualRevenue > 1000000")
314 .build();
315
316 assert_eq!(
317 query,
318 "SELECT COUNT() FROM Account WHERE AnnualRevenue > 1000000"
319 );
320 }
321
322 #[test]
323 fn test_subquery() {
324 let subquery = SubqueryBuilder::new("Contacts", &["Id", "Email"])
325 .where_clause("Email != null")
326 .limit(5)
327 .build();
328
329 assert_eq!(
330 subquery,
331 "(SELECT Id, Email FROM Contacts WHERE Email != null LIMIT 5)"
332 );
333 }
334}