1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
pub use libpq::types;

pub type Type = libpq::Type;

use std::collections::HashMap;

impl crate::FromSql for Type {
    fn from_binary(ty: &Type, raw: Option<&[u8]>) -> crate::Result<Self> {
        String::from_binary(ty, raw)?
            .parse()
            .map_err(|_| Self::error(ty, raw))
    }

    fn from_text(ty: &Type, raw: Option<&str>) -> crate::Result<Self> {
        crate::not_null(raw)?
            .parse()
            .map_err(|_| Self::error(ty, raw))
    }
}

impl crate::ToSql for Type {
    fn ty(&self) -> Type {
        types::TEXT
    }

    fn to_text(&self) -> crate::Result<Option<String>> {
        self.name.to_text()
    }

    fn to_binary(&self) -> crate::Result<Option<Vec<u8>>> {
        Ok(Some(self.oid.to_be_bytes().to_vec()))
    }
}

lazy_static::lazy_static! {
    static ref TYPES: HashMap<&'static str, &'static str> = {
        use std::any::type_name as t;

        let mut types = HashMap::new();
        types.insert(types::BIT.name, t::<u8>());
        types.insert(types::BOOL.name, t::<bool>());
        types.insert(types::CHAR.name, t::<char>());
        types.insert(types::FLOAT4.name, t::<f32>());
        types.insert(types::FLOAT8.name, t::<f64>());
        types.insert(types::INT2.name, t::<i16>());
        types.insert(types::INT4.name, t::<i32>());
        types.insert(types::INT8.name, t::<i64>());
        types.insert(types::TEXT.name, "String");
        types.insert(types::VARCHAR.name, "String");

        types.insert(
            types::BYTEA.name,
            #[cfg(feature = "bit")]
            t::<crate::Bytea>(),
            #[cfg(not(feature = "bit"))]
            "elephantry::Bytea",
        );
        types.insert(
            types::VARBIT.name,
            #[cfg(feature = "bit")]
            t::<bit_vec::BitVec>(),
            #[cfg(not(feature = "bit"))]
            "bit_vec::BitVec",
        );

        types.insert(
            types::DATE.name,
            "chrono::NaiveDate",
        );

        types.insert(
            types::BOX.name,
            #[cfg(feature = "geo")]
            t::<crate::Box>(),
            #[cfg(not(feature = "geo"))]
            "elephantry::Box",
        );
        types.insert(
            types::CIRCLE.name,
            #[cfg(feature = "geo")]
            t::<crate::Circle>(),
            #[cfg(not(feature = "geo"))]
            "elephantry::Circle",
        );
        types.insert(
            types::LINE.name,
            #[cfg(feature = "geo")]
            t::<crate::Line>(),
            #[cfg(not(feature = "geo"))]
            "elephantry::Line",
        );
        types.insert(
            types::LSEG.name,
            #[cfg(feature = "geo")]
            t::<crate::Segment>(),
            #[cfg(not(feature = "geo"))]
            "elephantry::Segment",
        );
        types.insert(
            types::PATH.name,
            #[cfg(feature = "geo")]
            t::<crate::Path>(),
            #[cfg(not(feature = "geo"))]
            "elephantry::Path",
        );
        types.insert(
            types::POINT.name,
            #[cfg(feature = "geo")]
            t::<crate::Point>(),
            #[cfg(not(feature = "geo"))]
            "elephantry::Point",
        );
        types.insert(
            types::POLYGON.name,
            #[cfg(feature = "geo")]
            t::<crate::Polygon>(),
            #[cfg(not(feature = "geo"))]
            "elephantry::Polygon",
        );

        types.insert(
            types::CIDR.name,
            #[cfg(feature = "net")]
            t::<ipnetwork::IpNetwork>(),
            #[cfg(not(feature = "net"))]
            "ipnetwork::IpNetwork",
        );
        types.insert(
            types::INET.name,
            "std::net::IpAddr",
        );
        types.insert(
            types::MACADDR.name,
            "macaddr::MacAddr6",
        );
        types.insert(
            types::MACADDR8.name,
            "macaddr::MacAddr8",
        );

        types.insert(
            types::JSON.name,
            #[cfg(feature = "json")]
            t::<serde_json::Value>(),
            #[cfg(not(feature = "json"))]
            "serde_json::Value",
        );
        types.insert(
            types::JSONB.name,
            "serde_json::Value",
        );

        types.insert(
            "lquery",
            #[cfg(feature = "ltree")]
            t::<crate::Lquery>(),
            #[cfg(not(feature = "ltree"))]
            "elephantry::Lquery",
        );
        types.insert(
            "ltree",
            #[cfg(feature = "ltree")]
            t::<crate::Ltree>(),
            #[cfg(not(feature = "ltree"))]
            "elephantry::Ltree",
        );
        types.insert(
            "ltxtquery",
            #[cfg(feature = "ltree")]
            t::<crate::Ltxtquery>(),
            #[cfg(not(feature = "ltree"))]
            "elephantry::Ltxtquery",
        );

        types.insert(
            types::MONEY.name,
            #[cfg(feature = "money")]
            t::<postgres_money::Money>(),
            #[cfg(not(feature = "money"))]
            "postgres_money::Money",
        );

        types.insert(
            types::NUMERIC.name,
            #[cfg(feature = "numeric")]
            t::<bigdecimal::BigDecimal>(),
            #[cfg(not(feature = "numeric"))]
            "bigdecimal::BigDecimal",
        );

        types.insert(
            types::TIME.name,
            "chrono::NaiveTime",
        );
        types.insert(
            types::TIMETZ.name,
            "chrono::TimeTz",
        );
        types.insert(
            types::TIMESTAMP.name,
            "chrono::NaiveDateTime",
        );
        types.insert(
            types::TIMESTAMPTZ.name,
            "chrono::DateTime<chrono::FixedOffset>",
        );

        types.insert(
            types::UUID.name,
            #[cfg(feature = "uuid")]
            t::<uuid::Uuid>(),
            #[cfg(not(feature = "uuid"))]
            "uuid::Uuid",
        );

        types.insert(
            types::XML.name,
            #[cfg(feature = "xml")]
            t::<xmltree::Element>(),
            #[cfg(not(feature = "xml"))]
            "xmltree::Element",
        );

        types
    };
}

#[doc(hidden)]
#[must_use]
pub fn sql_to_rust(ty: &crate::pq::Type) -> String {
    let rty = TYPES.get(ty.name).unwrap_or(&"String");

    if matches!(ty.kind, crate::pq::types::Kind::Array(_)) {
        format!("Vec<{rty}>")
    } else {
        (*rty).to_string()
    }
}

pub(crate) trait ToArray {
    fn to_array(&self) -> Self;
    fn to_range(&self) -> Self;
    fn to_multi_range(&self) -> Self;
    fn elementype(&self) -> Self;
    fn is_text(&self) -> bool;
}

impl ToArray for Type {
    fn to_range(&self) -> Self {
        match *self {
            types::ANY => types::ANY_RANGE,
            types::ANYCOMPATIBLE => types::ANYCOMPATIBLE_RANGE,
            types::INT4 => types::INT4_RANGE,
            types::INT8 => types::INT8_RANGE,
            types::NUMERIC => types::NUM_RANGE,
            types::TIMESTAMP => types::TS_RANGE,
            types::TIMESTAMPTZ => types::TSTZ_RANGE,
            types::DATE => types::DATE_RANGE,
            _ => self.clone(),
        }
    }

    fn to_multi_range(&self) -> Self {
        match *self {
            types::ANY_RANGE => types::ANYMULTI_RANGE,
            types::ANYCOMPATIBLE_RANGE => types::ANYCOMPATIBLEMULTI_RANGE,
            types::INT4_RANGE => types::INT4MULTI_RANGE,
            types::INT8_RANGE => types::INT8MULTI_RANGE,
            types::NUM_RANGE => types::NUMMULTI_RANGE,
            types::TS_RANGE => types::TSMULTI_RANGE,
            types::TSTZ_RANGE => types::TSTZMULTI_RANGE,
            types::DATE_RANGE => types::DATEMULTI_RANGE,
            _ => self.clone(),
        }
    }

    fn to_array(&self) -> Self {
        match *self {
            types::ACLITEM => types::ACLITEM_ARRAY,
            types::BIT => types::BIT_ARRAY,
            types::BOOL => types::BOOL_ARRAY,
            types::BOX => types::BOX_ARRAY,
            types::BPCHAR => types::BPCHAR_ARRAY,
            types::BYTEA => types::BYTEA_ARRAY,
            types::CHAR => types::CHAR_ARRAY,
            types::CID => types::CID_ARRAY,
            types::CIDR => types::CIDR_ARRAY,
            types::CIRCLE => types::CIRCLE_ARRAY,
            types::CSTRING => types::CSTRING_ARRAY,
            types::DATE => types::DATE_ARRAY,
            types::DATE_RANGE => types::DATE_RANGE_ARRAY,
            types::FLOAT4 => types::FLOAT4_ARRAY,
            types::FLOAT8 => types::FLOAT8_ARRAY,
            types::GTS_VECTOR => types::GTS_VECTOR_ARRAY,
            types::INET => types::INET_ARRAY,
            types::INT2 => types::INT2_ARRAY,
            types::INT2_VECTOR => types::INT2_VECTOR_ARRAY,
            types::INT4 => types::INT4_ARRAY,
            types::INT4_RANGE => types::INT4_RANGE_ARRAY,
            types::INT8 => types::INT8_ARRAY,
            types::INT8_RANGE => types::INT8_RANGE_ARRAY,
            types::INTERVAL => types::INTERVAL_ARRAY,
            types::JSON => types::JSON_ARRAY,
            types::JSONB => types::JSONB_ARRAY,
            types::JSONPATH => types::JSONPATH_ARRAY,
            types::LINE => types::LINE_ARRAY,
            types::LSEG => types::LSEG_ARRAY,
            types::MACADDR => types::MACADDR_ARRAY,
            types::MACADDR8 => types::MACADDR8_ARRAY,
            types::MONEY => types::MONEY_ARRAY,
            types::NAME => types::NAME_ARRAY,
            types::NUMERIC => types::NUMERIC_ARRAY,
            types::NUM_RANGE => types::NUM_RANGE_ARRAY,
            types::OID => types::OID_ARRAY,
            types::OID_VECTOR => types::OID_VECTOR_ARRAY,
            types::PATH => types::PATH_ARRAY,
            types::PG_LSN => types::PG_LSN_ARRAY,
            types::POINT => types::POINT_ARRAY,
            types::POLYGON => types::POLYGON_ARRAY,
            types::RECORD => types::RECORD_ARRAY,
            types::REFCURSOR => types::REFCURSOR_ARRAY,
            types::REGCLASS => types::REGCLASS_ARRAY,
            types::REGCONFIG => types::REGCONFIG_ARRAY,
            types::REGDICTIONARY => types::REGDICTIONARY_ARRAY,
            types::REGNAMESPACE => types::REGNAMESPACE_ARRAY,
            types::REGOPER => types::REGOPER_ARRAY,
            types::REGOPERATOR => types::REGOPERATOR_ARRAY,
            types::REGPROC => types::REGPROC_ARRAY,
            types::REGPROCEDURE => types::REGPROCEDURE_ARRAY,
            types::REGROLE => types::REGROLE_ARRAY,
            types::REGTYPE => types::REGTYPE_ARRAY,
            types::TEXT => types::TEXT_ARRAY,
            types::TID => types::TID_ARRAY,
            types::TIMESTAMP => types::TIMESTAMP_ARRAY,
            types::TIMESTAMPTZ => types::TIMESTAMPTZ_ARRAY,
            types::TIME => types::TIME_ARRAY,
            types::TIMETZ => types::TIMETZ_ARRAY,
            types::TSQUERY => types::TSQUERY_ARRAY,
            types::TSTZ_RANGE => types::TSTZ_RANGE_ARRAY,
            types::TS_RANGE => types::TS_RANGE_ARRAY,
            types::TS_VECTOR => types::TS_VECTOR_ARRAY,
            types::TXID_SNAPSHOT => types::TXID_SNAPSHOT_ARRAY,
            types::UUID => types::UUID_ARRAY,
            types::VARBIT => types::VARBIT_ARRAY,
            types::VARCHAR => types::VARCHAR_ARRAY,
            types::XID => types::XID_ARRAY,
            types::XML => types::XML_ARRAY,
            _ => self.clone(),
        }
    }

    fn elementype(&self) -> Self {
        match self.kind {
            crate::pq::types::Kind::Array(oid) => {
                crate::pq::Type::try_from(oid).unwrap_or(crate::pq::types::UNKNOWN)
            }
            _ => crate::pq::types::UNKNOWN,
        }
    }

    fn is_text(&self) -> bool {
        self == &types::TEXT || self == &types::VARCHAR
    }
}