sqlany_extfnapi/value_cursor.rs
1//! `ExtFnValueCursor` is implemented to allow for zero allocation iteration of values from the
2//! SQL Anywhere external function API, while binding each iterated values to a sane lifetime.
3//!
4//! While these objects cannot be used with Rust `for` loops, `while let` loops offer a similar
5//! level of ergonomics:
6//!
7//! ```ignore
8//! while let Some(value) = cursor.next()? {
9//! // use value
10//! }
11//! ```
12
13use crate::{Error, SqlDataType, SqlDataTypeKind};
14use std::{cmp, iter::FromIterator, marker::PhantomData};
15
16/// Cursor of SQL anywhere values that are potentially multipart.
17pub trait ExtFnValueCursor {
18 /// The type presented by the cursor.
19 type Item: ?Sized;
20
21 /// Advances the cursor to the next value part if available.
22 ///
23 /// Cursors are initially unstarted, so this method should be called before `get`
24 /// when iterating.
25 ///
26 /// The behavior of calling this method after `get` has returned `None`, or after this method
27 /// has returned an error is unspecified.
28 fn advance(&mut self) -> Result<(), Error>;
29
30 /// Returns the current value part.
31 ///
32 /// The behavior of calling this method before any calls to `advance` is unspecified.
33 fn get(&self) -> Option<&Self::Item>;
34
35 /// Returns if the data value is `NULL`
36 fn is_null(&self) -> bool;
37
38 /// Returns the actual catalog type for the argument
39 fn kind(&self) -> SqlDataTypeKind;
40
41 /// Returns the data type for the argument
42 fn data_type(&self) -> SqlDataType;
43
44 /// Returns the total length for the argument
45 fn len(&self) -> usize;
46
47 /// Advances the cursor, returning the next value part if available.
48 fn next(&mut self) -> Result<Option<&Self::Item>, Error> {
49 self.advance()?;
50 Ok((*self).get())
51 }
52
53 /// Calls a closure on each element of an iterator.
54 fn for_each<F>(mut self, mut f: F) -> Result<(), Error>
55 where
56 Self: Sized,
57 F: FnMut(&Self::Item),
58 {
59 while let Some(value) = self.next()? {
60 f(value);
61 }
62 Ok(())
63 }
64
65 /// Consume the cursor, producing a single, final value.
66 fn fold<F, U>(mut self, init: U, f: F) -> Result<U, Error>
67 where
68 Self: Sized,
69 F: FnMut(U, &Self::Item) -> U,
70 {
71 let mut f = f;
72 let mut output = init;
73 while let Some(value) = self.next()? {
74 output = f(output, value);
75 }
76 Ok(output)
77 }
78
79 /// Consume the cursor folding into the given Extend type
80 fn collect<U>(mut self) -> Result<U, Error>
81 where
82 Self: Sized,
83 U: for<'s> Extend<&'s Self::Item> + Default,
84 {
85 self.fold(U::default(), |mut out, x| {
86 out.extend(std::iter::once(x));
87 out
88 })
89 }
90}
91
92impl<'a, I: ?Sized> ExtFnValueCursor for &'a mut I
93where
94 I: ExtFnValueCursor,
95{
96 type Item = I::Item;
97
98 fn advance(&mut self) -> Result<(), Error> {
99 (**self).advance()
100 }
101
102 fn get(&self) -> Option<&I::Item> {
103 (**self).get()
104 }
105
106 fn next(&mut self) -> Result<Option<&I::Item>, Error> {
107 (**self).next()
108 }
109
110 /// Returns if the data value is `NULL`
111 fn is_null(&self) -> bool {
112 (**self).is_null()
113 }
114
115 /// Returns the actual catalog type for the argument
116 fn kind(&self) -> SqlDataTypeKind {
117 (**self).kind()
118 }
119
120 /// Returns the data type for the argument
121 fn data_type(&self) -> SqlDataType {
122 (**self).data_type()
123 }
124
125 /// Returns the total length for the argument
126 fn len(&self) -> usize {
127 (**self).len()
128 }
129}
130
131#[cfg(test)]
132mod test {
133 use super::*;
134
135 fn _is_object_safe(_: &dyn ExtFnValueCursor<Item = ()>) {}
136}