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}