1use crate::Value;
54use std::collections::HashMap;
55
56#[derive(Debug, Clone, PartialEq)]
58pub enum QueryError {
59 ColumnCountMismatch {
61 expected: usize,
63 actual: usize,
65 },
66 TypeMismatch {
68 column: std::borrow::Cow<'static, str>,
70 expected: &'static str,
72 },
73 MissingColumn {
75 column: &'static str,
77 },
78 Custom(String),
80}
81
82impl std::fmt::Display for QueryError {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 match self {
85 QueryError::ColumnCountMismatch { expected, actual } => {
86 write!(f, "列数不匹配: 期望 {}, 实际 {}", expected, actual)
87 }
88 QueryError::TypeMismatch { column, expected } => {
89 write!(f, "列 {:?} 类型不匹配, 期望 {}", column, expected)
90 }
91 QueryError::MissingColumn { column } => {
92 write!(f, "缺少列: {}", column)
93 }
94 QueryError::Custom(msg) => write!(f, "{}", msg),
95 }
96 }
97}
98
99impl std::error::Error for QueryError {}
100
101#[derive(Debug, Clone)]
106pub struct RowDesc {
107 pub columns: Vec<String>,
109}
110
111impl RowDesc {
112 pub fn new(columns: Vec<String>) -> Self {
114 Self { columns }
115 }
116
117 pub fn len(&self) -> usize {
119 self.columns.len()
120 }
121
122 pub fn is_empty(&self) -> bool {
124 self.columns.is_empty()
125 }
126
127 pub fn index_of(&self, name: &str) -> Option<usize> {
129 self.columns.iter().position(|c| c == name)
130 }
131}
132
133pub trait Queryable: Sized {
138 fn from_values(values: Vec<Value>) -> Result<Self, QueryError>;
140
141 fn from_values_with_desc(values: Vec<Value>, desc: &RowDesc) -> Result<Self, QueryError> {
143 if values.len() != desc.len() {
144 return Err(QueryError::ColumnCountMismatch {
145 expected: desc.len(),
146 actual: values.len(),
147 });
148 }
149 Self::from_values(values)
150 }
151}
152
153pub trait FromRow: Sized {
158 fn from_row(row: HashMap<String, Value>) -> Result<Self, QueryError>;
160}
161
162impl Queryable for Value {
166 fn from_values(values: Vec<Value>) -> Result<Self, QueryError> {
167 if values.len() != 1 {
168 return Err(QueryError::ColumnCountMismatch {
169 expected: 1,
170 actual: values.len(),
171 });
172 }
173 Ok(values.into_iter().next().expect("len==1 verified above"))
174 }
175}
176
177impl Queryable for (Value, Value) {
179 fn from_values(values: Vec<Value>) -> Result<Self, QueryError> {
180 if values.len() != 2 {
181 return Err(QueryError::ColumnCountMismatch {
182 expected: 2,
183 actual: values.len(),
184 });
185 }
186 let mut iter = values.into_iter();
187 Ok((iter.next().expect("len==2 verified above"), iter.next().expect("len==2 verified above")))
188 }
189}
190
191impl Queryable for (Value, Value, Value) {
193 fn from_values(values: Vec<Value>) -> Result<Self, QueryError> {
194 if values.len() != 3 {
195 return Err(QueryError::ColumnCountMismatch {
196 expected: 3,
197 actual: values.len(),
198 });
199 }
200 let mut iter = values.into_iter();
201 Ok((
202 iter.next().expect("len==3 verified above"),
203 iter.next().expect("len==3 verified above"),
204 iter.next().expect("len==3 verified above"),
205 ))
206 }
207}
208
209pub fn value_as_i64(v: &Value) -> Option<i64> {
213 v.as_i64()
214}
215
216pub fn value_as_f64(v: &Value) -> Option<f64> {
218 v.as_f64()
219}
220
221pub fn value_as_string(v: &Value) -> Option<String> {
223 v.as_str().map(|s| s.to_string())
224}
225
226pub fn value_as_bool(v: &Value) -> Option<bool> {
228 v.as_bool()
229}
230
231pub fn value_as_nullable_i64(v: &Value) -> Option<i64> {
233 if v.is_null() {
234 None
235 } else {
236 v.as_i64()
237 }
238}
239
240pub fn value_as_nullable_string(v: &Value) -> Option<String> {
242 if v.is_null() {
243 None
244 } else {
245 v.as_str().map(|s| s.to_string())
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252
253 #[derive(Debug, Default, PartialEq)]
256 struct UserRow {
257 id: i64,
258 name: String,
259 }
260
261 impl Queryable for UserRow {
262 fn from_values(values: Vec<Value>) -> Result<Self, QueryError> {
263 if values.len() != 2 {
264 return Err(QueryError::ColumnCountMismatch {
265 expected: 2,
266 actual: values.len(),
267 });
268 }
269 let id = values[0].as_i64().ok_or(QueryError::TypeMismatch {
270 column: "0".into(),
271 expected: "i64",
272 })?;
273 let name = values[1]
274 .as_str()
275 .ok_or(QueryError::TypeMismatch {
276 column: "1".into(),
277 expected: "String",
278 })?
279 .to_string();
280 Ok(UserRow { id, name })
281 }
282 }
283
284 impl FromRow for UserRow {
285 fn from_row(row: HashMap<String, Value>) -> Result<Self, QueryError> {
286 let id = row
287 .get("id")
288 .ok_or(QueryError::MissingColumn { column: "id" })?
289 .as_i64()
290 .ok_or(QueryError::TypeMismatch {
291 column: "id".into(),
292 expected: "i64",
293 })?;
294 let name = row
295 .get("name")
296 .ok_or(QueryError::MissingColumn { column: "name" })?
297 .as_str()
298 .ok_or(QueryError::TypeMismatch {
299 column: "name".into(),
300 expected: "String",
301 })?
302 .to_string();
303 Ok(UserRow { id, name })
304 }
305 }
306
307 #[test]
310 fn test_query_error_display() {
311 let e = QueryError::ColumnCountMismatch {
312 expected: 3,
313 actual: 2,
314 };
315 assert!(format!("{}", e).contains("3"));
316 assert!(format!("{}", e).contains("2"));
317
318 let e = QueryError::TypeMismatch {
319 column: "age".into(),
320 expected: "i64",
321 };
322 assert!(format!("{}", e).contains("age"));
323
324 let e = QueryError::MissingColumn { column: "id" };
325 assert!(format!("{}", e).contains("id"));
326
327 let e = QueryError::Custom("custom".into());
328 assert_eq!(format!("{}", e), "custom");
329 }
330
331 #[test]
334 fn test_row_desc_basic() {
335 let desc = RowDesc::new(vec!["id".into(), "name".into(), "age".into()]);
336 assert_eq!(desc.len(), 3);
337 assert!(!desc.is_empty());
338 assert_eq!(desc.index_of("name"), Some(1));
339 assert_eq!(desc.index_of("missing"), None);
340 }
341
342 #[test]
343 fn test_row_desc_empty() {
344 let desc = RowDesc::new(vec![]);
345 assert!(desc.is_empty());
346 assert_eq!(desc.len(), 0);
347 }
348
349 #[test]
352 fn test_user_row_from_values_success() {
353 let row =
354 UserRow::from_values(vec![Value::I64(42), Value::String("Alice".into())]).unwrap();
355 assert_eq!(row.id, 42);
356 assert_eq!(row.name, "Alice");
357 }
358
359 #[test]
360 fn test_user_row_from_values_count_mismatch() {
361 let result = UserRow::from_values(vec![Value::I64(42)]);
362 assert!(matches!(
363 result,
364 Err(QueryError::ColumnCountMismatch {
365 expected: 2,
366 actual: 1
367 })
368 ));
369 }
370
371 #[test]
372 fn test_user_row_from_values_type_mismatch() {
373 let result = UserRow::from_values(vec![
374 Value::String("not_an_int".into()),
375 Value::String("Alice".into()),
376 ]);
377 assert!(matches!(result, Err(QueryError::TypeMismatch { .. })));
378 }
379
380 #[test]
381 fn test_user_row_from_values_with_desc() {
382 let desc = RowDesc::new(vec!["id".into(), "name".into()]);
383 let row =
384 UserRow::from_values_with_desc(vec![Value::I64(1), Value::String("Bob".into())], &desc)
385 .unwrap();
386 assert_eq!(row.id, 1);
387 assert_eq!(row.name, "Bob");
388 }
389
390 #[test]
391 fn test_user_row_from_values_with_desc_mismatch() {
392 let desc = RowDesc::new(vec!["id".into(), "name".into(), "age".into()]);
393 let result =
394 UserRow::from_values_with_desc(vec![Value::I64(1), Value::String("Bob".into())], &desc);
395 assert!(matches!(
396 result,
397 Err(QueryError::ColumnCountMismatch { .. })
398 ));
399 }
400
401 #[test]
404 fn test_user_row_from_row_success() {
405 let mut map = HashMap::new();
406 map.insert("id".into(), Value::I64(99));
407 map.insert("name".into(), Value::String("Charlie".into()));
408 let row = UserRow::from_row(map).unwrap();
409 assert_eq!(row.id, 99);
410 assert_eq!(row.name, "Charlie");
411 }
412
413 #[test]
414 fn test_user_row_from_row_missing_column() {
415 let mut map = HashMap::new();
416 map.insert("id".into(), Value::I64(99));
417 let result = UserRow::from_row(map);
419 assert!(matches!(
420 result,
421 Err(QueryError::MissingColumn { column: "name" })
422 ));
423 }
424
425 #[test]
426 fn test_user_row_from_row_extra_columns_ignored() {
427 let mut map = HashMap::new();
428 map.insert("id".into(), Value::I64(1));
429 map.insert("name".into(), Value::String("X".into()));
430 map.insert("extra".into(), Value::String("ignored".into()));
431 let row = UserRow::from_row(map).unwrap();
432 assert_eq!(row.id, 1);
433 }
434
435 #[test]
438 fn test_value_queryable_single() {
439 let v = Value::from_values(vec![Value::I64(42)]).unwrap();
440 assert_eq!(v.as_i64(), Some(42));
441 }
442
443 #[test]
444 fn test_value_queryable_count_mismatch() {
445 let result = Value::from_values(vec![Value::I64(1), Value::I64(2)]);
446 assert!(matches!(
447 result,
448 Err(QueryError::ColumnCountMismatch { .. })
449 ));
450 }
451
452 #[test]
453 fn test_tuple_2_queryable() {
454 let (a, b) =
455 <(Value, Value)>::from_values(vec![Value::I64(1), Value::String("hello".into())])
456 .unwrap();
457 assert_eq!(a.as_i64(), Some(1));
458 assert_eq!(b.as_str(), Some("hello"));
459 }
460
461 #[test]
462 fn test_tuple_3_queryable() {
463 let (a, b, c) = <(Value, Value, Value)>::from_values(vec![
464 Value::I64(1),
465 Value::String("two".into()),
466 Value::F64(3.5),
467 ])
468 .unwrap();
469 assert_eq!(a.as_i64(), Some(1));
470 assert_eq!(b.as_str(), Some("two"));
471 assert_eq!(c.as_f64(), Some(3.5));
472 }
473
474 #[test]
477 fn test_value_helpers() {
478 assert_eq!(value_as_i64(&Value::I64(42)), Some(42));
479 assert_eq!(value_as_i64(&Value::String("42".into())), Some(42));
480 assert_eq!(value_as_f64(&Value::F64(3.5)), Some(3.5));
481 assert_eq!(
482 value_as_string(&Value::String("hi".into())),
483 Some("hi".into())
484 );
485 assert_eq!(value_as_bool(&Value::Bool(true)), Some(true));
486 }
487
488 #[test]
489 fn test_nullable_helpers() {
490 assert_eq!(value_as_nullable_i64(&Value::Null), None);
491 assert_eq!(value_as_nullable_i64(&Value::I64(42)), Some(42));
492 assert_eq!(value_as_nullable_string(&Value::Null), None);
493 assert_eq!(
494 value_as_nullable_string(&Value::String("hi".into())),
495 Some("hi".into())
496 );
497 }
498
499 #[test]
502 fn test_full_flow_queryable() {
503 let values = vec![Value::I64(1), Value::String("Alice".into())];
505 let row = UserRow::from_values(values).unwrap();
506 assert_eq!(
507 row,
508 UserRow {
509 id: 1,
510 name: "Alice".into()
511 }
512 );
513 }
514
515 #[test]
516 fn test_full_flow_from_row_with_extra_data() {
517 let mut map = HashMap::new();
519 map.insert("id".into(), Value::I64(7));
520 map.insert("name".into(), Value::String("Bob".into()));
521 map.insert("email".into(), Value::String("bob@example.com".into()));
522 map.insert("created_at".into(), Value::String("2026-01-01".into()));
523
524 let row = UserRow::from_row(map).unwrap();
525 assert_eq!(row.id, 7);
526 assert_eq!(row.name, "Bob");
527 }
528}