1use std::convert::TryFrom;
2use std::convert::TryInto;
3
4use crate::Error;
5use crate::FromSpanner;
6use crate::StructType;
7use crate::Transaction;
8use crate::Value;
9use google_api_proto::google::spanner::v1 as proto;
10
11pub trait RowIndex: private::Sealed {
15 #[doc(hidden)]
16 fn index(&self, struct_type: &StructType) -> Option<usize>;
17}
18
19impl RowIndex for usize {
21 fn index(&self, struct_type: &StructType) -> Option<usize> {
22 if *self < struct_type.fields().len() {
23 Some(*self)
24 } else {
25 None
26 }
27 }
28}
29
30impl RowIndex for str {
32 fn index(&self, struct_type: &StructType) -> Option<usize> {
33 struct_type.field_index(self)
34 }
35}
36
37impl<'a, T> RowIndex for &'a T
38where
39 T: RowIndex + ?Sized,
40{
41 fn index(&self, struct_type: &StructType) -> Option<usize> {
42 <T as RowIndex>::index(self, struct_type)
43 }
44}
45
46mod private {
47 pub trait Sealed {}
48
49 impl Sealed for usize {}
50 impl Sealed for str {}
51 impl<'a, T> Sealed for &'a T where T: ?Sized + Sealed {}
52}
53
54pub struct Row<'a> {
58 row_type: &'a StructType,
59 columns: &'a [Value],
60}
61
62impl<'a> Row<'a> {
63 pub fn row_type(&'a self) -> &'a StructType {
65 self.row_type
66 }
67
68 pub fn is_empty(&'a self) -> bool {
70 self.row_type.fields().is_empty()
71 }
72
73 pub fn get<T, R>(&'a self, row_index: R) -> Result<T, Error>
77 where
78 T: FromSpanner<'a>,
79 R: RowIndex + std::fmt::Display,
80 {
81 self.get_impl(&row_index)
82 }
83
84 pub fn get_unchecked<T, R>(&'a self, row_index: R) -> T
90 where
91 T: FromSpanner<'a>,
92 R: RowIndex + std::fmt::Display,
93 {
94 match self.get_impl(&row_index) {
95 Ok(value) => value,
96 Err(error) => panic!(
97 "unexpected error while reading column {}: {}",
98 row_index, error
99 ),
100 }
101 }
102
103 fn get_impl<T, R>(&'a self, row_index: &R) -> Result<T, Error>
104 where
105 T: FromSpanner<'a>,
106 R: RowIndex + std::fmt::Display,
107 {
108 match row_index.index(self.row_type) {
109 None => Err(Error::Codec(format!("no such column {}", row_index))),
110 Some(index) => <T as FromSpanner>::from_spanner_nullable(&self.columns[index]),
111 }
112 }
113}
114
115impl<'a> std::fmt::Debug for Row<'a> {
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 f.debug_struct("Row")
119 .field("columns", &self.row_type)
120 .finish()
121 }
122}
123
124#[derive(Debug)]
125pub(crate) struct Stats {
126 pub(crate) row_count: Option<i64>,
127}
128
129impl TryFrom<proto::ResultSetStats> for Stats {
130 type Error = Error;
131
132 fn try_from(value: proto::ResultSetStats) -> Result<Self, Self::Error> {
133 let row_count = match value.row_count {
134 Some(proto::result_set_stats::RowCount::RowCountExact(exact)) => Ok(Some(exact)),
135 Some(proto::result_set_stats::RowCount::RowCountLowerBound(_)) => Err(Error::Client(
136 "lower bound row count is unsupported".to_string(),
137 )),
138 None => Ok(None),
139 }?;
140 Ok(Self { row_count })
141 }
142}
143
144#[derive(Debug)]
149pub struct ResultSet {
150 row_type: StructType,
151 rows: Vec<Vec<Value>>,
152 pub(crate) transaction: Option<Transaction>,
153 pub(crate) stats: Stats,
154}
155
156impl ResultSet {
157 pub fn iter(&self) -> impl Iterator<Item = Row<'_>> {
159 self.rows.iter().map(move |columns| Row {
160 row_type: &self.row_type,
161 columns,
162 })
163 }
164}
165
166impl TryFrom<proto::ResultSet> for ResultSet {
167 type Error = crate::Error;
168
169 fn try_from(value: proto::ResultSet) -> Result<Self, Self::Error> {
170 let stats = value.stats.unwrap_or_default().try_into()?;
171 let metadata = value.metadata.unwrap_or_default();
172 let row_type: StructType = metadata.row_type.unwrap_or_default().try_into()?;
173
174 let rows = value
175 .rows
176 .iter()
177 .map(|row| {
178 row.values
179 .iter()
180 .zip(row_type.types())
181 .map(|(value, tpe)| Value::try_from(tpe, value.clone()))
182 .collect()
183 })
184 .collect::<Result<Vec<Vec<Value>>, Error>>()?;
185
186 Ok(Self {
187 row_type,
188 rows,
189 transaction: metadata.transaction.map(Transaction::from),
190 stats,
191 })
192 }
193}