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}