1use crate::backend::{QueryResult, Row};
5use crate::error::QueryError;
6use crate::model::Model;
7use crate::types::{DbType, decode};
8use crate::value::Value;
9
10pub trait FromValues: Sized {
19 fn from_values(row: &Row) -> Result<Self, QueryError>;
24}
25
26impl<T: DbType> FromValues for T {
27 fn from_values(row: &Row) -> Result<Self, QueryError> {
28 row.decode_at(0)
29 }
30}
31
32macro_rules! tuple_from_values {
33 ($($name:ident : $index:tt),+) => {
34 impl<$($name: DbType),+> FromValues for ($($name,)+) {
35 fn from_values(row: &Row) -> Result<Self, QueryError> {
36 Ok(($(row.decode_at::<$name>($index)?,)+))
37 }
38 }
39 };
40}
41tuple_from_values!(A: 0);
42tuple_from_values!(A: 0, B: 1);
43tuple_from_values!(A: 0, B: 1, C: 2);
44tuple_from_values!(A: 0, B: 1, C: 2, D: 3);
45tuple_from_values!(A: 0, B: 1, C: 2, D: 3, E: 4);
46tuple_from_values!(A: 0, B: 1, C: 2, D: 3, E: 4, F: 5);
47
48impl Row {
49 pub fn get_as<T: DbType>(&self, column: &str) -> Result<T, QueryError> {
54 let value = self
55 .get(column)
56 .cloned()
57 .ok_or_else(|| QueryError::Decode {
58 column: column.to_owned(),
59 reason: "column missing from result".into(),
60 })?;
61 decode(column, value)
62 }
63
64 pub fn decode_at<T: DbType>(&self, index: usize) -> Result<T, QueryError> {
69 let (name, value) = self.iter().nth(index).ok_or_else(|| QueryError::Decode {
70 column: format!("#{index}"),
71 reason: format!("row has only {} columns", self.len()),
72 })?;
73 decode(name, value.clone())
74 }
75}
76
77impl QueryResult {
78 pub fn decode<M: Model>(&self) -> Result<Vec<M>, QueryError> {
83 self.rows.iter().map(|row| M::from_row(row, "")).collect()
84 }
85
86 pub fn decode_values<T: FromValues>(&self) -> Result<Vec<T>, QueryError> {
91 self.rows.iter().map(T::from_values).collect()
92 }
93
94 pub fn scalar(&self) -> Option<&Value> {
96 self.rows.first()?.iter().next().map(|(_, v)| v)
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 fn row() -> Row {
105 Row::new(vec![
106 ("name".into(), Value::Text("ann".into())),
107 ("age".into(), Value::Int(30)),
108 ("nick".into(), Value::Null),
109 ])
110 }
111
112 #[test]
113 fn tuples_decode_by_position() {
114 let (name, age, nick) =
115 <(String, i64, Option<String>)>::from_values(&row()).unwrap_or_default();
116 assert_eq!((name.as_str(), age, nick), ("ann", 30, None));
117 assert_eq!(String::from_values(&row()), Ok("ann".to_owned()));
118 }
119
120 #[test]
121 fn errors_name_the_offending_column() {
122 let err = <(i64,)>::from_values(&row());
123 assert!(matches!(err, Err(QueryError::Decode { column, .. }) if column == "name"));
124 let short = <(String, i64, Option<String>, i64)>::from_values(&row());
125 assert!(matches!(short, Err(QueryError::Decode { column, .. }) if column == "#3"));
126 }
127
128 #[test]
129 fn rows_decode_by_name_and_decimals_fit_integers() {
130 assert_eq!(row().get_as::<i64>("age"), Ok(30));
131 assert!(row().get_as::<i64>("missing").is_err());
132 let sum = Row::new(vec![(
133 "s".into(),
134 Value::Decimal(rust_decimal::Decimal::new(27, 0)),
135 )]);
136 assert_eq!(sum.get_as::<i64>("s"), Ok(27));
137 assert_eq!(sum.get_as::<f64>("s"), Ok(27.0));
138 let fractional = Row::new(vec![(
139 "s".into(),
140 Value::Decimal(rust_decimal::Decimal::new(275, 1)),
141 )]);
142 assert!(fractional.get_as::<i64>("s").is_err());
143 }
144}