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