use std::fmt;
use std::str::FromStr;
use crate::DataType;
use crate::Error;
use crate::ErrorKind;
use crate::protocol::ResultSetData;
use crate::protocol::StatementResultSet;
#[derive(Debug, Clone)]
pub struct Schema {
pub(crate) fields: Vec<FieldSchema>,
}
impl Schema {
pub fn fields(&self) -> &[FieldSchema] {
&self.fields
}
}
#[derive(Debug, Clone)]
pub struct FieldSchema {
pub(crate) name: String,
pub(crate) data_type: DataType,
}
impl FieldSchema {
pub fn name(&self) -> &str {
&self.name
}
pub fn data_type(&self) -> DataType {
self.data_type
}
}
#[derive(Debug, Clone)]
pub struct ResultSet {
schema: Schema,
num_rows: usize,
data: ResultSetData,
}
impl ResultSet {
pub fn num_rows(&self) -> usize {
self.num_rows
}
pub fn schema(&self) -> &Schema {
&self.schema
}
pub fn json_rows(&self) -> Option<&[Vec<Option<String>>]> {
match &self.data {
ResultSetData::Json { rows } => Some(rows),
}
}
pub fn into_values(self) -> Result<Vec<Vec<Value>>, Error> {
let rows = match self.data {
ResultSetData::Json { rows } => rows,
};
let num_rows = self.num_rows;
let num_fields = self.schema.fields.len();
assert_eq!(rows.len(), num_rows);
let mut values = Vec::with_capacity(num_rows);
for row in rows {
assert_eq!(row.len(), num_fields);
let mut value_row = Vec::with_capacity(num_fields);
for (i, cell) in row.into_iter().enumerate() {
let Some(value) = cell else {
value_row.push(Value::Null);
continue;
};
let value = match self.schema.fields[i].data_type() {
DataType::Int => Value::Int(i64::from_str(&value).map_err(|err| {
Error::new(
ErrorKind::Unexpected,
format!("failed to parse int value: {err}"),
)
})?),
DataType::UInt => Value::UInt(u64::from_str(&value).map_err(|err| {
Error::new(
ErrorKind::Unexpected,
format!("failed to parse uint value: {err}"),
)
})?),
DataType::Float => Value::Float(f64::from_str(&value).map_err(|err| {
Error::new(
ErrorKind::Unexpected,
format!("failed to parse float value: {err}"),
)
})?),
DataType::Timestamp => {
Value::Timestamp(jiff::Timestamp::from_str(&value).map_err(|err| {
Error::new(
ErrorKind::Unexpected,
format!("failed to parse timestamp value: {err}"),
)
})?)
}
DataType::Interval => {
Value::Interval(jiff::SignedDuration::from_str(&value).map_err(|err| {
Error::new(
ErrorKind::Unexpected,
format!("failed to parse interval value: {err}"),
)
})?)
}
DataType::Boolean => Value::Boolean(bool::from_str(&value).map_err(|err| {
Error::new(
ErrorKind::Unexpected,
format!("failed to parse boolean value: {err}"),
)
})?),
DataType::String => Value::String(value),
DataType::Binary => Value::Binary(value),
DataType::Array => Value::Array(value),
DataType::Object => Value::Object(value),
DataType::Any => Value::Any(value),
DataType::Null => unreachable!("null values must be None in rows"),
};
value_row.push(value);
}
values.push(value_row);
}
Ok(values)
}
pub(crate) fn from_statement_result_set(result_set: StatementResultSet) -> ResultSet {
ResultSet {
num_rows: result_set.metadata.num_rows,
schema: Schema {
fields: result_set
.metadata
.fields
.into_iter()
.map(|field| FieldSchema {
name: field.name,
data_type: field.data_type,
})
.collect(),
},
data: result_set.data,
}
}
}
#[derive(Clone)]
pub enum Value {
Int(i64),
UInt(u64),
Float(f64),
Timestamp(jiff::Timestamp),
Interval(jiff::SignedDuration),
Boolean(bool),
String(String),
Binary(String),
Array(String),
Object(String),
Any(String),
Null,
}
impl fmt::Debug for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Int(v) => write!(f, "{v}"),
Value::UInt(v) => write!(f, "{v}"),
Value::Float(v) => write!(f, "{v:?}"),
Value::Timestamp(v) => format_timestamp(f, v),
Value::Interval(v) => format_interval(f, v),
Value::Boolean(v) => write!(f, "{v}"),
Value::String(v) => quote_string(f, v, '\''),
Value::Binary(v) => write!(f, "{}", hex::encode_upper(v)),
Value::Array(v) => write!(f, "{v}"),
Value::Object(v) => write!(f, "{v}"),
Value::Any(v) => write!(f, "{v}"),
Value::Null => write!(f, "NULL"),
}
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Int(v) => write!(f, "{v}"),
Value::UInt(v) => write!(f, "{v}"),
Value::Float(v) => write!(f, "{v:?}"),
Value::Timestamp(v) => format_timestamp(f, v),
Value::Interval(v) => format_interval(f, v),
Value::Boolean(v) => write!(f, "{v}"),
Value::String(v) => write!(f, "{v}"),
Value::Binary(v) => write!(f, "{}", hex::encode_upper(v)),
Value::Array(v) => write!(f, "{v}"),
Value::Object(v) => write!(f, "{v}"),
Value::Any(v) => write!(f, "{v}"),
Value::Null => write!(f, "NULL"),
}
}
}
fn format_timestamp(f: &mut fmt::Formatter<'_>, ts: &jiff::Timestamp) -> fmt::Result {
use jiff::fmt::StdFmtWrite;
use jiff::fmt::temporal;
let precision = f.precision().map(|p| u8::try_from(p).unwrap_or(u8::MAX));
temporal::DateTimePrinter::new()
.precision(precision)
.print_timestamp(ts, StdFmtWrite(f))
.map_err(|_| fmt::Error)
}
fn format_interval(f: &mut fmt::Formatter<'_>, d: &jiff::SignedDuration) -> fmt::Result {
use jiff::fmt::StdFmtWrite;
use jiff::fmt::friendly;
friendly::SpanPrinter::new()
.spacing(friendly::Spacing::None)
.designator(friendly::Designator::Compact)
.print_duration(d, StdFmtWrite(f))
.map_err(|_| fmt::Error)
}
fn quote_string(f: &mut fmt::Formatter<'_>, s: &str, quote: char) -> fmt::Result {
write!(f, "{quote}")?;
for c in s.chars() {
match c {
'\t' => write!(f, "\\t")?,
'\r' => write!(f, "\\r")?,
'\n' => write!(f, "\\n")?,
'\\' => write!(f, "\\\\")?,
'\x00'..='\x1F' => write!(f, "\\x{:02x}", c as u8)?,
c => {
if c != quote {
write!(f, "{c}")?
} else {
write!(f, "\\{quote}")?
}
}
}
}
write!(f, "{quote}")
}