1use crate::AliasedEntityColumn;
2use sql_orm_core::{Entity, EntityColumn, SqlTypeMapping};
3use sql_orm_query::{Expr, Predicate};
4
5#[allow(clippy::wrong_self_convention)]
7pub trait EntityColumnPredicateExt<E: Entity> {
8 fn eq<V>(self, value: V) -> Predicate
9 where
10 V: SqlTypeMapping;
11
12 fn ne<V>(self, value: V) -> Predicate
13 where
14 V: SqlTypeMapping;
15
16 fn gt<V>(self, value: V) -> Predicate
17 where
18 V: SqlTypeMapping;
19
20 fn gte<V>(self, value: V) -> Predicate
21 where
22 V: SqlTypeMapping;
23
24 fn lt<V>(self, value: V) -> Predicate
25 where
26 V: SqlTypeMapping;
27
28 fn lte<V>(self, value: V) -> Predicate
29 where
30 V: SqlTypeMapping;
31
32 fn is_null(self) -> Predicate;
33
34 fn is_not_null(self) -> Predicate;
35
36 fn contains(self, value: impl Into<String>) -> Predicate;
37
38 fn starts_with(self, value: impl Into<String>) -> Predicate;
39
40 fn ends_with(self, value: impl Into<String>) -> Predicate;
41}
42
43impl<E: Entity> EntityColumnPredicateExt<E> for EntityColumn<E> {
44 fn eq<V>(self, value: V) -> Predicate
45 where
46 V: SqlTypeMapping,
47 {
48 Predicate::eq(Expr::from(self), Expr::value(value.to_sql_value()))
49 }
50
51 fn ne<V>(self, value: V) -> Predicate
52 where
53 V: SqlTypeMapping,
54 {
55 Predicate::ne(Expr::from(self), Expr::value(value.to_sql_value()))
56 }
57
58 fn gt<V>(self, value: V) -> Predicate
59 where
60 V: SqlTypeMapping,
61 {
62 Predicate::gt(Expr::from(self), Expr::value(value.to_sql_value()))
63 }
64
65 fn gte<V>(self, value: V) -> Predicate
66 where
67 V: SqlTypeMapping,
68 {
69 Predicate::gte(Expr::from(self), Expr::value(value.to_sql_value()))
70 }
71
72 fn lt<V>(self, value: V) -> Predicate
73 where
74 V: SqlTypeMapping,
75 {
76 Predicate::lt(Expr::from(self), Expr::value(value.to_sql_value()))
77 }
78
79 fn lte<V>(self, value: V) -> Predicate
80 where
81 V: SqlTypeMapping,
82 {
83 Predicate::lte(Expr::from(self), Expr::value(value.to_sql_value()))
84 }
85
86 fn is_null(self) -> Predicate {
87 Predicate::is_null(Expr::from(self))
88 }
89
90 fn is_not_null(self) -> Predicate {
91 Predicate::is_not_null(Expr::from(self))
92 }
93
94 fn contains(self, value: impl Into<String>) -> Predicate {
95 Predicate::like(
96 Expr::from(self),
97 Expr::value(sql_orm_core::SqlValue::String(format!(
98 "%{}%",
99 value.into()
100 ))),
101 )
102 }
103
104 fn starts_with(self, value: impl Into<String>) -> Predicate {
105 Predicate::like(
106 Expr::from(self),
107 Expr::value(sql_orm_core::SqlValue::String(format!("{}%", value.into()))),
108 )
109 }
110
111 fn ends_with(self, value: impl Into<String>) -> Predicate {
112 Predicate::like(
113 Expr::from(self),
114 Expr::value(sql_orm_core::SqlValue::String(format!("%{}", value.into()))),
115 )
116 }
117}
118
119impl<E: Entity> EntityColumnPredicateExt<E> for AliasedEntityColumn<E> {
120 fn eq<V>(self, value: V) -> Predicate
121 where
122 V: SqlTypeMapping,
123 {
124 Predicate::eq(Expr::from(self), Expr::value(value.to_sql_value()))
125 }
126
127 fn ne<V>(self, value: V) -> Predicate
128 where
129 V: SqlTypeMapping,
130 {
131 Predicate::ne(Expr::from(self), Expr::value(value.to_sql_value()))
132 }
133
134 fn gt<V>(self, value: V) -> Predicate
135 where
136 V: SqlTypeMapping,
137 {
138 Predicate::gt(Expr::from(self), Expr::value(value.to_sql_value()))
139 }
140
141 fn gte<V>(self, value: V) -> Predicate
142 where
143 V: SqlTypeMapping,
144 {
145 Predicate::gte(Expr::from(self), Expr::value(value.to_sql_value()))
146 }
147
148 fn lt<V>(self, value: V) -> Predicate
149 where
150 V: SqlTypeMapping,
151 {
152 Predicate::lt(Expr::from(self), Expr::value(value.to_sql_value()))
153 }
154
155 fn lte<V>(self, value: V) -> Predicate
156 where
157 V: SqlTypeMapping,
158 {
159 Predicate::lte(Expr::from(self), Expr::value(value.to_sql_value()))
160 }
161
162 fn is_null(self) -> Predicate {
163 Predicate::is_null(Expr::from(self))
164 }
165
166 fn is_not_null(self) -> Predicate {
167 Predicate::is_not_null(Expr::from(self))
168 }
169
170 fn contains(self, value: impl Into<String>) -> Predicate {
171 Predicate::like(
172 Expr::from(self),
173 Expr::value(sql_orm_core::SqlValue::String(format!(
174 "%{}%",
175 value.into()
176 ))),
177 )
178 }
179
180 fn starts_with(self, value: impl Into<String>) -> Predicate {
181 Predicate::like(
182 Expr::from(self),
183 Expr::value(sql_orm_core::SqlValue::String(format!("{}%", value.into()))),
184 )
185 }
186
187 fn ends_with(self, value: impl Into<String>) -> Predicate {
188 Predicate::like(
189 Expr::from(self),
190 Expr::value(sql_orm_core::SqlValue::String(format!("%{}", value.into()))),
191 )
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::EntityColumnPredicateExt;
198 use crate::EntityColumnAliasExt;
199 use sql_orm_core::{
200 ColumnMetadata, Entity, EntityColumn, EntityMetadata, PrimaryKeyMetadata, SqlServerType,
201 SqlValue,
202 };
203 use sql_orm_query::{ColumnRef, Expr, Predicate, TableRef};
204
205 struct TestEntity;
206
207 static TEST_ENTITY_COLUMNS: [ColumnMetadata; 2] = [
208 ColumnMetadata {
209 rust_field: "id",
210 column_name: "id",
211 renamed_from: None,
212 sql_type: SqlServerType::BigInt,
213 nullable: false,
214 primary_key: true,
215 identity: None,
216 default_sql: None,
217 computed_sql: None,
218 rowversion: false,
219 insertable: false,
220 updatable: false,
221 max_length: None,
222 precision: None,
223 scale: None,
224 },
225 ColumnMetadata {
226 rust_field: "name",
227 column_name: "name",
228 renamed_from: None,
229 sql_type: SqlServerType::NVarChar,
230 nullable: true,
231 primary_key: false,
232 identity: None,
233 default_sql: None,
234 computed_sql: None,
235 rowversion: false,
236 insertable: true,
237 updatable: true,
238 max_length: Some(120),
239 precision: None,
240 scale: None,
241 },
242 ];
243
244 static TEST_ENTITY_METADATA: EntityMetadata = EntityMetadata {
245 rust_name: "TestEntity",
246 schema: "dbo",
247 table: "test_entities",
248 renamed_from: None,
249 columns: &TEST_ENTITY_COLUMNS,
250 primary_key: PrimaryKeyMetadata {
251 name: None,
252 columns: &["id"],
253 },
254 indexes: &[],
255 foreign_keys: &[],
256 navigations: &[],
257 };
258
259 impl Entity for TestEntity {
260 fn metadata() -> &'static EntityMetadata {
261 &TEST_ENTITY_METADATA
262 }
263 }
264
265 #[allow(non_upper_case_globals)]
266 impl TestEntity {
267 const id: EntityColumn<TestEntity> = EntityColumn::new("id", "id");
268 const name: EntityColumn<TestEntity> = EntityColumn::new("name", "name");
269 }
270
271 #[test]
272 fn comparison_methods_build_expected_predicates() {
273 let expected_column = Expr::Column(ColumnRef::new(
274 TableRef::new("dbo", "test_entities"),
275 "id",
276 "id",
277 ));
278
279 assert_eq!(
280 TestEntity::id.eq(7_i64),
281 Predicate::eq(expected_column.clone(), Expr::Value(SqlValue::I64(7)))
282 );
283 assert_eq!(
284 TestEntity::id.ne(8_i64),
285 Predicate::ne(expected_column.clone(), Expr::Value(SqlValue::I64(8)))
286 );
287 assert_eq!(
288 TestEntity::id.gt(9_i64),
289 Predicate::gt(expected_column.clone(), Expr::Value(SqlValue::I64(9)))
290 );
291 assert_eq!(
292 TestEntity::id.gte(10_i64),
293 Predicate::gte(expected_column.clone(), Expr::Value(SqlValue::I64(10)))
294 );
295 assert_eq!(
296 TestEntity::id.lt(11_i64),
297 Predicate::lt(expected_column.clone(), Expr::Value(SqlValue::I64(11)))
298 );
299 assert_eq!(
300 TestEntity::id.lte(12_i64),
301 Predicate::lte(expected_column, Expr::Value(SqlValue::I64(12)))
302 );
303 }
304
305 #[test]
306 fn null_predicate_methods_build_expected_predicates() {
307 let expected_column = Expr::Column(ColumnRef::new(
308 TableRef::new("dbo", "test_entities"),
309 "name",
310 "name",
311 ));
312
313 assert_eq!(
314 TestEntity::name.is_null(),
315 Predicate::is_null(expected_column.clone())
316 );
317 assert_eq!(
318 TestEntity::name.is_not_null(),
319 Predicate::is_not_null(expected_column)
320 );
321 }
322
323 #[test]
324 fn string_predicate_methods_build_expected_like_patterns() {
325 let expected_column = Expr::Column(ColumnRef::new(
326 TableRef::new("dbo", "test_entities"),
327 "name",
328 "name",
329 ));
330
331 assert_eq!(
332 TestEntity::name.contains("ana"),
333 Predicate::like(
334 expected_column.clone(),
335 Expr::Value(SqlValue::String("%ana%".to_string()))
336 )
337 );
338 assert_eq!(
339 TestEntity::name.starts_with("ana"),
340 Predicate::like(
341 expected_column.clone(),
342 Expr::Value(SqlValue::String("ana%".to_string()))
343 )
344 );
345 assert_eq!(
346 TestEntity::name.ends_with("ana"),
347 Predicate::like(
348 expected_column,
349 Expr::Value(SqlValue::String("%ana".to_string()))
350 )
351 );
352 }
353
354 #[test]
355 fn aliased_columns_build_predicates_against_table_alias() {
356 let expected_column = Expr::Column(ColumnRef::new(
357 TableRef::with_alias("dbo", "test_entities", "t"),
358 "name",
359 "name",
360 ));
361
362 assert_eq!(
363 TestEntity::name.aliased("t").contains("ana"),
364 Predicate::like(
365 expected_column.clone(),
366 Expr::Value(SqlValue::String("%ana%".to_string()))
367 )
368 );
369 assert_eq!(
370 TestEntity::name.aliased("t").is_not_null(),
371 Predicate::is_not_null(expected_column)
372 );
373 }
374}