use std::convert::TryFrom;
use std::convert::TryInto;
use crate::Error;
use crate::FromSpanner;
use crate::StructType;
use crate::Transaction;
use crate::Value;
use google_api_proto::google::spanner::v1 as proto;
pub trait RowIndex: private::Sealed {
#[doc(hidden)]
fn index(&self, struct_type: &StructType) -> Option<usize>;
}
impl RowIndex for usize {
fn index(&self, struct_type: &StructType) -> Option<usize> {
if *self < struct_type.fields().len() {
Some(*self)
} else {
None
}
}
}
impl RowIndex for str {
fn index(&self, struct_type: &StructType) -> Option<usize> {
struct_type.field_index(self)
}
}
impl<'a, T> RowIndex for &'a T
where
T: RowIndex + ?Sized,
{
fn index(&self, struct_type: &StructType) -> Option<usize> {
<T as RowIndex>::index(self, struct_type)
}
}
mod private {
pub trait Sealed {}
impl Sealed for usize {}
impl Sealed for str {}
impl<'a, T> Sealed for &'a T where T: ?Sized + Sealed {}
}
pub struct Row<'a> {
row_type: &'a StructType,
columns: &'a [Value],
}
impl<'a> Row<'a> {
pub fn row_type(&'a self) -> &'a StructType {
self.row_type
}
pub fn is_empty(&'a self) -> bool {
self.row_type.fields().is_empty()
}
pub fn get<T, R>(&'a self, row_index: R) -> Result<T, Error>
where
T: FromSpanner<'a>,
R: RowIndex + std::fmt::Display,
{
self.get_impl(&row_index)
}
pub fn get_unchecked<T, R>(&'a self, row_index: R) -> T
where
T: FromSpanner<'a>,
R: RowIndex + std::fmt::Display,
{
match self.get_impl(&row_index) {
Ok(value) => value,
Err(error) => panic!(
"unexpected error while reading column {}: {}",
row_index, error
),
}
}
fn get_impl<T, R>(&'a self, row_index: &R) -> Result<T, Error>
where
T: FromSpanner<'a>,
R: RowIndex + std::fmt::Display,
{
match row_index.index(self.row_type) {
None => Err(Error::Codec(format!("no such column {}", row_index))),
Some(index) => <T as FromSpanner>::from_spanner_nullable(&self.columns[index]),
}
}
}
impl<'a> std::fmt::Debug for Row<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Row")
.field("columns", &self.row_type)
.finish()
}
}
#[derive(Debug)]
pub(crate) struct Stats {
pub(crate) row_count: Option<i64>,
}
impl TryFrom<proto::ResultSetStats> for Stats {
type Error = Error;
fn try_from(value: proto::ResultSetStats) -> Result<Self, Self::Error> {
let row_count = match value.row_count {
Some(proto::result_set_stats::RowCount::RowCountExact(exact)) => Ok(Some(exact)),
Some(proto::result_set_stats::RowCount::RowCountLowerBound(_)) => Err(Error::Client(
"lower bound row count is unsupported".to_string(),
)),
None => Ok(None),
}?;
Ok(Self { row_count })
}
}
#[derive(Debug)]
pub struct ResultSet {
row_type: StructType,
rows: Vec<Vec<Value>>,
pub(crate) transaction: Option<Transaction>,
pub(crate) stats: Stats,
}
impl ResultSet {
pub fn iter(&self) -> impl Iterator<Item = Row<'_>> {
self.rows.iter().map(move |columns| Row {
row_type: &self.row_type,
columns,
})
}
}
impl TryFrom<proto::ResultSet> for ResultSet {
type Error = crate::Error;
fn try_from(value: proto::ResultSet) -> Result<Self, Self::Error> {
let stats = value.stats.unwrap_or_default().try_into()?;
let metadata = value.metadata.unwrap_or_default();
let row_type: StructType = metadata.row_type.unwrap_or_default().try_into()?;
let rows = value
.rows
.iter()
.map(|row| {
row.values
.iter()
.zip(row_type.types())
.map(|(value, tpe)| Value::try_from(tpe, value.clone()))
.collect()
})
.collect::<Result<Vec<Vec<Value>>, Error>>()?;
Ok(Self {
row_type,
rows,
transaction: metadata.transaction.map(Transaction::from),
stats,
})
}
}