1use core::{fmt::Debug, hash::Hash};
3
4use alloc::vec::Vec;
5
6use crate::encoding::Value;
7
8pub trait DynTable: Debug + Eq + Clone + PartialEq {
12 fn name(&self) -> &str;
14
15 fn number_of_columns(&self) -> usize;
17
18 fn write_pk_flags(&self, buf: &mut [u8]);
33}
34
35impl<T: DynTable> DynTable for &T {
36 #[inline]
37 fn name(&self) -> &str {
38 T::name(self)
39 }
40
41 #[inline]
42 fn number_of_columns(&self) -> usize {
43 T::number_of_columns(self)
44 }
45
46 #[inline]
47 fn write_pk_flags(&self, buf: &mut [u8]) {
48 T::write_pk_flags(self, buf);
49 }
50}
51
52pub trait IndexableValues {
54 type Text: Clone;
56 type Binary: Clone;
58
59 fn get(&self, col_idx: usize) -> Option<Value<Self::Text, Self::Binary>>;
69}
70
71impl<S: Clone, B: Clone> IndexableValues for Vec<Value<S, B>> {
72 type Text = S;
73 type Binary = B;
74
75 #[inline]
76 fn get(&self, col_idx: usize) -> Option<Value<Self::Text, Self::Binary>> {
77 <&[Value<S, B>]>::get(&self.as_slice(), col_idx)
78 }
79}
80
81impl<S: Clone, B: Clone> IndexableValues for &[Value<S, B>] {
82 type Text = S;
83 type Binary = B;
84
85 #[inline]
86 fn get(&self, col_idx: usize) -> Option<Value<Self::Text, Self::Binary>> {
87 <[Value<S, B>]>::get(self, col_idx).cloned()
88 }
89}
90
91impl<S: Clone, B: Clone> IndexableValues for Vec<Option<Value<S, B>>> {
92 type Text = S;
93 type Binary = B;
94
95 #[inline]
96 fn get(&self, col_idx: usize) -> Option<Value<Self::Text, Self::Binary>> {
97 <&[Option<Value<S, B>>]>::get(&self.as_slice(), col_idx)
98 }
99}
100
101impl<S: Clone, B: Clone> IndexableValues for &[Option<Value<S, B>>] {
102 type Text = S;
103 type Binary = B;
104
105 #[inline]
106 fn get(&self, col_idx: usize) -> Option<Value<Self::Text, Self::Binary>> {
107 <[Option<Value<S, B>>]>::get(self, col_idx).map(|v| {
108 if let Some(value) = v {
109 value.clone()
110 } else {
111 Value::Null
112 }
113 })
114 }
115}
116
117impl<O, S: Clone, B: Clone> IndexableValues for Vec<(O, Option<Value<S, B>>)> {
118 type Text = S;
119 type Binary = B;
120
121 #[inline]
122 fn get(&self, col_idx: usize) -> Option<Value<Self::Text, Self::Binary>> {
123 <&[(O, Option<Value<S, B>>)]>::get(&self.as_slice(), col_idx)
124 }
125}
126
127impl<O, S: Clone, B: Clone> IndexableValues for &[(O, Option<Value<S, B>>)] {
128 type Text = S;
129 type Binary = B;
130
131 #[inline]
132 fn get(&self, col_idx: usize) -> Option<Value<Self::Text, Self::Binary>> {
133 <[(O, Option<Value<S, B>>)]>::get(self, col_idx).map(|(_old, new)| {
134 if let Some(value) = new {
135 value.clone()
136 } else {
137 Value::Null
138 }
139 })
140 }
141}
142
143pub trait SchemaWithPK: DynTable + Clone + Hash {
155 fn number_of_primary_keys(&self) -> usize;
157
158 fn primary_key_index(&self, col_idx: usize) -> Option<usize>;
160
161 fn extract_pk<S: Clone, B: Clone>(
170 &self,
171 values: &impl IndexableValues<Text = S, Binary = B>,
172 ) -> alloc::vec::Vec<Value<S, B>>;
173
174 fn primary_key_columns(&self) -> Vec<usize> {
183 let mut pairs: Vec<(usize, usize)> = (0..self.number_of_columns())
184 .filter_map(|col| self.primary_key_index(col).map(|pos| (pos, col)))
185 .collect();
186 pairs.sort_by_key(|&(pos, _)| pos);
187 pairs.into_iter().map(|(_, col)| col).collect()
188 }
189}
190
191impl<T: SchemaWithPK> SchemaWithPK for &T {
192 fn number_of_primary_keys(&self) -> usize {
193 T::number_of_primary_keys(self)
194 }
195
196 fn primary_key_index(&self, col_idx: usize) -> Option<usize> {
197 T::primary_key_index(self, col_idx)
198 }
199
200 fn extract_pk<S: Clone, B: Clone>(
201 &self,
202 values: &impl IndexableValues<Text = S, Binary = B>,
203 ) -> alloc::vec::Vec<Value<S, B>> {
204 T::extract_pk(self, values)
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::{DynTable, IndexableValues, SchemaWithPK};
211 use crate::encoding::Value;
212 use crate::schema::SimpleTable;
213 use alloc::string::String;
214 use alloc::vec;
215 use alloc::vec::Vec;
216
217 fn users() -> SimpleTable {
218 SimpleTable::new("users", &["id", "name", "email"], &[0, 2])
219 }
220
221 #[test]
222 fn test_dyntable_ref_forwards() {
223 let t = users();
224 let r: &SimpleTable = &t;
225 assert_eq!(<&SimpleTable as DynTable>::name(&r), t.name());
226 assert_eq!(
227 <&SimpleTable as DynTable>::number_of_columns(&r),
228 t.number_of_columns()
229 );
230 let mut buf_ref = [0u8; 3];
231 let mut buf_direct = [0u8; 3];
232 <&SimpleTable as DynTable>::write_pk_flags(&r, &mut buf_ref);
233 t.write_pk_flags(&mut buf_direct);
234 assert_eq!(buf_ref, buf_direct);
235 }
236
237 #[test]
238 fn test_schema_with_pk_ref_forwards() {
239 let t = users();
240 let r: &SimpleTable = &t;
241 assert_eq!(
242 <&SimpleTable as SchemaWithPK>::number_of_primary_keys(&r),
243 t.number_of_primary_keys()
244 );
245 for idx in 0..t.number_of_columns() {
246 assert_eq!(
247 <&SimpleTable as SchemaWithPK>::primary_key_index(&r, idx),
248 t.primary_key_index(idx)
249 );
250 }
251 assert_eq!(
252 <&SimpleTable as SchemaWithPK>::primary_key_columns(&r),
253 t.primary_key_columns()
254 );
255 let values: Vec<Value<String, Vec<u8>>> = vec![
256 Value::Integer(1),
257 Value::Text("alice".into()),
258 Value::Text("a@x".into()),
259 ];
260 let pk_ref = <&SimpleTable as SchemaWithPK>::extract_pk(&r, &values);
261 let pk_direct = t.extract_pk(&values);
262 assert_eq!(pk_ref, pk_direct);
263 }
264
265 #[test]
266 fn test_indexable_values_vec_option() {
267 let v: Vec<Option<Value<String, Vec<u8>>>> =
269 vec![Some(Value::Integer(7)), None, Some(Value::Text("x".into()))];
270 assert_eq!(
271 <Vec<Option<Value<String, Vec<u8>>>> as IndexableValues>::get(&v, 0),
272 Some(Value::Integer(7))
273 );
274 assert_eq!(
275 <Vec<Option<Value<String, Vec<u8>>>> as IndexableValues>::get(&v, 1),
276 Some(Value::Null)
277 );
278 assert_eq!(
279 <Vec<Option<Value<String, Vec<u8>>>> as IndexableValues>::get(&v, 2),
280 Some(Value::Text("x".into()))
281 );
282 assert_eq!(
283 <Vec<Option<Value<String, Vec<u8>>>> as IndexableValues>::get(&v, 99),
284 None
285 );
286 }
287
288 #[test]
289 fn test_indexable_values_slice_option() {
290 let owned: Vec<Option<Value<String, Vec<u8>>>> = vec![Some(Value::Integer(1)), None];
291 let slice: &[Option<Value<String, Vec<u8>>>] = &owned;
292 assert_eq!(
293 <&[Option<Value<String, Vec<u8>>>] as IndexableValues>::get(&slice, 0),
294 Some(Value::Integer(1))
295 );
296 assert_eq!(
297 <&[Option<Value<String, Vec<u8>>>] as IndexableValues>::get(&slice, 1),
298 Some(Value::Null)
299 );
300 assert_eq!(
301 <&[Option<Value<String, Vec<u8>>>] as IndexableValues>::get(&slice, 5),
302 None
303 );
304 }
305
306 type PairVec = Vec<(u8, Option<Value<String, Vec<u8>>>)>;
307
308 #[test]
309 fn test_indexable_values_vec_pair() {
310 let v: PairVec = vec![(0, Some(Value::Integer(2))), (1, None)];
312 assert_eq!(
313 <PairVec as IndexableValues>::get(&v, 0),
314 Some(Value::Integer(2))
315 );
316 assert_eq!(<PairVec as IndexableValues>::get(&v, 1), Some(Value::Null));
317 assert_eq!(<PairVec as IndexableValues>::get(&v, 2), None);
318 }
319
320 type PairSlice<'a> = &'a [(u8, Option<Value<String, Vec<u8>>>)];
321
322 #[test]
323 fn test_indexable_values_slice_pair() {
324 let owned: PairVec = vec![(0, Some(Value::Text("y".into()))), (1, None)];
325 let slice: PairSlice<'_> = &owned;
326 assert_eq!(
327 <PairSlice<'_> as IndexableValues>::get(&slice, 0),
328 Some(Value::Text("y".into()))
329 );
330 assert_eq!(
331 <PairSlice<'_> as IndexableValues>::get(&slice, 1),
332 Some(Value::Null)
333 );
334 assert_eq!(<PairSlice<'_> as IndexableValues>::get(&slice, 3), None);
335 }
336}