quiver_types 0.1.0

Core types for the quiver crate
Documentation
//! Support for domain types: making `Column<MyType>` work.
//!
//! * For types you own: [`newtype_datatype!`](crate::newtype_datatype)
//!   (must be invoked in the crate declaring the type, per the orphan rule).
//! * For foreign types: the [`As`] adapter, e.g. `Column<As<Ipv4Addr, u32>>`.

/// Implements [`Datatype`] for a domain newtype,
/// making `Column<MyType>` work — including nesting (`List<MyType>`),
/// the convenience constructors, and the derive.
///
/// The newtype must convert to and from its representation:
/// `impl From<MyType> for Repr` and `impl From<Repr> for MyType`.
///
/// Reading stays zero-copy and yields the *representation's* borrowed value
/// (e.g. `&str` for a `String`-backed newtype);
/// owned values ([`Column::to_vec`](crate::Column::to_vec) etc.) are the newtype.
///
/// `column[index]` works too, borrowing through the representation.
/// That requires the representation to implement
/// [`RefDatatype`](crate::RefDatatype) — most do, but not e.g. `bool` or
/// `List<…>`; for those, add a trailing `noref` to skip the `Index` support.
///
/// ```
/// #[derive(Debug, PartialEq)]
/// struct SensorName(String);
///
/// impl From<String> for SensorName {
///     fn from(name: String) -> Self {
///         Self(name)
///     }
/// }
/// impl From<SensorName> for String {
///     fn from(name: SensorName) -> Self {
///         name.0
///     }
/// }
///
/// quiver::newtype_datatype!(SensorName, String);
///
/// let column = quiver::Column::<SensorName>::from_values([
///     SensorName("kitchen".to_owned()),
/// ]);
/// assert_eq!(column.value(0), "kitchen"); // borrowed: the repr's value
/// assert_eq!(&column[0], "kitchen"); // indexing, also borrowed
/// assert_eq!(column.to_vec(), [SensorName("kitchen".to_owned())]); // owned: the newtype
/// ```
#[macro_export]
macro_rules! newtype_datatype {
    ($newtype:ty, $repr:ty) => {
        $crate::newtype_datatype!($newtype, $repr, noref);

        impl $crate::RefDatatype for $newtype {
            type Ref = <$repr as $crate::RefDatatype>::Ref;

            fn value_ref(typed: &Self::Typed, index: usize) -> &Self::Ref {
                <$repr as $crate::RefDatatype>::value_ref(typed, index)
            }
        }
    };

    ($newtype:ty, $repr:ty, noref) => {
        impl $crate::Datatype for $newtype {
            const NULLABLE: bool = <$repr as $crate::Datatype>::NULLABLE;
            type Typed = <$repr as $crate::Datatype>::Typed;
            type Value<'a>
                = <$repr as $crate::Datatype>::Value<'a>
            where
                Self: 'a;
            type Owned = $newtype;

            fn datatype() -> $crate::arrow::datatypes::DataType {
                <$repr as $crate::Datatype>::datatype()
            }

            fn downcast(
                array: &dyn $crate::arrow::array::Array,
            ) -> ::core::result::Result<Self::Typed, $crate::ColumnError> {
                <$repr as $crate::Datatype>::downcast(array)
            }

            fn is_null(typed: &Self::Typed, index: usize) -> bool {
                <$repr as $crate::Datatype>::is_null(typed, index)
            }

            fn value(typed: &Self::Typed, index: usize) -> Self::Value<'_> {
                <$repr as $crate::Datatype>::value(typed, index)
            }

            fn build(
                values: impl ::core::iter::Iterator<Item = ::core::option::Option<Self::Owned>>,
            ) -> ::core::result::Result<$crate::arrow::array::ArrayRef, $crate::ColumnError> {
                <$repr as $crate::Datatype>::build(
                    values.map(|value| value.map(::core::convert::Into::into)),
                )
            }

            fn to_owned_value(value: Self::Value<'_>) -> Self::Owned {
                ::core::convert::From::from(<$repr as $crate::Datatype>::to_owned_value(value))
            }
        }

        impl $crate::InfallibleBuild for $newtype where $repr: $crate::InfallibleBuild {}
    };
}

use std::marker::PhantomData;

use crate::datatype::{ColumnError, Datatype, InfallibleBuild, PrimitiveDatatype, RefDatatype};

/// Adapter for using a *foreign* type (one you don't own, so
/// [`newtype_datatype!`](crate::newtype_datatype) is off-limits by the orphan rule)
/// as a logical column type, stored as `Repr`:
///
/// ```
/// use std::net::Ipv4Addr;
///
/// use quiver::{As, Column};
///
/// type IpColumn = Column<As<Ipv4Addr, u32>>; // u32: the arrow representation
///
/// let column = IpColumn::from_values([Ipv4Addr::LOCALHOST]);
/// assert_eq!(column.value(0), u32::from(Ipv4Addr::LOCALHOST)); // borrowed: the repr's value
/// assert_eq!(column.to_vec(), [Ipv4Addr::LOCALHOST]); // owned: the foreign type
/// ```
///
/// Requires `From` conversions between the foreign type and the representation's
/// owned value, in both directions.
/// Like [`newtype_datatype!`](crate::newtype_datatype), reading stays zero-copy and
/// yields the *representation's* borrowed value; owned values are the foreign type.
///
/// This type is never instantiated — it only appears as a type parameter.
pub struct As<T, Repr> {
    _marker: PhantomData<fn() -> (T, Repr)>,
}

impl<T, Repr> Datatype for As<T, Repr>
where
    T: 'static,
    Repr: Datatype + 'static,
    T: From<Repr::Owned>,
    Repr::Owned: From<T>,
{
    const NULLABLE: bool = Repr::NULLABLE;
    type Typed = Repr::Typed;
    type Value<'a>
        = Repr::Value<'a>
    where
        Self: 'a;
    type Owned = T;

    fn datatype() -> arrow::datatypes::DataType {
        Repr::datatype()
    }

    fn downcast(array: &dyn arrow::array::Array) -> Result<Self::Typed, ColumnError> {
        Repr::downcast(array)
    }

    fn is_null(typed: &Self::Typed, index: usize) -> bool {
        Repr::is_null(typed, index)
    }

    fn value(typed: &Self::Typed, index: usize) -> Self::Value<'_> {
        Repr::value(typed, index)
    }

    fn build(
        values: impl Iterator<Item = Option<Self::Owned>>,
    ) -> Result<arrow::array::ArrayRef, ColumnError> {
        Repr::build(values.map(|value| value.map(Repr::Owned::from)))
    }

    fn to_owned_value(value: Self::Value<'_>) -> Self::Owned {
        T::from(Repr::to_owned_value(value))
    }
}

impl<T, Repr> InfallibleBuild for As<T, Repr>
where
    T: 'static,
    Repr: Datatype + InfallibleBuild + 'static,
    T: From<Repr::Owned>,
    Repr::Owned: From<T>,
{
}

/// Like reading, `column[index]` yields the *representation's* reference.
impl<T, Repr> RefDatatype for As<T, Repr>
where
    T: 'static,
    Repr: RefDatatype + 'static,
    T: From<Repr::Owned>,
    Repr::Owned: From<T>,
{
    type Ref = Repr::Ref;

    fn value_ref(typed: &Self::Typed, index: usize) -> &Self::Ref {
        Repr::value_ref(typed, index)
    }
}

/// Like reading, [`Column::as_slice`](crate::Column::as_slice) yields
/// the *representation's* values.
impl<T, Repr> PrimitiveDatatype for As<T, Repr>
where
    T: 'static,
    Repr: PrimitiveDatatype + 'static,
    T: From<Repr::Owned>,
    Repr::Owned: From<T>,
{
    type Native = Repr::Native;

    fn values(typed: &Self::Typed) -> &[Self::Native] {
        Repr::values(typed)
    }
}