1#![warn(missing_docs)]
4
5use crate::{G, wkb::PostGisBinary};
9use jiff::{
10 Span, Zoned,
11 civil::{Date, DateTime},
12 fmt::temporal::DateTimeParser,
13 tz::TimeZone,
14};
15use sqlx::{
16 Decode, Postgres, Type,
17 error::BoxDynError,
18 postgres::{PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef},
19};
20
21static PARSER: DateTimeParser = DateTimeParser::new();
22
23impl Type<Postgres> for G {
26 fn type_info() -> PgTypeInfo {
27 PgTypeInfo::with_name("geometry")
28 }
29}
30
31impl PgHasArrayType for G {
32 fn array_type_info() -> PgTypeInfo {
33 PgTypeInfo::with_name("_geometry")
34 }
35}
36
37impl<'r> Decode<'r, Postgres> for G {
38 fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
39 match value.format() {
40 PgValueFormat::Binary => {
41 let bytes = value.as_bytes()?;
42 let ewkb = PostGisBinary::try_from(bytes)?;
43 Ok(ewkb.geom())
44 }
45 PgValueFormat::Text => Err("Failed decoding a PostGIS geometry column".into()),
46 }
47 }
48}
49
50#[derive(Debug)]
54pub struct PgDate(pub Date);
55
56impl Type<Postgres> for PgDate {
57 fn type_info() -> PgTypeInfo {
58 PgTypeInfo::with_name("date")
59 }
60}
61
62impl PgHasArrayType for PgDate {
63 fn array_type_info() -> PgTypeInfo {
64 PgTypeInfo::with_name("_date")
65 }
66}
67
68impl<'r> Decode<'r, Postgres> for PgDate {
69 fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
70 Ok(match value.format() {
71 PgValueFormat::Binary => {
72 let days: i32 = Decode::<Postgres>::decode(value)?;
74 let span = Span::new().try_days(days)?;
75 let pg_epoch = Date::new(2000, 1, 1)?;
76 PgDate(pg_epoch.checked_add(span)?)
77 }
78 PgValueFormat::Text => {
79 let it = PARSER.parse_date(value.as_str()?)?;
80 PgDate(it)
81 }
82 })
83 }
84}
85
86#[derive(Debug)]
90pub struct PgTimestamp(pub Zoned);
91
92impl Type<Postgres> for PgTimestamp {
93 fn type_info() -> PgTypeInfo {
94 PgTypeInfo::with_name("timestamp")
95 }
96}
97
98impl PgHasArrayType for PgTimestamp {
99 fn array_type_info() -> PgTypeInfo {
100 PgTypeInfo::with_name("_timestamp")
101 }
102}
103
104impl<'r> Decode<'r, Postgres> for PgTimestamp {
105 fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
106 Ok(match value.format() {
107 PgValueFormat::Binary => {
108 let micro_secs: i64 = Decode::<Postgres>::decode(value)?;
110 let span = Span::new().try_microseconds(micro_secs)?;
111 let pg_epoch = DateTime::new(2000, 1, 1, 0, 0, 0, 0)?;
112 let z = pg_epoch.checked_add(span)?.to_zoned(TimeZone::UTC)?;
113 PgTimestamp(z)
114 }
115 PgValueFormat::Text => {
116 let it = format!("{}Z", value.as_str()?).parse()?;
117 PgTimestamp(it)
118 }
119 })
120 }
121}