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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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 = "date")]
    crate::sql_test!(
        daterange,
        std::ops::RangeTo<chrono::NaiveDate>,
        [(
            "'[, 2010-01-01)'",
            ..chrono::NaiveDate::from_ymd_opt(2010, 1, 1).unwrap(),
        )]
    );

    #[cfg(feature = "date")]
    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(),
                )
        )]
    );
}