use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DataType {
Integer,
String,
Boolean,
Float,
Bytes,
Hierarchical,
Timestamp,
Decimal,
Json,
Array(Box<DataType>),
Vector(usize),
}
#[derive(Debug, Clone)]
pub struct Column {
pub name: String,
pub data_type: DataType,
pub nullable: bool,
}
#[derive(Debug, Clone)]
pub struct Schema {
pub name: String,
pub columns: Vec<Column>,
}
impl Schema {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
columns: Vec::new(),
}
}
pub fn with_column(mut self, name: impl Into<String>, data_type: DataType) -> Self {
self.columns.push(Column {
name: name.into(),
data_type,
nullable: false,
});
self
}
pub fn with_nullable_column(mut self, name: impl Into<String>, data_type: DataType) -> Self {
self.columns.push(Column {
name: name.into(),
data_type,
nullable: true,
});
self
}
pub fn find_column(&self, name: &str) -> Option<&Column> {
self.columns.iter().find(|c| c.name == name)
}
pub fn column_index(&self, name: &str) -> Option<usize> {
self.columns.iter().position(|c| c.name == name)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Integer(i64),
String(String),
Boolean(bool),
Float(f64),
Bytes(Vec<u8>),
Null,
Timestamp(i64),
Decimal(String),
Json(String),
Array(Vec<Value>),
Vector(Vec<f32>),
}
impl Value {
pub fn type_of(&self) -> DataType {
match self {
Value::Integer(_) => DataType::Integer,
Value::String(_) => DataType::String,
Value::Boolean(_) => DataType::Boolean,
Value::Float(_) => DataType::Float,
Value::Bytes(_) => DataType::Bytes,
Value::Null => DataType::String, Value::Timestamp(_) => DataType::Timestamp,
Value::Decimal(_) => DataType::Decimal,
Value::Json(_) => DataType::Json,
Value::Array(arr) => {
if let Some(first) = arr.first() {
DataType::Array(Box::new(first.type_of()))
} else {
DataType::Array(Box::new(DataType::String))
}
}
Value::Vector(v) => DataType::Vector(v.len()),
}
}
}
pub type Row = Vec<Value>;
pub type ResultSet = Vec<Row>;
#[cfg(feature = "backend-sqlite")]
impl rusqlite::ToSql for Value {
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
use rusqlite::types::{ToSqlOutput, ValueRef};
match self {
Value::Integer(i) => Ok(ToSqlOutput::from(*i)),
Value::String(s) => Ok(ToSqlOutput::from(s.as_str())),
Value::Boolean(b) => Ok(ToSqlOutput::from(*b as i64)),
Value::Float(f) => Ok(ToSqlOutput::from(*f)),
Value::Bytes(b) => Ok(ToSqlOutput::from(b.as_slice())),
Value::Null => Ok(ToSqlOutput::Borrowed(ValueRef::Null)),
Value::Timestamp(ts) => Ok(ToSqlOutput::from(*ts)),
Value::Decimal(d) => Ok(ToSqlOutput::from(d.as_str())),
Value::Json(j) => Ok(ToSqlOutput::from(j.as_str())),
Value::Array(_) => Ok(ToSqlOutput::from(format!("{:?}", self))),
Value::Vector(v) => {
let json = format!("[{}]", v.iter().map(|f| f.to_string()).collect::<Vec<_>>().join(","));
Ok(ToSqlOutput::from(json))
}
}
}
}
#[cfg(feature = "backend-postgres")]
impl postgres::types::ToSql for Value {
fn to_sql(&self, _ty: &postgres::types::Type, out: &mut bytes::BytesMut) -> Result<postgres::types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
use postgres::types::{IsNull, ToSql};
match self {
Value::Integer(i) => i.to_sql(_ty, out),
Value::String(s) => s.to_sql(_ty, out),
Value::Boolean(b) => b.to_sql(_ty, out),
Value::Float(f) => f.to_sql(_ty, out),
Value::Bytes(b) => b.to_sql(_ty, out),
Value::Null => Ok(IsNull::Yes),
Value::Timestamp(ts) => ts.to_sql(_ty, out),
Value::Decimal(d) => d.to_sql(_ty, out),
Value::Json(j) => j.to_sql(_ty, out),
Value::Array(_) => {
format!("{:?}", self).to_sql(_ty, out)
}
Value::Vector(v) => {
let json = format!("[{}]", v.iter().map(|f| f.to_string()).collect::<Vec<_>>().join(","));
json.to_sql(_ty, out)
}
}
}
fn accepts(_ty: &postgres::types::Type) -> bool {
true
}
postgres::types::to_sql_checked!();
}