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) -> impl Iterator<Item = usize> {
187 (0..self.number_of_primary_keys()).filter_map(move |position| {
188 (0..self.number_of_columns()).find(|&col| self.primary_key_index(col) == Some(position))
189 })
190 }
191}
192
193impl<T: SchemaWithPK> SchemaWithPK for &T {
194 fn number_of_primary_keys(&self) -> usize {
195 T::number_of_primary_keys(self)
196 }
197
198 fn primary_key_index(&self, col_idx: usize) -> Option<usize> {
199 T::primary_key_index(self, col_idx)
200 }
201
202 fn extract_pk<S: Clone, B: Clone>(
203 &self,
204 values: &impl IndexableValues<Text = S, Binary = B>,
205 ) -> alloc::vec::Vec<Value<S, B>> {
206 T::extract_pk(self, values)
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::{DynTable, IndexableValues, SchemaWithPK};
213 use crate::encoding::Value;
214 use crate::schema::SimpleTable;
215 use alloc::string::String;
216 use alloc::vec;
217 use alloc::vec::Vec;
218
219 fn users() -> SimpleTable {
220 SimpleTable::new("users", &["id", "name", "email"], &[0, 2])
221 }
222
223 #[test]
224 fn test_dyntable_ref_forwards() {
225 let t = users();
226 let r: &SimpleTable = &t;
227 assert_eq!(<&SimpleTable as DynTable>::name(&r), t.name());
228 assert_eq!(
229 <&SimpleTable as DynTable>::number_of_columns(&r),
230 t.number_of_columns()
231 );
232 let mut buf_ref = [0u8; 3];
233 let mut buf_direct = [0u8; 3];
234 <&SimpleTable as DynTable>::write_pk_flags(&r, &mut buf_ref);
235 t.write_pk_flags(&mut buf_direct);
236 assert_eq!(buf_ref, buf_direct);
237 }
238
239 #[test]
240 fn test_schema_with_pk_ref_forwards() {
241 let t = users();
242 let r: &SimpleTable = &t;
243 assert_eq!(
244 <&SimpleTable as SchemaWithPK>::number_of_primary_keys(&r),
245 t.number_of_primary_keys()
246 );
247 for idx in 0..t.number_of_columns() {
248 assert_eq!(
249 <&SimpleTable as SchemaWithPK>::primary_key_index(&r, idx),
250 t.primary_key_index(idx)
251 );
252 }
253 assert_eq!(
254 <&SimpleTable as SchemaWithPK>::primary_key_columns(&r).collect::<Vec<usize>>(),
255 t.primary_key_columns().collect::<Vec<usize>>()
256 );
257 let values: Vec<Value<String, Vec<u8>>> = vec![
258 Value::Integer(1),
259 Value::Text("alice".into()),
260 Value::Text("a@x".into()),
261 ];
262 let pk_ref = <&SimpleTable as SchemaWithPK>::extract_pk(&r, &values);
263 let pk_direct = t.extract_pk(&values);
264 assert_eq!(pk_ref, pk_direct);
265 }
266
267 #[test]
268 fn test_indexable_values_vec_option() {
269 let v: Vec<Option<Value<String, Vec<u8>>>> =
271 vec![Some(Value::Integer(7)), None, Some(Value::Text("x".into()))];
272 assert_eq!(
273 <Vec<Option<Value<String, Vec<u8>>>> as IndexableValues>::get(&v, 0),
274 Some(Value::Integer(7))
275 );
276 assert_eq!(
277 <Vec<Option<Value<String, Vec<u8>>>> as IndexableValues>::get(&v, 1),
278 Some(Value::Null)
279 );
280 assert_eq!(
281 <Vec<Option<Value<String, Vec<u8>>>> as IndexableValues>::get(&v, 2),
282 Some(Value::Text("x".into()))
283 );
284 assert_eq!(
285 <Vec<Option<Value<String, Vec<u8>>>> as IndexableValues>::get(&v, 99),
286 None
287 );
288 }
289
290 #[test]
291 fn test_indexable_values_slice_option() {
292 let owned: Vec<Option<Value<String, Vec<u8>>>> = vec![Some(Value::Integer(1)), None];
293 let slice: &[Option<Value<String, Vec<u8>>>] = &owned;
294 assert_eq!(
295 <&[Option<Value<String, Vec<u8>>>] as IndexableValues>::get(&slice, 0),
296 Some(Value::Integer(1))
297 );
298 assert_eq!(
299 <&[Option<Value<String, Vec<u8>>>] as IndexableValues>::get(&slice, 1),
300 Some(Value::Null)
301 );
302 assert_eq!(
303 <&[Option<Value<String, Vec<u8>>>] as IndexableValues>::get(&slice, 5),
304 None
305 );
306 }
307
308 type PairVec = Vec<(u8, Option<Value<String, Vec<u8>>>)>;
309
310 #[test]
311 fn test_indexable_values_vec_pair() {
312 let v: PairVec = vec![(0, Some(Value::Integer(2))), (1, None)];
314 assert_eq!(
315 <PairVec as IndexableValues>::get(&v, 0),
316 Some(Value::Integer(2))
317 );
318 assert_eq!(<PairVec as IndexableValues>::get(&v, 1), Some(Value::Null));
319 assert_eq!(<PairVec as IndexableValues>::get(&v, 2), None);
320 }
321
322 type PairSlice<'a> = &'a [(u8, Option<Value<String, Vec<u8>>>)];
323
324 #[test]
325 fn test_indexable_values_slice_pair() {
326 let owned: PairVec = vec![(0, Some(Value::Text("y".into()))), (1, None)];
327 let slice: PairSlice<'_> = &owned;
328 assert_eq!(
329 <PairSlice<'_> as IndexableValues>::get(&slice, 0),
330 Some(Value::Text("y".into()))
331 );
332 assert_eq!(
333 <PairSlice<'_> as IndexableValues>::get(&slice, 1),
334 Some(Value::Null)
335 );
336 assert_eq!(<PairSlice<'_> as IndexableValues>::get(&slice, 3), None);
337 }
338}