sqlx_postgres_interval/
lib.rs1use std::mem;
2
3use serde::{Deserialize, Serialize};
4use sqlx::{
5 Decode, Encode, Postgres, Type,
6 encode::IsNull,
7 error::BoxDynError,
8 postgres::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueRef, types::PgInterval},
9};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct Interval {
34 pub months: i32,
35 pub days: i32,
36 pub microseconds: i64,
37}
38
39impl Serialize for Interval {
40 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
41 where
42 S: serde::Serializer,
43 {
44 let Self {
45 months,
46 days,
47 microseconds,
48 } = self.clone();
49 let pgi = pg_interval::Interval {
50 months,
51 days,
52 microseconds,
53 };
54 serializer.serialize_str(&pgi.to_iso_8601())
55 }
56}
57
58impl<'de> Deserialize<'de> for Interval {
59 fn deserialize<D>(deserializer: D) -> Result<Interval, D::Error>
60 where
61 D: serde::Deserializer<'de>,
62 {
63 let s = String::deserialize(deserializer)?;
64 let pgi = pg_interval::Interval::from_iso(&s).map_err(|error| {
65 serde::de::Error::custom(match error {
66 pg_interval::ParseError::ParseIntErr(parse_int_error) => {
67 parse_int_error.to_string()
68 }
69 pg_interval::ParseError::ParseFloatErr(parse_float_error) => {
70 parse_float_error.to_string()
71 }
72 pg_interval::ParseError::InvalidYearMonth(invalid_year_month) => invalid_year_month,
73 pg_interval::ParseError::InvalidTime(invalid_time) => invalid_time,
74 pg_interval::ParseError::InvalidInterval(invalid_interval) => invalid_interval,
75 })
76 })?;
77 Ok(Interval {
78 months: pgi.months,
79 days: pgi.days,
80 microseconds: pgi.microseconds,
81 })
82 }
83}
84
85impl Type<Postgres> for Interval {
86 fn type_info() -> PgTypeInfo {
87 PgInterval::type_info()
88 }
89}
90
91impl PgHasArrayType for Interval {
92 fn array_type_info() -> PgTypeInfo {
93 PgInterval::array_type_info()
94 }
95}
96
97impl<'de> Decode<'de, Postgres> for Interval {
98 fn decode(value: PgValueRef<'de>) -> Result<Self, BoxDynError> {
99 let PgInterval {
100 months,
101 days,
102 microseconds,
103 } = PgInterval::decode(value)?;
104 Ok(Interval {
105 months,
106 days,
107 microseconds,
108 })
109 }
110}
111
112impl Encode<'_, Postgres> for Interval {
113 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
114 let Self {
115 months,
116 days,
117 microseconds,
118 } = self.clone();
119 let pg_interval = PgInterval {
120 months,
121 days,
122 microseconds,
123 };
124 pg_interval.encode_by_ref(buf)
125 }
126
127 fn size_hint(&self) -> usize {
128 2 * mem::size_of::<i64>()
129 }
130}
131
132impl TryFrom<std::time::Duration> for Interval {
133 type Error = BoxDynError;
134
135 fn try_from(value: std::time::Duration) -> Result<Self, BoxDynError> {
140 if value.as_nanos() % 1000 != 0 {
141 return Err("PostgreSQL `INTERVAL` does not support nanoseconds precision".into());
142 }
143
144 Ok(Self {
145 months: 0,
146 days: 0,
147 microseconds: value.as_micros().try_into()?,
148 })
149 }
150}
151
152#[cfg(feature = "chrono")]
153impl TryFrom<chrono::Duration> for Interval {
154 type Error = BoxDynError;
155
156 fn try_from(value: chrono::Duration) -> Result<Self, BoxDynError> {
161 value
162 .num_nanoseconds()
163 .map_or::<Result<_, Self::Error>, _>(
164 Err("Overflow has occurred for PostgreSQL `INTERVAL`".into()),
165 |nanoseconds| {
166 if nanoseconds % 1000 != 0 {
167 return Err(
168 "PostgreSQL `INTERVAL` does not support nanoseconds precision".into(),
169 );
170 }
171 Ok(())
172 },
173 )?;
174
175 value.num_microseconds().map_or(
176 Err("Overflow has occurred for PostgreSQL `INTERVAL`".into()),
177 |microseconds| {
178 Ok(Self {
179 months: 0,
180 days: 0,
181 microseconds,
182 })
183 },
184 )
185 }
186}
187
188#[cfg(feature = "time")]
189impl TryFrom<time::Duration> for Interval {
190 type Error = BoxDynError;
191
192 fn try_from(value: time::Duration) -> Result<Self, BoxDynError> {
197 if value.whole_nanoseconds() % 1000 != 0 {
198 return Err("PostgreSQL `INTERVAL` does not support nanoseconds precision".into());
199 }
200
201 Ok(Self {
202 months: 0,
203 days: 0,
204 microseconds: value.whole_microseconds().try_into()?,
205 })
206 }
207}