1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! `ExtFnValueCursor` is implemented to allow for zero allocation iteration of values from the
//! SQL Anywhere external function API, while binding each iterated values to a sane lifetime.
//!
//! While these objects cannot be used with Rust `for` loops, `while let` loops offer a similar
//! level of ergonomics:
//!
//! ```ignore
//! while let Some(value) = cursor.next()? {
//!     // use value
//! }
//! ```

use crate::{Error, SqlDataType, SqlDataTypeKind};
use std::{cmp, iter::FromIterator, marker::PhantomData};

/// Cursor of SQL anywhere values that are potentially multipart.
pub trait ExtFnValueCursor {
    /// The type presented by the cursor.
    type Item: ?Sized;

    /// Advances the cursor to the next value part if available.
    ///
    /// Cursors are initially unstarted, so this method should be called before `get`
    /// when iterating.
    ///
    /// The behavior of calling this method after `get` has returned `None`, or after this method
    /// has returned an error is unspecified.
    fn advance(&mut self) -> Result<(), Error>;

    /// Returns the current value part.
    ///
    /// The behavior of calling this method before any calls to `advance` is unspecified.
    fn get(&self) -> Option<&Self::Item>;

    /// Returns if the data value is `NULL`
    fn is_null(&self) -> bool;

    /// Returns the actual catalog type for the argument
    fn kind(&self) -> SqlDataTypeKind;

    /// Returns the data type for the argument
    fn data_type(&self) -> SqlDataType;

    /// Returns the total length for the argument
    fn len(&self) -> usize;

    /// Advances the cursor, returning the next value part if available.
    fn next(&mut self) -> Result<Option<&Self::Item>, Error> {
        self.advance()?;
        Ok((*self).get())
    }

    /// Calls a closure on each element of an iterator.
    fn for_each<F>(mut self, mut f: F) -> Result<(), Error>
    where
        Self: Sized,
        F: FnMut(&Self::Item),
    {
        while let Some(value) = self.next()? {
            f(value);
        }
        Ok(())
    }

    /// Consume the cursor, producing a single, final value.
    fn fold<F, U>(mut self, init: U, f: F) -> Result<U, Error>
    where
        Self: Sized,
        F: FnMut(U, &Self::Item) -> U,
    {
        let mut f = f;
        let mut output = init;
        while let Some(value) = self.next()? {
            output = f(output, value);
        }
        Ok(output)
    }

    /// Consume the cursor folding into the given Extend type
    fn collect<U>(mut self) -> Result<U, Error>
    where
        Self: Sized,
        U: for<'s> Extend<&'s Self::Item> + Default,
    {
        self.fold(U::default(), |mut out, x| {
            out.extend(std::iter::once(x));
            out
        })
    }
}

impl<'a, I: ?Sized> ExtFnValueCursor for &'a mut I
where
    I: ExtFnValueCursor,
{
    type Item = I::Item;

    fn advance(&mut self) -> Result<(), Error> {
        (**self).advance()
    }

    fn get(&self) -> Option<&I::Item> {
        (**self).get()
    }

    fn next(&mut self) -> Result<Option<&I::Item>, Error> {
        (**self).next()
    }

    /// Returns if the data value is `NULL`
    fn is_null(&self) -> bool {
        (**self).is_null()
    }

    /// Returns the actual catalog type for the argument
    fn kind(&self) -> SqlDataTypeKind {
        (**self).kind()
    }

    /// Returns the data type for the argument
    fn data_type(&self) -> SqlDataType {
        (**self).data_type()
    }

    /// Returns the total length for the argument
    fn len(&self) -> usize {
        (**self).len()
    }
}

#[cfg(test)]
mod test {
    use super::*;

    fn _is_object_safe(_: &dyn ExtFnValueCursor<Item = ()>) {}
}