sqlany-extfnapi 0.2.1

SQL Anywhere External Function API
Documentation
//! `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 = ()>) {}
}