use std::ops::Deref;
use crate::engine::io::SelfEncodedSize;
use super::types::{BaseSqlTypes, SqlTypeDefinition};
use bytes::BufMut;
use thiserror::Error;
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct SqlTuple(pub Vec<Option<BaseSqlTypes>>);
impl SqlTuple {
pub fn filter_map(
self,
source: &SqlTypeDefinition,
target: &SqlTypeDefinition,
) -> Result<SqlTuple, SqlTupleError> {
if self.len() != source.len() {
return Err(SqlTupleError::SourceLenMismatch(self.len(), source.len()));
}
let mut output = Vec::with_capacity(target.len());
'outer: for (t_name, _) in target.iter() {
for s in 0..source.len() {
if *t_name == source[s].0 {
output.push(self.0[s].clone()); continue 'outer;
}
}
return Err(SqlTupleError::InvalidColumn(t_name.to_string()));
}
Ok(SqlTuple(output))
}
pub fn merge(left: &SqlTuple, right: &SqlTuple) -> SqlTuple {
SqlTuple(left.iter().cloned().chain(right.iter().cloned()).collect())
}
pub fn serialize(&self, buffer: &mut impl BufMut) {
for data in &self.0 {
match data {
Some(d) => d.serialize(buffer),
None => {}
}
}
}
}
impl Deref for SqlTuple {
type Target = Vec<Option<BaseSqlTypes>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl SelfEncodedSize for SqlTuple {
fn encoded_size(&self) -> usize {
self.iter().fold(0, |acc, col| match col {
Some(col_s) => acc + col_s.encoded_size(),
None => acc,
})
}
}
#[derive(Debug, Error)]
pub enum SqlTupleError {
#[error("Tuple length {0} does not match source length {1}")]
SourceLenMismatch(usize, usize),
#[error("Requested Column Name: {0} doesn't exist")]
InvalidColumn(String),
}
#[cfg(test)]
mod tests {
use bytes::BytesMut;
use super::*;
use crate::engine::objects::types::BaseSqlTypesMapper;
fn get_src_type() -> SqlTypeDefinition {
SqlTypeDefinition(vec![
("foo".to_string(), BaseSqlTypesMapper::Integer),
("bar".to_string(), BaseSqlTypesMapper::Text),
("baz".to_string(), BaseSqlTypesMapper::Text),
])
}
#[test]
fn sizes_match() -> Result<(), Box<dyn std::error::Error>> {
let test = SqlTuple(vec![
Some(BaseSqlTypes::Text("test".to_string())),
Some(BaseSqlTypes::Integer(0)),
]);
let calc_len = test.encoded_size();
let mut buffer = BytesMut::new();
test.serialize(&mut buffer);
assert_eq!(calc_len, buffer.freeze().len());
Ok(())
}
#[test]
fn test_sql_tuple_filter() -> Result<(), Box<dyn std::error::Error>> {
let source = get_src_type();
let target = SqlTypeDefinition(vec![source[2].clone(), source[1].clone()]);
let src_cols = SqlTuple(vec![
None,
Some(BaseSqlTypes::Text("Test".to_string())),
Some(BaseSqlTypes::Text("Test2".to_string())),
]);
let filtered = src_cols.filter_map(&source, &target)?;
let expected = SqlTuple(vec![
Some(BaseSqlTypes::Text("Test2".to_string())),
Some(BaseSqlTypes::Text("Test".to_string())),
]);
assert_eq!(filtered, expected);
Ok(())
}
#[test]
fn test_sql_tuple_merge() -> Result<(), Box<dyn std::error::Error>> {
let left = SqlTuple(vec![
None,
Some(BaseSqlTypes::Text("Test".to_string())),
Some(BaseSqlTypes::Text("Test2".to_string())),
]);
let right = SqlTuple(vec![
Some(BaseSqlTypes::Text("Test2".to_string())),
Some(BaseSqlTypes::Text("Test".to_string())),
]);
let expected = SqlTuple(vec![
None,
Some(BaseSqlTypes::Text("Test".to_string())),
Some(BaseSqlTypes::Text("Test2".to_string())),
Some(BaseSqlTypes::Text("Test2".to_string())),
Some(BaseSqlTypes::Text("Test".to_string())),
]);
let merged = SqlTuple::merge(&left, &right);
assert_eq!(merged, expected);
Ok(())
}
#[test]
fn test_encoded_size() {
let tuple = SqlTuple(vec![Some(BaseSqlTypes::Uuid(uuid::Uuid::new_v4())), None]);
let mut buffer = BytesMut::new();
tuple.serialize(&mut buffer);
let buffer = buffer.freeze();
assert_eq!(tuple.encoded_size(), buffer.len());
}
}