feophantlib/engine/objects/
sql_tuple.rs

1//!Wrapper type for a row in the database unattached to a table
2use std::ops::Deref;
3
4use crate::engine::io::SelfEncodedSize;
5
6use super::types::{BaseSqlTypes, SqlTypeDefinition};
7use bytes::BufMut;
8use thiserror::Error;
9
10#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
11pub struct SqlTuple(pub Vec<Option<BaseSqlTypes>>);
12
13impl SqlTuple {
14    //Rewrites the tuple to only provide columns requested in the order requested
15    pub fn filter_map(
16        self,
17        source: &SqlTypeDefinition,
18        target: &SqlTypeDefinition,
19    ) -> Result<SqlTuple, SqlTupleError> {
20        if self.len() != source.len() {
21            return Err(SqlTupleError::SourceLenMismatch(self.len(), source.len()));
22        }
23
24        let mut output = Vec::with_capacity(target.len());
25
26        //TODO handle possible type conversion OR figure out if it bombs
27        'outer: for (t_name, _) in target.iter() {
28            for s in 0..source.len() {
29                if *t_name == source[s].0 {
30                    output.push(self.0[s].clone()); //TODO remove the clone
31                    continue 'outer;
32                }
33            }
34            return Err(SqlTupleError::InvalidColumn(t_name.to_string()));
35        }
36
37        Ok(SqlTuple(output))
38    }
39
40    pub fn merge(left: &SqlTuple, right: &SqlTuple) -> SqlTuple {
41        //Code from here: https://stackoverflow.com/a/56490417
42        SqlTuple(left.iter().cloned().chain(right.iter().cloned()).collect())
43    }
44
45    pub fn serialize(&self, buffer: &mut impl BufMut) {
46        for data in &self.0 {
47            match data {
48                Some(d) => d.serialize(buffer),
49                None => {}
50            }
51        }
52    }
53}
54
55impl Deref for SqlTuple {
56    type Target = Vec<Option<BaseSqlTypes>>;
57
58    fn deref(&self) -> &Self::Target {
59        &self.0
60    }
61}
62
63impl SelfEncodedSize for SqlTuple {
64    fn encoded_size(&self) -> usize {
65        self.iter().fold(0, |acc, col| match col {
66            Some(col_s) => acc + col_s.encoded_size(),
67            None => acc,
68        })
69    }
70}
71
72#[derive(Debug, Error)]
73pub enum SqlTupleError {
74    #[error("Tuple length {0} does not match source length {1}")]
75    SourceLenMismatch(usize, usize),
76    #[error("Requested Column Name: {0} doesn't exist")]
77    InvalidColumn(String),
78}
79
80#[cfg(test)]
81mod tests {
82    use bytes::BytesMut;
83
84    use super::*;
85    use crate::engine::objects::types::BaseSqlTypesMapper;
86
87    fn get_src_type() -> SqlTypeDefinition {
88        SqlTypeDefinition(vec![
89            ("foo".to_string(), BaseSqlTypesMapper::Integer),
90            ("bar".to_string(), BaseSqlTypesMapper::Text),
91            ("baz".to_string(), BaseSqlTypesMapper::Text),
92        ])
93    }
94
95    #[test]
96    fn sizes_match() -> Result<(), Box<dyn std::error::Error>> {
97        let test = SqlTuple(vec![
98            Some(BaseSqlTypes::Text("test".to_string())),
99            Some(BaseSqlTypes::Integer(0)),
100        ]);
101        let calc_len = test.encoded_size();
102
103        let mut buffer = BytesMut::new();
104        test.serialize(&mut buffer);
105
106        assert_eq!(calc_len, buffer.freeze().len());
107        Ok(())
108    }
109
110    #[test]
111    fn test_sql_tuple_filter() -> Result<(), Box<dyn std::error::Error>> {
112        let source = get_src_type();
113
114        let target = SqlTypeDefinition(vec![source[2].clone(), source[1].clone()]);
115
116        let src_cols = SqlTuple(vec![
117            None,
118            Some(BaseSqlTypes::Text("Test".to_string())),
119            Some(BaseSqlTypes::Text("Test2".to_string())),
120        ]);
121
122        let filtered = src_cols.filter_map(&source, &target)?;
123
124        let expected = SqlTuple(vec![
125            Some(BaseSqlTypes::Text("Test2".to_string())),
126            Some(BaseSqlTypes::Text("Test".to_string())),
127        ]);
128
129        assert_eq!(filtered, expected);
130
131        Ok(())
132    }
133
134    #[test]
135    fn test_sql_tuple_merge() -> Result<(), Box<dyn std::error::Error>> {
136        let left = SqlTuple(vec![
137            None,
138            Some(BaseSqlTypes::Text("Test".to_string())),
139            Some(BaseSqlTypes::Text("Test2".to_string())),
140        ]);
141
142        let right = SqlTuple(vec![
143            Some(BaseSqlTypes::Text("Test2".to_string())),
144            Some(BaseSqlTypes::Text("Test".to_string())),
145        ]);
146
147        let expected = SqlTuple(vec![
148            None,
149            Some(BaseSqlTypes::Text("Test".to_string())),
150            Some(BaseSqlTypes::Text("Test2".to_string())),
151            Some(BaseSqlTypes::Text("Test2".to_string())),
152            Some(BaseSqlTypes::Text("Test".to_string())),
153        ]);
154
155        let merged = SqlTuple::merge(&left, &right);
156
157        assert_eq!(merged, expected);
158
159        Ok(())
160    }
161
162    #[test]
163    fn test_encoded_size() {
164        let tuple = SqlTuple(vec![Some(BaseSqlTypes::Uuid(uuid::Uuid::new_v4())), None]);
165
166        let mut buffer = BytesMut::new();
167        tuple.serialize(&mut buffer);
168        let buffer = buffer.freeze();
169
170        assert_eq!(tuple.encoded_size(), buffer.len());
171    }
172}