rangex/
inex.rs

1#[cfg(feature = "sqlx")]
2use std::ops::Bound;
3
4#[cfg(feature = "sqlx")]
5use sqlx::{
6    postgres::{types::PgRange, PgArgumentBuffer, PgTypeInfo, PgValueRef},
7    Decode, Encode, Postgres, Type,
8};
9
10#[derive(Debug, PartialEq, Eq, Clone)]
11#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
12pub struct InExRange<T> {
13    pub inclusive_start: T,
14    pub exclusive_end: T,
15}
16
17impl<T> From<InExRange<T>> for std::ops::Range<T> {
18    fn from(x: InExRange<T>) -> Self {
19        x.inclusive_start..x.exclusive_end
20    }
21}
22
23#[derive(Debug, Clone)]
24struct Error(String, String);
25
26impl std::fmt::Display for Error {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        f.write_fmt(format_args!("Invalid bounds: {}, {}", self.0, self.1))
29    }
30}
31
32impl std::error::Error for Error {}
33
34#[cfg(feature = "sqlx")]
35impl<'r, T> Decode<'r, Postgres> for InExRange<T>
36where
37    T: Type<Postgres> + for<'a> Decode<'a, Postgres> + std::fmt::Debug,
38{
39    #[inline(always)]
40    fn decode(value: PgValueRef) -> Result<Self, sqlx::error::BoxDynError> {
41        let pgrange = PgRange::decode(value)?;
42
43        let (inclusive_start, exclusive_end) = match (pgrange.start, pgrange.end) {
44            (Bound::Included(a), Bound::Excluded(b)) => (a, b),
45            (a, b) => return Err(Box::new(Error(format!("{:?}", a), format!("{:?}", b)))),
46        };
47
48        Ok(InExRange {
49            inclusive_start,
50            exclusive_end,
51        })
52    }
53}
54
55#[cfg(feature = "sqlx")]
56impl<'q, T> Encode<'q, Postgres> for InExRange<T>
57where
58    T: Encode<'q, Postgres> + Clone,
59{
60    #[inline(always)]
61    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> sqlx::encode::IsNull {
62        PgRange::encode_by_ref(
63            &PgRange {
64                start: Bound::Included(self.inclusive_start.clone()),
65                end: Bound::Excluded(self.exclusive_end.clone()),
66            },
67            buf,
68        )
69    }
70}
71
72#[cfg(feature = "sqlx")]
73impl<T> Type<Postgres> for InExRange<T>
74where
75    PgRange<T>: sqlx::Type<Postgres>,
76{
77    #[inline(always)]
78    fn type_info() -> PgTypeInfo {
79        PgRange::<T>::type_info()
80    }
81
82    #[inline(always)]
83    fn compatible(ty: &PgTypeInfo) -> bool {
84        PgRange::<T>::compatible(ty)
85    }
86}