use crate::{
error::Error,
tds::codec::{ColumnData, FixedLenType, TokenRow, TypeInfo, VarLenType},
FromSql,
};
use std::{fmt::Display, sync::Arc};
#[derive(Debug, Clone)]
pub struct Column {
pub(crate) name: String,
pub(crate) column_type: ColumnType,
}
impl Column {
pub fn new(name: String, column_type: ColumnType) -> Self {
Self { name, column_type }
}
pub fn name(&self) -> &str {
&self.name
}
pub fn column_type(&self) -> ColumnType {
self.column_type
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColumnType {
Null,
Bit,
Int1,
Int2,
Int4,
Int8,
Datetime4,
Float4,
Float8,
Money,
Datetime,
Money4,
Guid,
Intn,
Bitn,
Decimaln,
Numericn,
Floatn,
Datetimen,
Daten,
Timen,
Datetime2,
DatetimeOffsetn,
BigVarBin,
BigVarChar,
BigBinary,
BigChar,
NVarchar,
NChar,
Xml,
Udt,
Text,
Image,
NText,
SSVariant,
}
impl From<&TypeInfo> for ColumnType {
fn from(ti: &TypeInfo) -> Self {
match ti {
TypeInfo::FixedLen(flt) => match flt {
FixedLenType::Int1 => Self::Int1,
FixedLenType::Bit => Self::Bit,
FixedLenType::Int2 => Self::Int2,
FixedLenType::Int4 => Self::Int4,
FixedLenType::Datetime4 => Self::Datetime4,
FixedLenType::Float4 => Self::Float4,
FixedLenType::Money => Self::Money,
FixedLenType::Datetime => Self::Datetime,
FixedLenType::Float8 => Self::Float8,
FixedLenType::Money4 => Self::Money4,
FixedLenType::Int8 => Self::Int8,
FixedLenType::Null => Self::Null,
},
TypeInfo::VarLenSized(cx) => match cx.r#type() {
VarLenType::Guid => Self::Guid,
VarLenType::Intn => match cx.len() {
1 => Self::Int1,
2 => Self::Int2,
4 => Self::Int4,
8 => Self::Int8,
_ => Self::Intn,
},
VarLenType::Bitn => Self::Bitn,
VarLenType::Decimaln => Self::Decimaln,
VarLenType::Numericn => Self::Numericn,
VarLenType::Floatn => match cx.len() {
4 => Self::Float4,
8 => Self::Float8,
_ => Self::Floatn,
},
VarLenType::Money => Self::Money,
VarLenType::Datetimen => Self::Datetimen,
#[cfg(feature = "tds73")]
VarLenType::Daten => Self::Daten,
#[cfg(feature = "tds73")]
VarLenType::Timen => Self::Timen,
#[cfg(feature = "tds73")]
VarLenType::Datetime2 => Self::Datetime2,
#[cfg(feature = "tds73")]
VarLenType::DatetimeOffsetn => Self::DatetimeOffsetn,
VarLenType::BigVarBin => Self::BigVarBin,
VarLenType::BigVarChar => Self::BigVarChar,
VarLenType::BigBinary => Self::BigBinary,
VarLenType::BigChar => Self::BigChar,
VarLenType::NVarchar => Self::NVarchar,
VarLenType::NChar => Self::NChar,
VarLenType::Xml => Self::Xml,
VarLenType::Udt => Self::Udt,
VarLenType::Text => Self::Text,
VarLenType::Image => Self::Image,
VarLenType::NText => Self::NText,
VarLenType::SSVariant => Self::SSVariant,
},
TypeInfo::VarLenSizedPrecision { ty, .. } => match ty {
VarLenType::Guid => Self::Guid,
VarLenType::Intn => Self::Intn,
VarLenType::Bitn => Self::Bitn,
VarLenType::Decimaln => Self::Decimaln,
VarLenType::Numericn => Self::Numericn,
VarLenType::Floatn => Self::Floatn,
VarLenType::Money => Self::Money,
VarLenType::Datetimen => Self::Datetimen,
#[cfg(feature = "tds73")]
VarLenType::Daten => Self::Daten,
#[cfg(feature = "tds73")]
VarLenType::Timen => Self::Timen,
#[cfg(feature = "tds73")]
VarLenType::Datetime2 => Self::Datetime2,
#[cfg(feature = "tds73")]
VarLenType::DatetimeOffsetn => Self::DatetimeOffsetn,
VarLenType::BigVarBin => Self::BigVarBin,
VarLenType::BigVarChar => Self::BigVarChar,
VarLenType::BigBinary => Self::BigBinary,
VarLenType::BigChar => Self::BigChar,
VarLenType::NVarchar => Self::NVarchar,
VarLenType::NChar => Self::NChar,
VarLenType::Xml => Self::Xml,
VarLenType::Udt => Self::Udt,
VarLenType::Text => Self::Text,
VarLenType::Image => Self::Image,
VarLenType::NText => Self::NText,
VarLenType::SSVariant => Self::SSVariant,
},
TypeInfo::Xml { .. } => Self::Xml,
}
}
}
#[derive(Debug)]
pub struct Row {
pub(crate) columns: Arc<Vec<Column>>,
pub(crate) data: TokenRow<'static>,
pub(crate) result_index: usize,
}
pub trait QueryIdx
where
Self: Display,
{
fn idx(&self, row: &Row) -> Option<usize>;
}
impl QueryIdx for usize {
fn idx(&self, _row: &Row) -> Option<usize> {
Some(*self)
}
}
impl QueryIdx for &str {
fn idx(&self, row: &Row) -> Option<usize> {
row.columns.iter().position(|c| c.name() == *self)
}
}
impl Row {
pub fn columns(&self) -> &[Column] {
&self.columns
}
pub fn cells(&self) -> impl Iterator<Item = (&Column, &ColumnData<'static>)> {
self.columns().iter().zip(self.data.iter())
}
pub fn result_index(&self) -> usize {
self.result_index
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.data.len()
}
#[track_caller]
pub fn get<'a, R, I>(&'a self, idx: I) -> Option<R>
where
R: FromSql<'a>,
I: QueryIdx,
{
self.try_get(idx).unwrap()
}
#[track_caller]
pub fn try_get<'a, R, I>(&'a self, idx: I) -> crate::Result<Option<R>>
where
R: FromSql<'a>,
I: QueryIdx,
{
let idx = idx.idx(self).ok_or_else(|| {
Error::Conversion(format!("Could not find column with index {}", idx).into())
})?;
let data = self.data.get(idx).unwrap();
R::from_sql(data)
}
}
impl IntoIterator for Row {
type Item = ColumnData<'static>;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.data.into_iter()
}
}