Skip to content

Commit 97d4b3e

Browse files
authored
feat: implement To/FromDbValue for chrono::DateTime<Utc> (#474)
1 parent de98fe0 commit 97d4b3e

2 files changed

Lines changed: 202 additions & 46 deletions

File tree

cot/src/db/fields.rs

Lines changed: 36 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -11,102 +11,121 @@ use crate::db::{
1111
LimitedString, Model, PrimaryKey, Result, SqlxValueRef, ToDbFieldValue, ToDbValue,
1212
};
1313

14+
mod chrono_fields;
1415
mod chrono_wrapper;
1516

1617
macro_rules! impl_from_sqlite_default {
1718
() => {
1819
#[cfg(feature = "sqlite")]
19-
fn from_sqlite(value: SqliteValueRef<'_>) -> Result<Self> {
20+
fn from_sqlite(
21+
value: crate::db::impl_sqlite::SqliteValueRef<'_>,
22+
) -> crate::db::Result<Self> {
23+
use crate::db::SqlxValueRef;
2024
value.get::<Self>()
2125
}
2226
};
2327
($wrapper_ty:ty) => {
2428
#[cfg(feature = "sqlite")]
25-
fn from_sqlite(value: SqliteValueRef<'_>) -> Result<Self> {
29+
fn from_sqlite(
30+
value: crate::db::impl_sqlite::SqliteValueRef<'_>,
31+
) -> crate::db::Result<Self> {
2632
<$wrapper_ty as FromDbValue>::from_sqlite(value).map(|val| val.into())
2733
}
2834
};
2935
($wrapper_ty:ty, option) => {
3036
#[cfg(feature = "sqlite")]
31-
fn from_sqlite(value: SqliteValueRef<'_>) -> Result<Self> {
37+
fn from_sqlite(
38+
value: crate::db::impl_sqlite::SqliteValueRef<'_>,
39+
) -> crate::db::Result<Self> {
3240
<$wrapper_ty as FromDbValue>::from_sqlite(value).map(|val| val.map(|val| val.into()))
3341
}
3442
};
3543
}
44+
use impl_from_sqlite_default;
3645

3746
macro_rules! impl_from_postgres_default {
3847
() => {
3948
#[cfg(feature = "postgres")]
40-
fn from_postgres(value: PostgresValueRef<'_>) -> Result<Self> {
49+
fn from_postgres(
50+
value: crate::db::impl_postgres::PostgresValueRef<'_>,
51+
) -> crate::db::Result<Self> {
52+
use crate::db::SqlxValueRef;
4153
value.get::<Self>()
4254
}
4355
};
4456
($wrapper_ty:ty) => {
4557
#[cfg(feature = "postgres")]
46-
fn from_postgres(value: PostgresValueRef<'_>) -> Result<Self> {
58+
fn from_postgres(
59+
value: crate::db::impl_postgres::PostgresValueRef<'_>,
60+
) -> crate::db::Result<Self> {
4761
<$wrapper_ty as FromDbValue>::from_postgres(value).map(|val| val.into())
4862
}
4963
};
5064
($wrapper_ty:ty, option) => {
5165
#[cfg(feature = "postgres")]
52-
fn from_postgres(value: PostgresValueRef<'_>) -> Result<Self> {
66+
fn from_postgres(
67+
value: crate::db::impl_postgres::PostgresValueRef<'_>,
68+
) -> crate::db::Result<Self> {
5369
<$wrapper_ty as FromDbValue>::from_postgres(value).map(|val| val.map(|val| val.into()))
5470
}
5571
};
5672
}
73+
use impl_from_postgres_default;
5774

5875
macro_rules! impl_from_mysql_default {
5976
() => {
6077
#[cfg(feature = "mysql")]
61-
fn from_mysql(value: MySqlValueRef<'_>) -> Result<Self> {
78+
fn from_mysql(value: crate::db::impl_mysql::MySqlValueRef<'_>) -> Result<Self> {
79+
use crate::db::SqlxValueRef;
6280
value.get::<Self>()
6381
}
6482
};
6583
($wrapper_ty:ty) => {
6684
#[cfg(feature = "mysql")]
67-
fn from_mysql(value: MySqlValueRef<'_>) -> Result<Self> {
85+
fn from_mysql(value: crate::db::impl_mysql::MySqlValueRef<'_>) -> Result<Self> {
6886
<$wrapper_ty as FromDbValue>::from_mysql(value).map(|val| val.into())
6987
}
7088
};
7189
($wrapper_ty:ty, option) => {
7290
#[cfg(feature = "mysql")]
73-
fn from_mysql(value: MySqlValueRef<'_>) -> Result<Self> {
91+
fn from_mysql(value: crate::db::impl_mysql::MySqlValueRef<'_>) -> Result<Self> {
7492
<$wrapper_ty as FromDbValue>::from_mysql(value).map(|val| val.map(|val| val.into()))
7593
}
7694
};
7795
}
7896

7997
macro_rules! impl_to_db_value_default {
8098
($ty:ty) => {
81-
impl ToDbValue for $ty {
82-
fn to_db_value(&self) -> DbValue {
99+
impl crate::db::ToDbValue for $ty {
100+
fn to_db_value(&self) -> crate::db::DbValue {
83101
self.clone().into()
84102
}
85103
}
86104

87-
impl ToDbValue for Option<$ty> {
88-
fn to_db_value(&self) -> DbValue {
105+
impl crate::db::ToDbValue for Option<$ty> {
106+
fn to_db_value(&self) -> crate::db::DbValue {
89107
self.clone().into()
90108
}
91109
}
92110
};
93111

94112
($ty:ty, $wrapper_ty:ty) => {
95-
impl ToDbValue for $ty {
96-
fn to_db_value(&self) -> DbValue {
113+
impl crate::db::ToDbValue for $ty {
114+
fn to_db_value(&self) -> crate::db::DbValue {
97115
Into::<$wrapper_ty>::into(self.clone()).to_db_value()
98116
}
99117
}
100118

101-
impl ToDbValue for Option<$ty> {
102-
fn to_db_value(&self) -> DbValue {
119+
impl crate::db::ToDbValue for Option<$ty> {
120+
fn to_db_value(&self) -> crate::db::DbValue {
103121
self.clone()
104122
.map(|val| Into::<$wrapper_ty>::into(val))
105123
.to_db_value()
106124
}
107125
}
108126
};
109127
}
128+
use impl_to_db_value_default;
110129

111130
macro_rules! impl_db_field {
112131
($ty:ty, $column_type:ident) => {
@@ -231,35 +250,6 @@ impl ToDbValue for &str {
231250
}
232251
}
233252

234-
impl DatabaseField for chrono::DateTime<chrono::FixedOffset> {
235-
const TYPE: ColumnType = ColumnType::DateTimeWithTimeZone;
236-
}
237-
238-
impl FromDbValue for chrono::DateTime<chrono::FixedOffset> {
239-
impl_from_sqlite_default!();
240-
241-
impl_from_postgres_default!();
242-
243-
#[cfg(feature = "mysql")]
244-
fn from_mysql(value: MySqlValueRef<'_>) -> Result<Self> {
245-
Ok(value.get::<chrono::DateTime<chrono::Utc>>()?.fixed_offset())
246-
}
247-
}
248-
impl FromDbValue for Option<chrono::DateTime<chrono::FixedOffset>> {
249-
impl_from_sqlite_default!();
250-
251-
impl_from_postgres_default!();
252-
253-
#[cfg(feature = "mysql")]
254-
fn from_mysql(value: MySqlValueRef<'_>) -> Result<Self> {
255-
Ok(value
256-
.get::<Option<chrono::DateTime<chrono::Utc>>>()?
257-
.map(|dt| dt.fixed_offset()))
258-
}
259-
}
260-
261-
impl_to_db_value_default!(chrono::DateTime<chrono::FixedOffset>);
262-
263253
impl ToDbValue for Option<&str> {
264254
fn to_db_value(&self) -> DbValue {
265255
self.map(ToString::to_string).into()

cot/src/db/fields/chrono_fields.rs

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
use crate::db::fields::{
2+
impl_from_postgres_default, impl_from_sqlite_default, impl_to_db_value_default,
3+
};
4+
#[cfg(feature = "mysql")]
5+
use crate::db::impl_mysql::MySqlValueRef;
6+
use crate::db::{ColumnType, DatabaseField, FromDbValue, Result, SqlxValueRef};
7+
8+
impl DatabaseField for chrono::DateTime<chrono::FixedOffset> {
9+
const TYPE: ColumnType = ColumnType::DateTimeWithTimeZone;
10+
}
11+
12+
impl FromDbValue for chrono::DateTime<chrono::FixedOffset> {
13+
impl_from_sqlite_default!();
14+
15+
impl_from_postgres_default!();
16+
17+
#[cfg(feature = "mysql")]
18+
fn from_mysql(value: MySqlValueRef<'_>) -> Result<Self> {
19+
Ok(value.get::<chrono::DateTime<chrono::Utc>>()?.fixed_offset())
20+
}
21+
}
22+
impl FromDbValue for Option<chrono::DateTime<chrono::FixedOffset>> {
23+
impl_from_sqlite_default!();
24+
25+
impl_from_postgres_default!();
26+
27+
#[cfg(feature = "mysql")]
28+
fn from_mysql(value: MySqlValueRef<'_>) -> Result<Self> {
29+
Ok(value
30+
.get::<Option<chrono::DateTime<chrono::Utc>>>()?
31+
.map(|dt| dt.fixed_offset()))
32+
}
33+
}
34+
35+
impl_to_db_value_default!(chrono::DateTime<chrono::FixedOffset>);
36+
37+
impl DatabaseField for chrono::DateTime<chrono::Utc> {
38+
const TYPE: ColumnType = ColumnType::DateTimeWithTimeZone;
39+
}
40+
41+
impl FromDbValue for chrono::DateTime<chrono::Utc> {
42+
impl_from_sqlite_default!();
43+
44+
impl_from_postgres_default!();
45+
46+
#[cfg(feature = "mysql")]
47+
fn from_mysql(value: MySqlValueRef<'_>) -> Result<Self> {
48+
value.get::<Self>()
49+
}
50+
}
51+
impl FromDbValue for Option<chrono::DateTime<chrono::Utc>> {
52+
impl_from_sqlite_default!();
53+
54+
impl_from_postgres_default!();
55+
56+
#[cfg(feature = "mysql")]
57+
fn from_mysql(value: MySqlValueRef<'_>) -> Result<Self> {
58+
value.get::<Option<chrono::DateTime<chrono::Utc>>>()
59+
}
60+
}
61+
62+
impl_to_db_value_default!(chrono::DateTime<chrono::Utc>);
63+
64+
#[cfg(test)]
65+
mod tests {
66+
use chrono::{DateTime, FixedOffset, Utc};
67+
68+
use crate::db::{ColumnType, DatabaseField, DbValue, ToDbValue};
69+
70+
#[test]
71+
fn test_datetime_fixed_offset_column_type() {
72+
assert_eq!(
73+
<DateTime<FixedOffset> as DatabaseField>::TYPE,
74+
ColumnType::DateTimeWithTimeZone
75+
);
76+
const {
77+
assert!(!<DateTime<FixedOffset> as DatabaseField>::NULLABLE);
78+
}
79+
}
80+
81+
#[test]
82+
fn test_datetime_utc_column_type() {
83+
assert_eq!(
84+
<DateTime<Utc> as DatabaseField>::TYPE,
85+
ColumnType::DateTimeWithTimeZone
86+
);
87+
const {
88+
assert!(!<DateTime<Utc> as DatabaseField>::NULLABLE);
89+
}
90+
}
91+
92+
#[test]
93+
fn test_option_datetime_column_type() {
94+
assert_eq!(
95+
<Option<DateTime<FixedOffset>> as DatabaseField>::TYPE,
96+
ColumnType::DateTimeWithTimeZone
97+
);
98+
const {
99+
assert!(<Option<DateTime<FixedOffset>> as DatabaseField>::NULLABLE);
100+
}
101+
102+
assert_eq!(
103+
<Option<DateTime<Utc>> as DatabaseField>::TYPE,
104+
ColumnType::DateTimeWithTimeZone
105+
);
106+
const {
107+
assert!(<Option<DateTime<Utc>> as DatabaseField>::NULLABLE);
108+
}
109+
}
110+
111+
#[test]
112+
fn test_datetime_fixed_offset_to_db_value() {
113+
let dt = DateTime::parse_from_rfc3339("2023-01-01T12:00:00+01:00").unwrap();
114+
let db_value = dt.to_db_value();
115+
116+
match db_value {
117+
DbValue::ChronoDateTimeWithTimeZone(Some(v)) => assert_eq!(*v, dt),
118+
_ => panic!("Expected DbValue::ChronoDateTimeWithTimeZone, got {db_value:?}"),
119+
}
120+
}
121+
122+
#[test]
123+
fn test_datetime_utc_to_db_value() {
124+
let dt = Utc::now();
125+
let db_value = dt.to_db_value();
126+
127+
match db_value {
128+
DbValue::ChronoDateTimeUtc(Some(v)) => assert_eq!(*v, dt),
129+
_ => panic!("Expected DbValue::ChronoDateTimeUtc, got {db_value:?}"),
130+
}
131+
}
132+
133+
#[test]
134+
fn test_option_datetime_to_db_value() {
135+
let dt = DateTime::parse_from_rfc3339("2023-01-01T12:00:00+01:00").unwrap();
136+
let some_dt = Some(dt);
137+
let none_dt: Option<DateTime<FixedOffset>> = None;
138+
139+
match some_dt.to_db_value() {
140+
DbValue::ChronoDateTimeWithTimeZone(Some(v)) => assert_eq!(*v, dt),
141+
_ => panic!(
142+
"Expected DbValue::ChronoDateTimeWithTimeZone(Some), got {:?}",
143+
some_dt.to_db_value()
144+
),
145+
}
146+
147+
assert_eq!(
148+
none_dt.to_db_value(),
149+
DbValue::ChronoDateTimeWithTimeZone(None)
150+
);
151+
152+
let dt_utc = Utc::now();
153+
let some_dt_utc = Some(dt_utc);
154+
let none_dt_utc: Option<DateTime<Utc>> = None;
155+
156+
match some_dt_utc.to_db_value() {
157+
DbValue::ChronoDateTimeUtc(Some(v)) => assert_eq!(*v, dt_utc),
158+
_ => panic!(
159+
"Expected DbValue::ChronoDateTimeUtc(Some), got {:?}",
160+
some_dt_utc.to_db_value()
161+
),
162+
}
163+
164+
assert_eq!(none_dt_utc.to_db_value(), DbValue::ChronoDateTimeUtc(None));
165+
}
166+
}

0 commit comments

Comments
 (0)