Skip to main content

nautilus_core/column/
mod.rs

1//! Typed column references for type-safe query building.
2
3pub mod from_value;
4pub mod marker;
5pub mod row_access;
6pub mod typed;
7
8pub use from_value::FromValue;
9pub use marker::ColumnMarker;
10pub use row_access::RowAccess;
11pub use typed::{Column, OrderField, SelectColumns};
12
13#[cfg(test)]
14mod tests {
15    use std::borrow::Cow;
16
17    use super::*;
18    use crate::expr::Expr;
19    use crate::select::OrderDir;
20    use crate::value::Value;
21
22    #[test]
23    fn test_column_new() {
24        let col: Column<i64> = Column::new("users", "id");
25        assert_eq!(col.table(), "users");
26        assert_eq!(col.name(), "id");
27
28        let col: Column<String> = Column::new("users", "email");
29        assert_eq!(col.table(), "users");
30        assert_eq!(col.name(), "email");
31    }
32
33    #[test]
34    fn test_column_alias() {
35        let col: Column<i64> = Column::new("users", "id");
36        assert_eq!(col.alias(), "users__id");
37
38        let col: Column<String> = Column::new("posts", "title");
39        assert_eq!(col.alias(), "posts__title");
40    }
41
42    #[test]
43    fn test_column_marker() {
44        let col: Column<i64> = Column::new("users", "id");
45        let marker = col.marker();
46        assert_eq!(marker.table, "users");
47        assert_eq!(marker.name, "id");
48        assert!(matches!(marker.table, Cow::Borrowed("users")));
49        assert!(matches!(marker.name, Cow::Borrowed("id")));
50    }
51
52    #[test]
53    fn test_column_to_expr() {
54        let col: Column<i64> = Column::new("users", "id");
55        let expr: Expr = col.into();
56        assert_eq!(expr, Expr::Column("users__id".to_string()));
57    }
58
59    #[test]
60    fn test_eq_operator() {
61        let col: Column<i64> = Column::new("users", "id");
62        let expr = col.eq(42i64);
63        assert_eq!(
64            expr,
65            Expr::column("users__id").eq(Expr::param(Value::I64(42)))
66        );
67    }
68
69    #[test]
70    fn test_eq_operator_string() {
71        let col: Column<String> = Column::new("users", "email");
72        let expr = col.eq("test@example.com");
73        assert_eq!(
74            expr,
75            Expr::column("users__email")
76                .eq(Expr::param(Value::String("test@example.com".to_string())))
77        );
78    }
79
80    #[test]
81    fn test_desc_order() {
82        let col: Column<String> = Column::new("users", "email");
83        let order = col.desc();
84        assert_eq!(order.column, "users__email");
85        assert_eq!(order.direction, OrderDir::Desc);
86    }
87
88    #[test]
89    fn test_asc_order() {
90        let col: Column<i64> = Column::new("users", "id");
91        let order = col.asc();
92        assert_eq!(order.column, "users__id");
93        assert_eq!(order.direction, OrderDir::Asc);
94    }
95
96    #[test]
97    fn test_nested_order_field() {
98        let field: OrderField<i32> = OrderField::new("shipments", "delivery.etaMinutes");
99        assert_eq!(field.table(), "shipments");
100        assert_eq!(field.path(), "delivery.etaMinutes");
101
102        let order = field.asc();
103        assert_eq!(order.column, "shipments__delivery.etaMinutes");
104        assert_eq!(order.direction, OrderDir::Asc);
105    }
106
107    #[test]
108    fn test_ends_with() {
109        let col: Column<String> = Column::new("users", "email");
110        let expr = col.ends_with("example.com");
111        assert_eq!(
112            expr,
113            Expr::column("users__email")
114                .like(Expr::param(Value::String("%example.com".to_string())))
115        );
116    }
117
118    #[test]
119    fn test_starts_with() {
120        let col: Column<String> = Column::new("users", "email");
121        let expr = col.starts_with("admin");
122        assert_eq!(
123            expr,
124            Expr::column("users__email").like(Expr::param(Value::String("admin%".to_string())))
125        );
126    }
127
128    #[test]
129    fn test_contains() {
130        let col: Column<String> = Column::new("users", "email");
131        let expr = col.contains("example");
132        assert_eq!(
133            expr,
134            Expr::column("users__email").like(Expr::param(Value::String("%example%".to_string())))
135        );
136    }
137
138    #[test]
139    fn test_from_value_i64() {
140        assert_eq!(i64::from_value(&Value::I64(42)).unwrap(), 42);
141        assert!(i64::from_value(&Value::Null).is_err());
142        assert!(i64::from_value(&Value::String("test".to_string())).is_err());
143    }
144
145    #[test]
146    fn test_from_value_string() {
147        assert_eq!(
148            String::from_value(&Value::String("test".to_string())).unwrap(),
149            "test"
150        );
151        assert!(String::from_value(&Value::Null).is_err());
152        assert!(String::from_value(&Value::I64(42)).is_err());
153    }
154
155    #[test]
156    fn test_from_value_bool() {
157        assert!(bool::from_value(&Value::Bool(true)).unwrap());
158        assert!(!bool::from_value(&Value::Bool(false)).unwrap());
159        assert!(bool::from_value(&Value::I64(1)).unwrap());
160        assert!(!bool::from_value(&Value::I64(0)).unwrap());
161        assert!(bool::from_value(&Value::Null).is_err());
162        assert!(bool::from_value(&Value::I64(2)).is_err());
163    }
164
165    #[test]
166    fn test_select_columns_single() {
167        let selection = (Column::<i64>::new("users", "id"),);
168        let columns = selection.columns();
169        assert_eq!(columns.len(), 1);
170        assert_eq!(columns[0].table, "users");
171        assert_eq!(columns[0].name, "id");
172    }
173
174    #[test]
175    fn test_select_columns_two() {
176        let selection = (
177            Column::<i64>::new("users", "id"),
178            Column::<String>::new("users", "email"),
179        );
180        let columns = selection.columns();
181        assert_eq!(columns.len(), 2);
182        assert_eq!(columns[0].table, "users");
183        assert_eq!(columns[0].name, "id");
184        assert_eq!(columns[1].table, "users");
185        assert_eq!(columns[1].name, "email");
186    }
187
188    #[test]
189    fn test_select_columns_multiple_tables() {
190        let selection = (
191            Column::<i64>::new("users", "id"),
192            Column::<String>::new("posts", "title"),
193        );
194        let columns = selection.columns();
195        assert_eq!(columns.len(), 2);
196        assert_eq!(columns[0].table, "users");
197        assert_eq!(columns[0].name, "id");
198        assert_eq!(columns[1].table, "posts");
199        assert_eq!(columns[1].name, "title");
200    }
201
202    #[test]
203    fn test_column_copy() {
204        let col1: Column<i64> = Column::new("users", "id");
205        let col2 = col1;
206        let col3 = col1;
207        assert_eq!(col1.name(), col2.name());
208        assert_eq!(col2.name(), col3.name());
209    }
210
211    #[test]
212    fn test_column_marker_from() {
213        let col: Column<i64> = Column::new("users", "id");
214        let marker: ColumnMarker = col.into();
215        assert_eq!(marker.table, "users");
216        assert_eq!(marker.name, "id");
217    }
218
219    #[test]
220    fn test_column_marker_alias() {
221        let marker = ColumnMarker::new("users", "id");
222        assert_eq!(marker.alias(), "users__id");
223
224        let marker = ColumnMarker::new("posts", "title");
225        assert_eq!(marker.alias(), "posts__title");
226    }
227
228    #[test]
229    fn test_dynamic_column_marker_owns_runtime_strings() {
230        let table = String::from("users");
231        let name = String::from("email");
232        let marker = ColumnMarker::new(table, name);
233
234        assert!(matches!(marker.table, Cow::Owned(_)));
235        assert!(matches!(marker.name, Cow::Owned(_)));
236    }
237
238    struct MockRow {
239        values: Vec<Value>,
240    }
241
242    impl<'row> RowAccess<'row> for MockRow {
243        fn get_by_pos(&'row self, idx: usize) -> Option<&'row Value> {
244            self.values.get(idx)
245        }
246
247        fn get(&'row self, _name: &str) -> Option<&'row Value> {
248            None
249        }
250
251        fn column_name(&'row self, _idx: usize) -> Option<&'row str> {
252            None
253        }
254
255        fn len(&self) -> usize {
256            self.values.len()
257        }
258    }
259
260    #[test]
261    fn test_select_columns_decode_single() {
262        let selection = (Column::<i64>::new("users", "id"),);
263        let mock_row = MockRow {
264            values: vec![Value::I64(42)],
265        };
266
267        let result = selection.decode(&mock_row).unwrap();
268        assert_eq!(result, (42,));
269    }
270
271    #[test]
272    fn test_select_columns_decode_two() {
273        let selection = (
274            Column::<i64>::new("users", "id"),
275            Column::<String>::new("users", "email"),
276        );
277        let mock_row = MockRow {
278            values: vec![
279                Value::I64(42),
280                Value::String("test@example.com".to_string()),
281            ],
282        };
283
284        let result = selection.decode(&mock_row).unwrap();
285        assert_eq!(result, (42, "test@example.com".to_string()));
286    }
287
288    #[test]
289    fn test_select_columns_decode_three() {
290        let selection = (
291            Column::<i64>::new("users", "id"),
292            Column::<String>::new("users", "email"),
293            Column::<bool>::new("users", "active"),
294        );
295        let mock_row = MockRow {
296            values: vec![
297                Value::I64(99),
298                Value::String("admin@example.com".to_string()),
299                Value::Bool(true),
300            ],
301        };
302
303        let result = selection.decode(&mock_row).unwrap();
304        assert_eq!(result, (99, "admin@example.com".to_string(), true));
305    }
306
307    #[test]
308    fn test_select_columns_decode_missing_column() {
309        let selection = (
310            Column::<i64>::new("users", "id"),
311            Column::<String>::new("users", "email"),
312        );
313        let mock_row = MockRow {
314            values: vec![Value::I64(42)],
315        };
316
317        let result = selection.decode(&mock_row);
318        assert!(result.is_err());
319    }
320
321    #[test]
322    fn test_select_columns_decode_type_error() {
323        let selection = (Column::<i64>::new("users", "id"),);
324        let mock_row = MockRow {
325            values: vec![Value::String("not a number".to_string())],
326        };
327
328        let result = selection.decode(&mock_row);
329        assert!(result.is_err());
330    }
331}