1#[cfg(feature = "macros")]
3pub use quick_oxibooks_sql_macro::qb_sql;
4
5mod query;
6pub use query::Query;
7mod limit;
8pub(crate) use limit::Limit;
9mod order;
10pub use order::{Order, OrderClause};
11mod condition;
12pub use condition::{Operator, TypedWhereClause, WhereClause};
13
14#[cfg(feature = "macros")]
15pub use pastey::paste;
16
17#[cfg(test)]
18mod tests {
19 use super::*;
20 use quickbooks_types::Customer;
21
22 #[test]
23 fn test_empty_query() {
24 #[cfg(feature = "macros")]
25 let qry = qb_sql!(select * from Customer);
26 #[cfg(not(feature = "macros"))]
27 let qry: Query<Customer> = Query::new();
28 assert_eq!(qry.condition.len(), 0);
29 assert_eq!(qry.order.len(), 0);
30 assert!(qry.limit.is_none());
31 }
32
33 #[test]
34 fn test_basic_query() {
35 #[cfg(feature = "macros")]
36 let qry = qb_sql!(
37 select * from Customer
38 where display_name like "John%"
39 );
40 #[cfg(not(feature = "macros"))]
41 let qry: Query<Customer> = unsafe {
42 Query::new().condition(WhereClause {
43 field: "DisplayName",
44 operator: Operator::Like,
45 values: vec!["John%".to_string()],
46 })
47 };
48
49 assert_eq!(qry.condition.len(), 1);
50 assert_eq!(qry.condition[0].field, "DisplayName");
51 }
52
53 #[test]
54 fn test_multiple_conditions() {
55 let balance_min = 1000.0;
56 #[cfg(feature = "macros")]
57 let qry = qb_sql!(
58 select * from Customer
59 where display_name like "John%"
60 and balance >= balance_min
61 );
62 #[cfg(not(feature = "macros"))]
63 let qry: Query<Customer> = unsafe {
64 Query::new()
65 .condition(WhereClause {
66 field: "DisplayName",
67 operator: Operator::Like,
68 values: vec!["John%".into()],
69 })
70 .condition(WhereClause {
71 field: "Balance",
72 operator: Operator::GreaterEqual,
73 values: vec![balance_min.to_string()],
74 })
75 };
76
77 assert_eq!(qry.condition.len(), 2);
78 }
79
80 #[test]
81 fn test_order_by() {
82 #[cfg(feature = "macros")]
83 let qry = qb_sql!(
84 select * from Customer
85 where display_name like "John%"
86 order by display_name asc, balance desc
87 );
88 #[cfg(not(feature = "macros"))]
89 let qry: Query<Customer> = unsafe {
90 Query::new()
91 .condition(WhereClause {
92 field: "DisplayName",
93 operator: Operator::Like,
94 values: vec!["John%".into()],
95 })
96 .order("DisplayName", Order::Asc)
97 .order("Balance", Order::Desc)
98 };
99
100 assert_eq!(qry.order.len(), 2);
101 assert_eq!(qry.order[0].field, "DisplayName");
102 assert_eq!(qry.order[0].order, Order::Asc);
103 }
104
105 #[test]
106 fn test_limit_and_offset() {
107 let offset_val = 5;
108 #[cfg(feature = "macros")]
109 let qry = qb_sql!(
110 select * from Customer
111 where display_name like "John%"
112 limit 10 offset offset_val
113 );
114 #[cfg(not(feature = "macros"))]
115 let qry: Query<Customer> = unsafe {
116 Query::new()
117 .condition(WhereClause {
118 field: "DisplayName",
119 operator: Operator::Like,
120 values: vec!["John%".into()],
121 })
122 .limit(10, Some(offset_val))
123 };
124
125 assert!(qry.limit.is_some());
126 let limit = qry.limit.unwrap();
127 assert_eq!(limit.number, 10);
128 assert_eq!(limit.offset, Some(5));
129 }
130
131 #[test]
132 fn test_qry_string_generation() {
133 #[cfg(feature = "macros")]
134 let qry = qb_sql!(
135 select * from Customer
136 where display_name like "John%"
137 and id in (1, 2, 3)
138 and balance >= 1000.0
139 order by display_name asc, balance desc
140 limit 10 offset 5
141 );
142 #[cfg(not(feature = "macros"))]
143 let qry: Query<Customer> = unsafe {
144 Query::new()
145 .condition(WhereClause {
146 field: "DisplayName",
147 operator: Operator::Like,
148 values: vec!["John%".into()],
149 })
150 .condition(WhereClause {
151 field: "Id",
152 operator: Operator::In,
153 values: vec!["1".into(), "2".into(), "3".into()],
154 })
155 .condition(WhereClause {
156 field: "Balance",
157 operator: Operator::GreaterEqual,
158 values: vec!["1000".into()],
159 })
160 .order("DisplayName", Order::Asc)
161 .order("Balance", Order::Desc)
162 .limit(10, Some(5))
163 };
164
165 let qry_string = qry.query_string();
166 let expected = "select * from Customer where DisplayName LIKE 'John%' and Id IN ('1', '2', '3') and Balance >= '1000' order by DisplayName ASC, Balance DESC LIMIT 10 OFFSET 5";
167 assert_eq!(qry_string, expected);
168 }
169
170 #[test]
171 fn test_in_operator() {
172 #[cfg(feature = "macros")]
173 let qry = qb_sql!(
174 select * from Customer
175 where id in (1, 2, 3, 4, 5)
176 );
177
178 #[cfg(not(feature = "macros"))]
179 let qry: Query<Customer> = unsafe {
180 Query::new().condition(WhereClause {
181 field: "Id",
182 operator: Operator::In,
183 values: vec!["1".into(), "2".into(), "3".into(), "4".into(), "5".into()],
184 })
185 };
186
187 assert_eq!(qry.condition.len(), 1);
188 assert_eq!(qry.condition[0].field, "Id");
189 assert_eq!(qry.condition[0].operator, Operator::In);
190 assert_eq!(qry.condition[0].values.len(), 5);
191
192 let qry_string = qry.query_string();
193 assert_eq!(
194 qry_string,
195 "select * from Customer where Id IN ('1', '2', '3', '4', '5')"
196 );
197 }
198
199 #[test]
200 fn test_in_operator_with_strings() {
201 let title1 = "Mr";
202 let title2 = "Mrs";
203 #[cfg(feature = "macros")]
204 let qry = qb_sql!(
205 select * from Customer
206 where title in (title1, title2, "Dr")
207 );
208
209 #[cfg(not(feature = "macros"))]
210 let qry: Query<Customer> = unsafe {
211 Query::new().condition(WhereClause {
212 field: "Title",
213 operator: Operator::In,
214 values: vec![title1.into(), title2.into(), "Dr".into()],
215 })
216 };
217
218 assert_eq!(qry.condition.len(), 1);
219 assert_eq!(qry.condition[0].values.len(), 3);
220
221 let qry_string = qry.query_string();
222 assert_eq!(
223 qry_string,
224 "select * from Customer where Title IN ('Mr', 'Mrs', 'Dr')"
225 );
226 }
227
228 #[test]
229 fn test_in_iterator() {
230 let ids = vec![1, 2, 3, 4, 5];
231 #[cfg(feature = "macros")]
232 let qry = qb_sql!(
233 select * from Customer
234 where id in (ids)
235 );
236 #[cfg(not(feature = "macros"))]
237 let qry: Query<Customer> = unsafe {
238 Query::new().condition(WhereClause {
239 field: "Id",
240 operator: Operator::In,
241 values: ids.iter().map(|id| id.to_string()).collect(),
242 })
243 };
244
245 assert_eq!(qry.condition.len(), 1);
246 assert_eq!(qry.condition[0].field, "Id");
247 assert_eq!(qry.condition[0].operator, Operator::In);
248 assert_eq!(qry.condition[0].values.len(), 5);
249
250 let qry_string = qry.query_string();
251 assert_eq!(
252 qry_string,
253 "select * from Customer where Id IN ('1', '2', '3', '4', '5')"
254 );
255 }
256
257 #[test]
258 fn test_nested_fields() {
259 #[cfg(feature = "macros")]
260 let qry = qb_sql!(
261 select * from Customer
262 where primary_email_addr.address like "%@example.com"
263 );
264
265 #[cfg(not(feature = "macros"))]
266 let qry: Query<Customer> = unsafe {
267 Query::new().condition(WhereClause {
268 field: "PrimaryEmailAddr.Address",
269 operator: Operator::Like,
270 values: vec!["%@example.com".into()],
271 })
272 };
273
274 assert_eq!(qry.condition.len(), 1);
275 assert_eq!(qry.condition[0].field, "PrimaryEmailAddr.Address");
276
277 let qry_string = qry.query_string();
278 assert_eq!(
279 qry_string,
280 "select * from Customer where PrimaryEmailAddr.Address LIKE '%@example.com'"
281 );
282 }
283}