elephantry 5.1.1

Object model manager for PostgreSQL
Documentation
mod bounds;

use bounds::Bounds;
use std::convert::TryInto;

macro_rules! impl_range {
    ($ty:ty) => {
        impl<T: crate::ToSql> crate::ToSql for $ty {
            fn ty(&self) -> crate::pq::Type {
                Bounds::from(self).ty()
            }

            fn to_text(&self) -> crate::Result<Option<String>> {
                Bounds::from(self).to_text()
            }

            fn to_binary(&self) -> crate::Result<Option<Vec<u8>>> {
                Bounds::from(self).to_binary()
            }
        }

        impl<T: crate::FromSql> crate::FromSql for $ty {
            fn from_text(ty: &crate::pq::Type, raw: Option<&str>) -> crate::Result<Self> {
                Bounds::from_text(ty, raw)?
                    .try_into()
                    .map_err(|_| Self::error(ty, raw))
            }

            fn from_binary(ty: &crate::pq::Type, raw: Option<&[u8]>) -> crate::Result<Self> {
                Bounds::from_binary(ty, raw)?
                    .try_into()
                    .map_err(|_| Self::error(ty, raw))
            }
        }

        impl<T: crate::FromSql + crate::ToSql> crate::entity::Simple for $ty {}
    };
}

impl_range!(std::ops::Range<T>);
impl_range!(std::ops::RangeFrom<T>);
impl_range!(std::ops::RangeTo<T>);
impl_range!((std::ops::Bound<T>, std::ops::Bound<T>));

impl<T: crate::ToSql> crate::ToSql for std::ops::RangeToInclusive<T> {
    fn ty(&self) -> crate::pq::Type {
        Bounds::from(self).ty()
    }

    fn to_text(&self) -> crate::Result<Option<String>> {
        Bounds::from(self).to_text()
    }

    fn to_binary(&self) -> crate::Result<Option<Vec<u8>>> {
        Bounds::from(self).to_binary()
    }
}

impl crate::ToSql for std::ops::RangeFull {
    fn ty(&self) -> crate::pq::Type {
        crate::pq::types::UNKNOWN
    }

    fn to_text(&self) -> crate::Result<Option<String>> {
        "(,)".to_text()
    }

    fn to_binary(&self) -> crate::Result<Option<Vec<u8>>> {
        Ok(Some(vec![
            (bounds::Flags::LB_INF | bounds::Flags::UB_INF).bits(),
        ]))
    }
}

impl crate::FromSql for std::ops::RangeFull {
    fn from_text(_: &crate::pq::Type, _: Option<&str>) -> crate::Result<Self> {
        Ok(Self)
    }

    fn from_binary(_: &crate::pq::Type, _: Option<&[u8]>) -> crate::Result<Self> {
        Ok(Self)
    }
}

impl crate::entity::Simple for std::ops::RangeFull {}

impl<T: crate::ToSql> crate::ToSql for std::ops::RangeInclusive<T> {
    fn ty(&self) -> crate::pq::Type {
        Bounds::from(self).ty()
    }

    fn to_text(&self) -> crate::Result<Option<String>> {
        Bounds::from(self).to_text()
    }

    fn to_binary(&self) -> crate::Result<Option<Vec<u8>>> {
        Bounds::from(self).to_binary()
    }
}

#[cfg(test)]
mod test {
    mod full {
        crate::sql_test!(int4range, std::ops::RangeFull, [("'(,)'", ..)]);
    }

    crate::sql_test!(int4range, std::ops::Range<i32>, [("'[0, 10)'", 0_i32..10)]);

    crate::sql_test!(
        int8range,
        (std::ops::Bound<i64>, std::ops::Bound<i64>),
        [(
            "'[0, 10]'",
            (std::ops::Bound::Included(0), std::ops::Bound::Excluded(11))
        )]
    );

    #[cfg(feature = "numeric")]
    crate::sql_test!(
        numrange,
        std::ops::RangeFrom<bigdecimal::BigDecimal>,
        [("'[3900,)'", bigdecimal::BigDecimal::from(3_900)..)]
    );

    #[cfg(feature = "chrono")]
    crate::sql_test!(
        daterange,
        std::ops::RangeTo<chrono::NaiveDate>,
        [(
            "'[, 2010-01-01)'",
            ..chrono::NaiveDate::from_ymd_opt(2010, 1, 1).unwrap(),
        )]
    );

    #[cfg(feature = "chrono")]
    crate::sql_test!(
        tstzrange,
        std::ops::Range<chrono::DateTime<chrono::Utc>>,
        [(
            "'[1970-01-01 00:00:00+00, 2010-01-01 00:00:00+00)'",
            chrono::TimeZone::from_utc_datetime(
                &chrono::Utc,
                &chrono::NaiveDate::from_ymd_opt(1970, 1, 1)
                    .and_then(|x| x.and_hms_opt(0, 0, 0))
                    .unwrap()
            )
                ..chrono::TimeZone::from_utc_datetime(
                    &chrono::Utc,
                    &chrono::NaiveDate::from_ymd_opt(2010, 1, 1)
                        .and_then(|x| x.and_hms_opt(0, 0, 0))
                        .unwrap(),
                )
        )]
    );
}