sqlx_cloudflare_do/
row.rs1use std::fmt::{self, Debug, Formatter};
2use std::sync::Arc;
3
4use sqlx_cloudflare_core::Value;
5use sqlx_core::column::ColumnIndex;
6use sqlx_core::error::Error;
7use sqlx_core::row::Row;
8
9use crate::{Do, DoColumn, DoValueRef};
10
11pub struct DoRow {
13 pub(crate) columns: Arc<[DoColumn]>,
15 pub(crate) values: Box<[Value]>,
16}
17
18impl DoRow {
19 pub(crate) fn from_result(
25 names: Vec<String>,
26 rows: Vec<Vec<Value>>,
27 ) -> Result<Vec<DoRow>, Error> {
28 sqlx_cloudflare_core::rows(
29 names,
30 rows,
31 |name, ordinal, type_info| DoColumn {
32 name,
33 ordinal,
34 type_info,
35 },
36 |columns, values| DoRow { columns, values },
37 )
38 }
39}
40
41impl Row for DoRow {
42 type Database = Do;
43
44 fn columns(&self) -> &[DoColumn] {
45 &self.columns
46 }
47
48 fn try_get_raw<I>(&self, index: I) -> Result<DoValueRef<'_>, Error>
49 where
50 I: ColumnIndex<Self>,
51 {
52 let index = index.index(self)?;
53 Ok(DoValueRef(&self.values[index]))
54 }
55}
56
57impl ColumnIndex<DoRow> for &'_ str {
58 fn index(&self, row: &DoRow) -> Result<usize, Error> {
62 row.columns
63 .iter()
64 .rposition(|column| column.name == *self)
65 .ok_or_else(|| Error::ColumnNotFound((*self).into()))
66 }
67}
68
69impl Debug for DoRow {
70 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
71 let mut map = f.debug_map();
72
73 for (column, value) in self.columns.iter().zip(self.values.iter()) {
74 map.entry(&column.name, value);
75 }
76
77 map.finish()
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use sqlx_cloudflare_core::Value;
84 use sqlx_core::row::Row;
85 use sqlx_core::type_info::TypeInfo;
86 use sqlx_core::value::ValueRef;
87
88 use super::DoRow;
89 use crate::DoTypeInfo;
90
91 #[test]
92 fn a_column_is_typed_by_its_first_non_null_value() {
93 let rows = DoRow::from_result(
94 vec!["id".into(), "name".into()],
95 vec![
96 vec![Value::Integer(1), Value::Null],
97 vec![Value::Integer(2), Value::Text("b".into())],
98 ],
99 )
100 .unwrap();
101
102 let types: Vec<_> = rows[0]
103 .columns()
104 .iter()
105 .map(|column| column.type_info)
106 .collect();
107 assert_eq!(types, [DoTypeInfo::Integer, DoTypeInfo::Text]);
108 }
109
110 #[test]
111 fn a_name_finds_the_last_column_that_has_it() {
112 let rows = DoRow::from_result(
113 vec!["id".into(), "id".into()],
114 vec![vec![Value::Integer(1), Value::Integer(2)]],
115 )
116 .unwrap();
117
118 let row = &rows[0];
119 assert_eq!(row.try_get::<i64, _>(0).unwrap(), 1);
120 assert_eq!(row.try_get::<i64, _>("id").unwrap(), 2);
121 assert!(row.try_get_raw("missing").is_err());
122 }
123
124 #[test]
125 fn a_value_carries_its_own_type() {
126 let rows = DoRow::from_result(
127 vec!["n".into()],
128 vec![vec![Value::Integer(1)], vec![Value::Real(1.5)]],
129 )
130 .unwrap();
131
132 assert_eq!(rows[1].try_get_raw(0).unwrap().type_info().name(), "REAL");
135 assert!(rows[1].try_get::<i64, _>(0).is_err());
136 assert!((rows[1].try_get::<f64, _>(0).unwrap() - 1.5).abs() < f64::EPSILON);
137 }
138}