sea_query/value/
array.rs

1use super::*;
2use crate::RcOrArc;
3#[cfg(feature = "with-json")]
4use crate::backend::ValueEncoder;
5use std::sync::Arc;
6
7type EnumArray = Box<(Arc<str>, Box<[Option<Arc<Enum>>]>)>;
8
9#[derive(Debug, Clone)]
10#[cfg_attr(not(feature = "hashable-value"), derive(PartialEq))]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub enum Array {
13    Bool(Box<[Option<bool>]>),
14    TinyInt(Box<[Option<i8>]>),
15    SmallInt(Box<[Option<i16>]>),
16    Int(Box<[Option<i32>]>),
17    BigInt(Box<[Option<i64>]>),
18    TinyUnsigned(Box<[Option<u8>]>),
19    SmallUnsigned(Box<[Option<u16>]>),
20    Unsigned(Box<[Option<u32>]>),
21    BigUnsigned(Box<[Option<u64>]>),
22    Float(Box<[Option<f32>]>),
23    Double(Box<[Option<f64>]>),
24    String(Box<[Option<String>]>),
25    Char(Box<[Option<char>]>),
26    Bytes(Box<[Option<Vec<u8>>]>),
27    Enum(EnumArray),
28    #[cfg(feature = "with-json")]
29    #[cfg_attr(docsrs, doc(cfg(feature = "with-json")))]
30    Json(Box<[Option<Json>]>),
31    #[cfg(feature = "with-chrono")]
32    #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
33    ChronoDate(Box<[Option<NaiveDate>]>),
34    #[cfg(feature = "with-chrono")]
35    #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
36    ChronoTime(Box<[Option<NaiveTime>]>),
37    #[cfg(feature = "with-chrono")]
38    #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
39    ChronoDateTime(Box<[Option<NaiveDateTime>]>),
40    #[cfg(feature = "with-chrono")]
41    #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
42    ChronoDateTimeUtc(Box<[Option<DateTime<Utc>>]>),
43    #[cfg(feature = "with-chrono")]
44    #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
45    ChronoDateTimeLocal(Box<[Option<DateTime<Local>>]>),
46    #[cfg(feature = "with-chrono")]
47    #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
48    ChronoDateTimeWithTimeZone(Box<[Option<DateTime<FixedOffset>>]>),
49    #[cfg(feature = "with-time")]
50    #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
51    TimeDate(Box<[Option<time::Date>]>),
52    #[cfg(feature = "with-time")]
53    #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
54    TimeTime(Box<[Option<time::Time>]>),
55    #[cfg(feature = "with-time")]
56    #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
57    TimeDateTime(Box<[Option<PrimitiveDateTime>]>),
58    #[cfg(feature = "with-time")]
59    #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
60    TimeDateTimeWithTimeZone(Box<[Option<OffsetDateTime>]>),
61    #[cfg(feature = "with-jiff")]
62    #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
63    JiffDate(Box<[Option<jiff::civil::Date>]>),
64    #[cfg(feature = "with-jiff")]
65    #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
66    JiffTime(Box<[Option<jiff::civil::Time>]>),
67    #[cfg(feature = "with-jiff")]
68    #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
69    JiffDateTime(Box<[Option<jiff::civil::DateTime>]>),
70    #[cfg(feature = "with-jiff")]
71    #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
72    JiffTimestamp(Box<[Option<Timestamp>]>),
73    #[cfg(feature = "with-jiff")]
74    #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
75    JiffZoned(Box<[Option<Zoned>]>),
76    #[cfg(feature = "with-uuid")]
77    #[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))]
78    Uuid(Box<[Option<Uuid>]>),
79    #[cfg(feature = "with-rust_decimal")]
80    #[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))]
81    Decimal(Box<[Option<Decimal>]>),
82    #[cfg(feature = "with-bigdecimal")]
83    #[cfg_attr(docsrs, doc(cfg(feature = "with-bigdecimal")))]
84    BigDecimal(Box<[Option<BigDecimal>]>),
85    #[cfg(feature = "with-ipnetwork")]
86    #[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))]
87    IpNetwork(Box<[Option<IpNetwork>]>),
88    #[cfg(feature = "with-mac_address")]
89    #[cfg_attr(docsrs, doc(cfg(feature = "with-mac_address")))]
90    MacAddress(Box<[Option<MacAddress>]>),
91    Null(ArrayType),
92}
93
94impl Array {
95    pub fn array_type(&self) -> ArrayType {
96        match self {
97            Array::Bool(_) => ArrayType::Bool,
98            Array::TinyInt(_) => ArrayType::TinyInt,
99            Array::SmallInt(_) => ArrayType::SmallInt,
100            Array::Int(_) => ArrayType::Int,
101            Array::BigInt(_) => ArrayType::BigInt,
102            Array::TinyUnsigned(_) => ArrayType::TinyUnsigned,
103            Array::SmallUnsigned(_) => ArrayType::SmallUnsigned,
104            Array::Unsigned(_) => ArrayType::Unsigned,
105            Array::BigUnsigned(_) => ArrayType::BigUnsigned,
106            Array::Float(_) => ArrayType::Float,
107            Array::Double(_) => ArrayType::Double,
108            Array::String(_) => ArrayType::String,
109            Array::Char(_) => ArrayType::Char,
110            Array::Bytes(_) => ArrayType::Bytes,
111            Array::Enum(boxed) => ArrayType::Enum(boxed.as_ref().0.clone()),
112            #[cfg(feature = "with-json")]
113            Array::Json(_) => ArrayType::Json,
114            #[cfg(feature = "with-chrono")]
115            Array::ChronoDate(_) => ArrayType::ChronoDate,
116            #[cfg(feature = "with-chrono")]
117            Array::ChronoTime(_) => ArrayType::ChronoTime,
118            #[cfg(feature = "with-chrono")]
119            Array::ChronoDateTime(_) => ArrayType::ChronoDateTime,
120            #[cfg(feature = "with-chrono")]
121            Array::ChronoDateTimeUtc(_) => ArrayType::ChronoDateTimeUtc,
122            #[cfg(feature = "with-chrono")]
123            Array::ChronoDateTimeLocal(_) => ArrayType::ChronoDateTimeLocal,
124            #[cfg(feature = "with-chrono")]
125            Array::ChronoDateTimeWithTimeZone(_) => ArrayType::ChronoDateTimeWithTimeZone,
126            #[cfg(feature = "with-time")]
127            Array::TimeDate(_) => ArrayType::TimeDate,
128            #[cfg(feature = "with-time")]
129            Array::TimeTime(_) => ArrayType::TimeTime,
130            #[cfg(feature = "with-time")]
131            Array::TimeDateTime(_) => ArrayType::TimeDateTime,
132            #[cfg(feature = "with-time")]
133            Array::TimeDateTimeWithTimeZone(_) => ArrayType::TimeDateTimeWithTimeZone,
134            #[cfg(feature = "with-jiff")]
135            Array::JiffDate(_) => ArrayType::JiffDate,
136            #[cfg(feature = "with-jiff")]
137            Array::JiffTime(_) => ArrayType::JiffTime,
138            #[cfg(feature = "with-jiff")]
139            Array::JiffDateTime(_) => ArrayType::JiffDateTime,
140            #[cfg(feature = "with-jiff")]
141            Array::JiffTimestamp(_) => ArrayType::JiffTimestamp,
142            #[cfg(feature = "with-jiff")]
143            Array::JiffZoned(_) => ArrayType::JiffZoned,
144            #[cfg(feature = "with-uuid")]
145            Array::Uuid(_) => ArrayType::Uuid,
146            #[cfg(feature = "with-rust_decimal")]
147            Array::Decimal(_) => ArrayType::Decimal,
148            #[cfg(feature = "with-bigdecimal")]
149            Array::BigDecimal(_) => ArrayType::BigDecimal,
150            #[cfg(feature = "with-ipnetwork")]
151            Array::IpNetwork(_) => ArrayType::IpNetwork,
152            #[cfg(feature = "with-mac_address")]
153            Array::MacAddress(_) => ArrayType::MacAddress,
154            Array::Null(ty) => ty.clone(),
155        }
156    }
157
158    pub fn is_null(&self) -> bool {
159        matches!(self, Array::Null(_))
160    }
161
162    pub fn is_empty(&self) -> bool {
163        match self {
164            Array::Bool(v) => v.is_empty(),
165            Array::TinyInt(v) => v.is_empty(),
166            Array::SmallInt(v) => v.is_empty(),
167            Array::Int(v) => v.is_empty(),
168            Array::BigInt(v) => v.is_empty(),
169            Array::TinyUnsigned(v) => v.is_empty(),
170            Array::SmallUnsigned(v) => v.is_empty(),
171            Array::Unsigned(v) => v.is_empty(),
172            Array::BigUnsigned(v) => v.is_empty(),
173            Array::Float(v) => v.is_empty(),
174            Array::Double(v) => v.is_empty(),
175            Array::String(v) => v.is_empty(),
176            Array::Char(v) => v.is_empty(),
177            Array::Bytes(v) => v.is_empty(),
178            Array::Enum(b) => b.as_ref().1.is_empty(),
179            #[cfg(feature = "with-json")]
180            Array::Json(v) => v.is_empty(),
181            #[cfg(feature = "with-chrono")]
182            Array::ChronoDate(v) => v.is_empty(),
183            #[cfg(feature = "with-chrono")]
184            Array::ChronoTime(v) => v.is_empty(),
185            #[cfg(feature = "with-chrono")]
186            Array::ChronoDateTime(v) => v.is_empty(),
187            #[cfg(feature = "with-chrono")]
188            Array::ChronoDateTimeUtc(v) => v.is_empty(),
189            #[cfg(feature = "with-chrono")]
190            Array::ChronoDateTimeLocal(v) => v.is_empty(),
191            #[cfg(feature = "with-chrono")]
192            Array::ChronoDateTimeWithTimeZone(v) => v.is_empty(),
193            #[cfg(feature = "with-time")]
194            Array::TimeDate(v) => v.is_empty(),
195            #[cfg(feature = "with-time")]
196            Array::TimeTime(v) => v.is_empty(),
197            #[cfg(feature = "with-time")]
198            Array::TimeDateTime(v) => v.is_empty(),
199            #[cfg(feature = "with-time")]
200            Array::TimeDateTimeWithTimeZone(v) => v.is_empty(),
201            #[cfg(feature = "with-jiff")]
202            Array::JiffDate(v) => v.is_empty(),
203            #[cfg(feature = "with-jiff")]
204            Array::JiffTime(v) => v.is_empty(),
205            #[cfg(feature = "with-jiff")]
206            Array::JiffDateTime(v) => v.is_empty(),
207            #[cfg(feature = "with-jiff")]
208            Array::JiffTimestamp(v) => v.is_empty(),
209            #[cfg(feature = "with-jiff")]
210            Array::JiffZoned(v) => v.is_empty(),
211            #[cfg(feature = "with-uuid")]
212            Array::Uuid(v) => v.is_empty(),
213            #[cfg(feature = "with-rust_decimal")]
214            Array::Decimal(v) => v.is_empty(),
215            #[cfg(feature = "with-bigdecimal")]
216            Array::BigDecimal(v) => v.is_empty(),
217            #[cfg(feature = "with-ipnetwork")]
218            Array::IpNetwork(v) => v.is_empty(),
219            #[cfg(feature = "with-mac_address")]
220            Array::MacAddress(v) => v.is_empty(),
221            Array::Null(_) => true,
222        }
223    }
224
225    #[cfg(feature = "with-json")]
226    pub(crate) fn to_json_value(&self) -> Json {
227        fn map_slice_of_opts<T, F>(slice: &[Option<T>], mut f: F) -> Json
228        where
229            F: FnMut(&T) -> Json,
230        {
231            slice
232                .iter()
233                .map(|o| match o.as_ref() {
234                    Some(v) => f(v),
235                    None => Json::Null,
236                })
237                .collect()
238        }
239
240        fn encode_to_string<F>(f: F) -> String
241        where
242            F: FnOnce(&CommonSqlQueryBuilder, &mut String),
243        {
244            let mut s = String::new();
245            let enc = CommonSqlQueryBuilder;
246            f(&enc, &mut s);
247            s
248        }
249
250        match self {
251            Array::Bool(v) => map_slice_of_opts(v, |&b| Json::Bool(b)),
252            Array::TinyInt(v) => map_slice_of_opts(v, |&x| x.into()),
253            Array::SmallInt(v) => map_slice_of_opts(v, |&x| x.into()),
254            Array::Int(v) => map_slice_of_opts(v, |&x| x.into()),
255            Array::BigInt(v) => map_slice_of_opts(v, |&x| x.into()),
256            Array::TinyUnsigned(v) => map_slice_of_opts(v, |&x| x.into()),
257            Array::SmallUnsigned(v) => map_slice_of_opts(v, |&x| x.into()),
258            Array::Unsigned(v) => map_slice_of_opts(v, |&x| x.into()),
259            Array::BigUnsigned(v) => map_slice_of_opts(v, |&x| x.into()),
260            Array::Float(v) => map_slice_of_opts(v, |&x| x.into()),
261            Array::Double(v) => map_slice_of_opts(v, |&x| x.into()),
262            Array::String(v) => map_slice_of_opts(v, |s| Json::String(s.clone())),
263            Array::Char(v) => map_slice_of_opts(v, |&c| Json::String(c.to_string())),
264            Array::Bytes(v) => map_slice_of_opts(v, |bytes| {
265                Json::String(std::str::from_utf8(bytes).unwrap().to_string())
266            }),
267            Array::Enum(v) => {
268                let (_, arr) = v.as_ref();
269                map_slice_of_opts(arr, |e| Json::String(e.value.to_string()))
270            }
271            #[cfg(feature = "with-json")]
272            Array::Json(v) => map_slice_of_opts(v, |j| j.clone()),
273            #[cfg(feature = "with-chrono")]
274            Array::ChronoDate(v) => map_slice_of_opts(v, |&d| {
275                Json::String(encode_to_string(|enc, buf| enc.write_naive_date_to(buf, d)))
276            }),
277            #[cfg(feature = "with-chrono")]
278            Array::ChronoTime(v) => map_slice_of_opts(v, |&t| {
279                Json::String(encode_to_string(|enc, buf| enc.write_naive_time_to(buf, t)))
280            }),
281            #[cfg(feature = "with-chrono")]
282            Array::ChronoDateTime(v) => map_slice_of_opts(v, |&dt| {
283                Json::String(encode_to_string(|enc, buf| {
284                    enc.write_naive_datetime_to(buf, dt)
285                }))
286            }),
287            #[cfg(feature = "with-chrono")]
288            Array::ChronoDateTimeUtc(v) => map_slice_of_opts(v, |dt| {
289                Json::String(encode_to_string(|enc, buf| {
290                    enc.write_datetime_utc_to(buf, dt)
291                }))
292            }),
293            #[cfg(feature = "with-chrono")]
294            Array::ChronoDateTimeLocal(v) => map_slice_of_opts(v, |dt| {
295                Json::String(encode_to_string(|enc, buf| {
296                    enc.write_datetime_local_to(buf, dt)
297                }))
298            }),
299            #[cfg(feature = "with-chrono")]
300            Array::ChronoDateTimeWithTimeZone(v) => map_slice_of_opts(v, |dt| {
301                Json::String(encode_to_string(|enc, buf| {
302                    enc.write_datetime_fixed_to(buf, dt)
303                }))
304            }),
305            #[cfg(feature = "with-time")]
306            Array::TimeDate(v) => map_slice_of_opts(v, |&d| {
307                Json::String(encode_to_string(|enc, buf| enc.write_time_date_to(buf, d)))
308            }),
309            #[cfg(feature = "with-time")]
310            Array::TimeTime(v) => map_slice_of_opts(v, |&t| {
311                Json::String(encode_to_string(|enc, buf| enc.write_time_time_to(buf, t)))
312            }),
313            #[cfg(feature = "with-time")]
314            Array::TimeDateTime(v) => map_slice_of_opts(v, |&dt| {
315                Json::String(encode_to_string(|enc, buf| {
316                    enc.write_time_datetime_to(buf, dt)
317                }))
318            }),
319            #[cfg(feature = "with-time")]
320            Array::TimeDateTimeWithTimeZone(v) => map_slice_of_opts(v, |&dt| {
321                Json::String(encode_to_string(|enc, buf| {
322                    enc.write_time_datetime_tz_to(buf, dt)
323                }))
324            }),
325            #[cfg(feature = "with-jiff")]
326            Array::JiffDate(v) => map_slice_of_opts(v, |&d| {
327                Json::String(encode_to_string(|enc, buf| enc.write_jiff_date_to(buf, d)))
328            }),
329            #[cfg(feature = "with-jiff")]
330            Array::JiffTime(v) => map_slice_of_opts(v, |&t| {
331                Json::String(encode_to_string(|enc, buf| enc.write_jiff_time_to(buf, t)))
332            }),
333            #[cfg(feature = "with-jiff")]
334            Array::JiffDateTime(v) => map_slice_of_opts(v, |&dt| {
335                Json::String(encode_to_string(|enc, buf| {
336                    enc.write_jiff_datetime_to(buf, dt)
337                }))
338            }),
339            #[cfg(feature = "with-jiff")]
340            Array::JiffTimestamp(v) => map_slice_of_opts(v, |&ts| {
341                Json::String(encode_to_string(|enc, buf| {
342                    enc.write_jiff_timestamp_to(buf, ts)
343                }))
344            }),
345            #[cfg(feature = "with-jiff")]
346            Array::JiffZoned(v) => map_slice_of_opts(v, |z| {
347                Json::String(encode_to_string(|enc, buf| enc.write_jiff_zoned_to(buf, z)))
348            }),
349            #[cfg(feature = "with-uuid")]
350            Array::Uuid(v) => map_slice_of_opts(v, |&u| Json::String(u.to_string())),
351            #[cfg(feature = "with-rust_decimal")]
352            Array::Decimal(v) => map_slice_of_opts(v, |&d| {
353                use rust_decimal::prelude::ToPrimitive;
354                Json::Number(serde_json::Number::from_f64(d.to_f64().unwrap()).unwrap())
355            }),
356            #[cfg(feature = "with-bigdecimal")]
357            Array::BigDecimal(v) => map_slice_of_opts(v, |bd| {
358                use bigdecimal::ToPrimitive;
359                Json::Number(serde_json::Number::from_f64(bd.to_f64().unwrap()).unwrap())
360            }),
361            #[cfg(feature = "with-ipnetwork")]
362            Array::IpNetwork(v) => map_slice_of_opts(v, |&ip| {
363                Json::String(encode_to_string(|enc, buf| enc.write_ipnetwork_to(buf, ip)))
364            }),
365            #[cfg(feature = "with-mac_address")]
366            Array::MacAddress(v) => map_slice_of_opts(v, |&mac| {
367                Json::String(encode_to_string(|enc, buf| {
368                    enc.write_mac_address_to(buf, mac)
369                }))
370            }),
371            Array::Null(_) => Json::Null,
372        }
373    }
374
375    pub fn dummy_value(&self) -> Self {
376        match self {
377            Array::Bool(_) => Array::Bool(Box::new([])),
378            Array::TinyInt(_) => Array::TinyInt(Box::new([])),
379            Array::SmallInt(_) => Array::SmallInt(Box::new([])),
380            Array::Int(_) => Array::Int(Box::new([])),
381            Array::BigInt(_) => Array::BigInt(Box::new([])),
382            Array::TinyUnsigned(_) => Array::TinyUnsigned(Box::new([])),
383            Array::SmallUnsigned(_) => Array::SmallUnsigned(Box::new([])),
384            Array::Unsigned(_) => Array::Unsigned(Box::new([])),
385            Array::BigUnsigned(_) => Array::BigUnsigned(Box::new([])),
386            Array::Float(_) => Array::Float(Box::new([])),
387            Array::Double(_) => Array::Double(Box::new([])),
388            Array::String(_) => Array::String(Box::new([])),
389            Array::Char(_) => Array::Char(Box::new([])),
390            Array::Bytes(_) => Array::Bytes(Box::new([])),
391            Array::Enum(val) => {
392                let val = val.as_ref();
393                Array::Enum(Box::new((val.0.clone(), Box::new([]))))
394            }
395            #[cfg(feature = "with-json")]
396            Array::Json(_) => Array::Json(Box::new([])),
397            #[cfg(feature = "with-chrono")]
398            Array::ChronoDate(_) => Array::ChronoDate(Box::new([])),
399            #[cfg(feature = "with-chrono")]
400            Array::ChronoTime(_) => Array::ChronoTime(Box::new([])),
401            #[cfg(feature = "with-chrono")]
402            Array::ChronoDateTime(_) => Array::ChronoDateTime(Box::new([])),
403            #[cfg(feature = "with-chrono")]
404            Array::ChronoDateTimeUtc(_) => Array::ChronoDateTimeUtc(Box::new([])),
405            #[cfg(feature = "with-chrono")]
406            Array::ChronoDateTimeLocal(_) => Array::ChronoDateTimeLocal(Box::new([])),
407            #[cfg(feature = "with-chrono")]
408            Array::ChronoDateTimeWithTimeZone(_) => Array::ChronoDateTimeWithTimeZone(Box::new([])),
409            #[cfg(feature = "with-time")]
410            Array::TimeDate(_) => Array::TimeDate(Box::new([])),
411            #[cfg(feature = "with-time")]
412            Array::TimeTime(_) => Array::TimeTime(Box::new([])),
413            #[cfg(feature = "with-time")]
414            Array::TimeDateTime(_) => Array::TimeDateTime(Box::new([])),
415            #[cfg(feature = "with-time")]
416            Array::TimeDateTimeWithTimeZone(_) => Array::TimeDateTimeWithTimeZone(Box::new([])),
417            #[cfg(feature = "with-jiff")]
418            Array::JiffDate(_) => Array::JiffDate(Box::new([])),
419            #[cfg(feature = "with-jiff")]
420            Array::JiffTime(_) => Array::JiffTime(Box::new([])),
421            #[cfg(feature = "with-jiff")]
422            Array::JiffDateTime(_) => Array::JiffDateTime(Box::new([])),
423            #[cfg(feature = "with-jiff")]
424            Array::JiffTimestamp(_) => Array::JiffTimestamp(Box::new([])),
425            #[cfg(feature = "with-jiff")]
426            Array::JiffZoned(_) => Array::JiffZoned(Box::new([])),
427            #[cfg(feature = "with-uuid")]
428            Array::Uuid(_) => Array::Uuid(Box::new([])),
429            #[cfg(feature = "with-rust_decimal")]
430            Array::Decimal(_) => Array::Decimal(Box::new([])),
431            #[cfg(feature = "with-bigdecimal")]
432            Array::BigDecimal(_) => Array::BigDecimal(Box::new([])),
433            #[cfg(feature = "with-ipnetwork")]
434            Array::IpNetwork(_) => Array::IpNetwork(Box::new([])),
435            #[cfg(feature = "with-mac_address")]
436            Array::MacAddress(_) => Array::MacAddress(Box::new([])),
437            Array::Null(ty) => Array::Null(ty.clone()),
438        }
439    }
440}
441
442impl From<Array> for Value {
443    fn from(value: Array) -> Self {
444        Value::Array(value)
445    }
446}
447
448/// Trait for custom types that can be used as PostgreSQL array elements.
449///
450/// When implemented, SeaQuery will provide:
451/// - `ValueType` for `Vec<T>` and `Vec<Option<T>>`
452/// - `From<Vec<T>> for Value` / `From<Vec<Option<T>>> for Value`
453/// - `From<Vec<T>> for Array` / `From<Vec<Option<T>>> for Array`
454pub trait ArrayElement: Sized {
455    /// The underlying element type stored in the array.
456    ///
457    /// Usually this is a built-in type like `String`, `i32`, `Uuid`, ...
458    type ArrayValueType: ArrayValue;
459
460    /// Convert self into the underlying array element type.
461    fn into_array_value(self) -> Self::ArrayValueType;
462
463    /// Convert from a Value to `Vec<Option<Self>>`
464    fn try_from_value(v: Value) -> Result<Vec<Option<Self>>, ValueTypeErr>;
465}
466
467/// Helper trait for types that can be stored inside a Postgres array [`Array`].
468///
469/// This is used by [`ArrayElement`] to build `Array` without causing deep trait resolution.
470pub trait ArrayValue: Sized {
471    #[doc(hidden)]
472    fn vec_opt_into_array(vec: Vec<Option<Self>>) -> Array;
473    #[doc(hidden)]
474    fn vec_into_array(vec: Vec<Self>) -> Array;
475}
476
477impl<T> ArrayValue for T
478where
479    Vec<Option<T>>: Into<Array>,
480{
481    fn vec_opt_into_array(vec: Vec<Option<Self>>) -> Array {
482        vec.into()
483    }
484
485    fn vec_into_array(vec: Vec<Self>) -> Array {
486        let vec: Vec<_> = vec.into_iter().map(Some).collect();
487        vec.into()
488    }
489}
490
491impl<T: ArrayElement + ValueType> ValueType for Vec<Option<T>> {
492    fn try_from(v: Value) -> Result<Self, ValueTypeErr> {
493        T::try_from_value(v)
494    }
495
496    fn type_name() -> String {
497        format!("Vec<Option<{}>>", T::type_name())
498    }
499
500    fn array_type() -> ArrayType {
501        unimplemented!()
502    }
503
504    fn column_type() -> ColumnType {
505        ColumnType::Array(RcOrArc::new(T::column_type()))
506    }
507}
508
509impl<T: ArrayElement + ValueType> ValueType for Vec<T> {
510    fn try_from(v: Value) -> Result<Self, ValueTypeErr> {
511        let vec_opt = T::try_from_value(v)?;
512        vec_opt
513            .into_iter()
514            .map(|opt| opt.ok_or(ValueTypeErr))
515            .collect()
516    }
517
518    fn type_name() -> String {
519        format!("Vec<{}>", T::type_name())
520    }
521
522    fn array_type() -> ArrayType {
523        unimplemented!()
524    }
525
526    fn column_type() -> ColumnType {
527        ColumnType::Array(RcOrArc::new(T::column_type()))
528    }
529}
530
531impl<T> From<Vec<T>> for Value
532where
533    T: ArrayElement,
534{
535    fn from(vec: Vec<T>) -> Value {
536        Array::from(vec).into()
537    }
538}
539
540impl<T> From<Vec<Option<T>>> for Value
541where
542    T: ArrayElement,
543{
544    fn from(vec: Vec<Option<T>>) -> Value {
545        Array::from(vec).into()
546    }
547}
548
549impl<T> From<Vec<T>> for Array
550where
551    T: ArrayElement,
552{
553    fn from(vec: Vec<T>) -> Array {
554        let converted: Vec<_> = vec.into_iter().map(|x| x.into_array_value()).collect();
555
556        ArrayValue::vec_into_array(converted)
557    }
558}
559
560impl<T> From<Vec<Option<T>>> for Array
561where
562    T: ArrayElement,
563{
564    fn from(vec: Vec<Option<T>>) -> Array {
565        let converted: Vec<Option<T::ArrayValueType>> = vec
566            .into_iter()
567            .map(|opt| opt.map(|e| e.into_array_value()))
568            .collect();
569        ArrayValue::vec_opt_into_array(converted)
570    }
571}
572
573#[cfg(feature = "hashable-value")]
574mod hash {
575    use ordered_float::{FloatCore, OrderedFloat};
576
577    use super::Array;
578
579    #[inline]
580    fn map_option_ordered_float_vec<T>(
581        vec: &[Option<T>],
582    ) -> impl Iterator<Item = Option<OrderedFloat<T>>> + '_
583    where
584        T: FloatCore,
585    {
586        vec.iter().copied().map(|x| x.map(OrderedFloat))
587    }
588
589    #[inline]
590    fn cmp_option_ordered_float_vec<T>(left: &[Option<T>], right: &[Option<T>]) -> bool
591    where
592        T: FloatCore,
593    {
594        map_option_ordered_float_vec(left).eq(map_option_ordered_float_vec(right))
595    }
596
597    impl PartialEq for Array {
598        fn eq(&self, other: &Self) -> bool {
599            match (self, other) {
600                (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
601                (Self::TinyInt(l0), Self::TinyInt(r0)) => l0 == r0,
602                (Self::SmallInt(l0), Self::SmallInt(r0)) => l0 == r0,
603                (Self::Int(l0), Self::Int(r0)) => l0 == r0,
604                (Self::BigInt(l0), Self::BigInt(r0)) => l0 == r0,
605                (Self::TinyUnsigned(l0), Self::TinyUnsigned(r0)) => l0 == r0,
606                (Self::SmallUnsigned(l0), Self::SmallUnsigned(r0)) => l0 == r0,
607                (Self::Unsigned(l0), Self::Unsigned(r0)) => l0 == r0,
608                (Self::BigUnsigned(l0), Self::BigUnsigned(r0)) => l0 == r0,
609                (Self::Float(l0), Self::Float(r0)) => cmp_option_ordered_float_vec(l0, r0),
610                (Self::Double(l0), Self::Double(r0)) => cmp_option_ordered_float_vec(l0, r0),
611                (Self::String(l0), Self::String(r0)) => l0 == r0,
612                (Self::Char(l0), Self::Char(r0)) => l0 == r0,
613                (Self::Bytes(l0), Self::Bytes(r0)) => l0 == r0,
614                (Self::Enum(l0), Self::Enum(r0)) => l0 == r0,
615                #[cfg(feature = "with-json")]
616                (Self::Json(l0), Self::Json(r0)) => l0 == r0,
617                #[cfg(feature = "with-chrono")]
618                (Self::ChronoDate(l0), Self::ChronoDate(r0)) => l0 == r0,
619                #[cfg(feature = "with-chrono")]
620                (Self::ChronoTime(l0), Self::ChronoTime(r0)) => l0 == r0,
621                #[cfg(feature = "with-chrono")]
622                (Self::ChronoDateTime(l0), Self::ChronoDateTime(r0)) => l0 == r0,
623                #[cfg(feature = "with-chrono")]
624                (Self::ChronoDateTimeUtc(l0), Self::ChronoDateTimeUtc(r0)) => l0 == r0,
625                #[cfg(feature = "with-chrono")]
626                (Self::ChronoDateTimeLocal(l0), Self::ChronoDateTimeLocal(r0)) => l0 == r0,
627                #[cfg(feature = "with-chrono")]
628                (Self::ChronoDateTimeWithTimeZone(l0), Self::ChronoDateTimeWithTimeZone(r0)) => {
629                    l0 == r0
630                }
631                #[cfg(feature = "with-time")]
632                (Self::TimeDate(l0), Self::TimeDate(r0)) => l0 == r0,
633                #[cfg(feature = "with-time")]
634                (Self::TimeTime(l0), Self::TimeTime(r0)) => l0 == r0,
635                #[cfg(feature = "with-time")]
636                (Self::TimeDateTime(l0), Self::TimeDateTime(r0)) => l0 == r0,
637                #[cfg(feature = "with-time")]
638                (Self::TimeDateTimeWithTimeZone(l0), Self::TimeDateTimeWithTimeZone(r0)) => {
639                    l0 == r0
640                }
641                #[cfg(feature = "with-jiff")]
642                (Self::JiffDate(l0), Self::JiffDate(r0)) => l0 == r0,
643                #[cfg(feature = "with-jiff")]
644                (Self::JiffTime(l0), Self::JiffTime(r0)) => l0 == r0,
645                #[cfg(feature = "with-jiff")]
646                (Self::JiffDateTime(l0), Self::JiffDateTime(r0)) => l0 == r0,
647                #[cfg(feature = "with-jiff")]
648                (Self::JiffTimestamp(l0), Self::JiffTimestamp(r0)) => l0 == r0,
649                #[cfg(feature = "with-jiff")]
650                (Self::JiffZoned(l0), Self::JiffZoned(r0)) => l0 == r0,
651                #[cfg(feature = "with-uuid")]
652                (Self::Uuid(l0), Self::Uuid(r0)) => l0 == r0,
653                #[cfg(feature = "with-rust_decimal")]
654                (Self::Decimal(l0), Self::Decimal(r0)) => l0 == r0,
655                #[cfg(feature = "with-bigdecimal")]
656                (Self::BigDecimal(l0), Self::BigDecimal(r0)) => l0 == r0,
657                #[cfg(feature = "with-ipnetwork")]
658                (Self::IpNetwork(l0), Self::IpNetwork(r0)) => l0 == r0,
659                #[cfg(feature = "with-mac_address")]
660                (Self::MacAddress(l0), Self::MacAddress(r0)) => l0 == r0,
661                (Self::Null(l0), Self::Null(r0)) => l0 == r0,
662                _ => false,
663            }
664        }
665    }
666
667    impl Eq for Array {}
668
669    impl std::hash::Hash for Array {
670        fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
671            use ordered_float::OrderedFloat;
672
673            std::mem::discriminant(self).hash(state);
674            match self {
675                Array::Bool(items) => items.hash(state),
676                Array::TinyInt(items) => items.hash(state),
677                Array::SmallInt(items) => items.hash(state),
678                Array::Int(items) => items.hash(state),
679                Array::BigInt(items) => items.hash(state),
680                Array::TinyUnsigned(items) => items.hash(state),
681                Array::SmallUnsigned(items) => items.hash(state),
682                Array::Unsigned(items) => items.hash(state),
683                Array::BigUnsigned(items) => items.hash(state),
684                Array::Float(items) => {
685                    for x in items.iter() {
686                        x.map(OrderedFloat).hash(state)
687                    }
688                }
689                Array::Double(items) => {
690                    for x in items.iter() {
691                        x.map(OrderedFloat).hash(state)
692                    }
693                }
694                Array::String(items) => items.hash(state),
695                Array::Char(items) => items.hash(state),
696                Array::Bytes(items) => items.hash(state),
697                Array::Enum(items) => items.hash(state),
698                #[cfg(feature = "with-json")]
699                Array::Json(items) => items.hash(state),
700                #[cfg(feature = "with-chrono")]
701                Array::ChronoDate(items) => items.hash(state),
702                #[cfg(feature = "with-chrono")]
703                Array::ChronoTime(items) => items.hash(state),
704                #[cfg(feature = "with-chrono")]
705                Array::ChronoDateTime(items) => items.hash(state),
706                #[cfg(feature = "with-chrono")]
707                Array::ChronoDateTimeUtc(items) => items.hash(state),
708                #[cfg(feature = "with-chrono")]
709                Array::ChronoDateTimeLocal(items) => items.hash(state),
710                #[cfg(feature = "with-chrono")]
711                Array::ChronoDateTimeWithTimeZone(items) => items.hash(state),
712                #[cfg(feature = "with-time")]
713                Array::TimeDate(items) => items.hash(state),
714                #[cfg(feature = "with-time")]
715                Array::TimeTime(items) => items.hash(state),
716                #[cfg(feature = "with-time")]
717                Array::TimeDateTime(items) => items.hash(state),
718                #[cfg(feature = "with-time")]
719                Array::TimeDateTimeWithTimeZone(items) => items.hash(state),
720                #[cfg(feature = "with-jiff")]
721                Array::JiffDate(items) => items.hash(state),
722                #[cfg(feature = "with-jiff")]
723                Array::JiffTime(items) => items.hash(state),
724                #[cfg(feature = "with-jiff")]
725                Array::JiffDateTime(items) => items.hash(state),
726                #[cfg(feature = "with-jiff")]
727                Array::JiffTimestamp(items) => items.hash(state),
728                #[cfg(feature = "with-jiff")]
729                Array::JiffZoned(items) => items.hash(state),
730                #[cfg(feature = "with-uuid")]
731                Array::Uuid(items) => items.hash(state),
732                #[cfg(feature = "with-rust_decimal")]
733                Array::Decimal(items) => items.hash(state),
734                #[cfg(feature = "with-bigdecimal")]
735                Array::BigDecimal(items) => items.hash(state),
736                #[cfg(feature = "with-ipnetwork")]
737                Array::IpNetwork(items) => items.hash(state),
738                #[cfg(feature = "with-mac_address")]
739                Array::MacAddress(items) => items.hash(state),
740                Array::Null(ty) => ty.hash(state),
741            }
742        }
743    }
744}