scylla_cql/
value.rs

1use std::net::IpAddr;
2use std::result::Result as StdResult;
3
4use thiserror::Error;
5use uuid::Uuid;
6
7use crate::deserialize::value::DeserializeValue;
8use crate::deserialize::value::{
9    mk_deser_err, BuiltinDeserializationErrorKind, MapIterator, UdtIterator,
10};
11use crate::deserialize::DeserializationError;
12use crate::deserialize::FrameSlice;
13use crate::frame::response::result::{CollectionType, ColumnType};
14use crate::frame::types;
15
16#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
17#[error("Value is too large to fit in the CQL type")]
18pub struct ValueOverflow;
19
20/// Represents an unset value
21#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
22pub struct Unset;
23
24/// Represents an counter value
25#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
26pub struct Counter(pub i64);
27
28/// Enum providing a way to represent a value that might be unset
29#[derive(Debug, Clone, Copy, Default)]
30pub enum MaybeUnset<V> {
31    #[default]
32    Unset,
33    Set(V),
34}
35
36/// Represents timeuuid (uuid V1) value
37///
38/// This type has custom comparison logic which follows Scylla/Cassandra semantics.
39/// For details, see [`Ord` implementation](#impl-Ord-for-CqlTimeuuid).
40#[derive(Debug, Clone, Copy, Eq)]
41pub struct CqlTimeuuid(Uuid);
42
43/// [`Uuid`] delegate methods
44impl CqlTimeuuid {
45    pub fn as_bytes(&self) -> &[u8; 16] {
46        self.0.as_bytes()
47    }
48
49    pub fn as_u128(&self) -> u128 {
50        self.0.as_u128()
51    }
52
53    pub fn as_fields(&self) -> (u32, u16, u16, &[u8; 8]) {
54        self.0.as_fields()
55    }
56
57    pub fn as_u64_pair(&self) -> (u64, u64) {
58        self.0.as_u64_pair()
59    }
60
61    pub fn from_slice(b: &[u8]) -> Result<Self, uuid::Error> {
62        Ok(Self(Uuid::from_slice(b)?))
63    }
64
65    pub fn from_slice_le(b: &[u8]) -> Result<Self, uuid::Error> {
66        Ok(Self(Uuid::from_slice_le(b)?))
67    }
68
69    pub fn from_bytes(bytes: [u8; 16]) -> Self {
70        Self(Uuid::from_bytes(bytes))
71    }
72
73    pub fn from_bytes_le(bytes: [u8; 16]) -> Self {
74        Self(Uuid::from_bytes_le(bytes))
75    }
76
77    pub fn from_fields(d1: u32, d2: u16, d3: u16, d4: &[u8; 8]) -> Self {
78        Self(Uuid::from_fields(d1, d2, d3, d4))
79    }
80
81    pub fn from_fields_le(d1: u32, d2: u16, d3: u16, d4: &[u8; 8]) -> Self {
82        Self(Uuid::from_fields_le(d1, d2, d3, d4))
83    }
84
85    pub fn from_u128(v: u128) -> Self {
86        Self(Uuid::from_u128(v))
87    }
88
89    pub fn from_u128_le(v: u128) -> Self {
90        Self(Uuid::from_u128_le(v))
91    }
92
93    pub fn from_u64_pair(high_bits: u64, low_bits: u64) -> Self {
94        Self(Uuid::from_u64_pair(high_bits, low_bits))
95    }
96}
97
98impl CqlTimeuuid {
99    /// Read 8 most significant bytes of timeuuid from serialized bytes
100    fn msb(&self) -> u64 {
101        // Scylla and Cassandra use a standard UUID memory layout for MSB:
102        // 4 bytes    2 bytes    2 bytes
103        // time_low - time_mid - time_hi_and_version
104        let bytes = self.0.as_bytes();
105        u64::from_be_bytes([
106            bytes[6] & 0x0f,
107            bytes[7],
108            bytes[4],
109            bytes[5],
110            bytes[0],
111            bytes[1],
112            bytes[2],
113            bytes[3],
114        ])
115    }
116
117    fn lsb(&self) -> u64 {
118        let bytes = self.0.as_bytes();
119        u64::from_be_bytes([
120            bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15],
121        ])
122    }
123
124    fn lsb_signed(&self) -> u64 {
125        self.lsb() ^ 0x8080808080808080
126    }
127}
128
129impl std::str::FromStr for CqlTimeuuid {
130    type Err = uuid::Error;
131
132    fn from_str(s: &str) -> Result<Self, Self::Err> {
133        Ok(Self(Uuid::from_str(s)?))
134    }
135}
136
137impl std::fmt::Display for CqlTimeuuid {
138    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139        write!(f, "{}", self.0)
140    }
141}
142
143impl AsRef<Uuid> for CqlTimeuuid {
144    fn as_ref(&self) -> &Uuid {
145        &self.0
146    }
147}
148
149impl From<CqlTimeuuid> for Uuid {
150    fn from(value: CqlTimeuuid) -> Self {
151        value.0
152    }
153}
154
155impl From<Uuid> for CqlTimeuuid {
156    fn from(value: Uuid) -> Self {
157        Self(value)
158    }
159}
160
161/// Compare two values of timeuuid type.
162///
163/// Cassandra legacy requires:
164/// - converting 8 most significant bytes to date, which is then compared.
165/// - masking off UUID version from the 8 ms-bytes during compare, to
166///   treat possible non-version-1 UUID the same way as UUID.
167/// - using signed compare for least significant bits.
168impl Ord for CqlTimeuuid {
169    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
170        let mut res = self.msb().cmp(&other.msb());
171        if let std::cmp::Ordering::Equal = res {
172            res = self.lsb_signed().cmp(&other.lsb_signed());
173        }
174        res
175    }
176}
177
178impl PartialOrd for CqlTimeuuid {
179    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
180        Some(self.cmp(other))
181    }
182}
183
184impl PartialEq for CqlTimeuuid {
185    fn eq(&self, other: &Self) -> bool {
186        self.cmp(other) == std::cmp::Ordering::Equal
187    }
188}
189
190impl std::hash::Hash for CqlTimeuuid {
191    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
192        self.lsb_signed().hash(state);
193        self.msb().hash(state);
194    }
195}
196
197/// Native CQL `varint` representation.
198///
199/// Represented as two's-complement binary in big-endian order.
200///
201/// This type is a raw representation in bytes. It's the default
202/// implementation of `varint` type - independent of any
203/// external crates and crate features.
204///
205/// The type is not very useful in most use cases.
206/// However, users can make use of more complex types
207/// such as `num_bigint::BigInt` (v0.3/v0.4).
208/// The library support (e.g. conversion from [`CqlValue`]) for these types is
209/// enabled via `num-bigint-03` and `num-bigint-04` crate features.
210///
211/// This struct holds owned bytes. If you wish to borrow the bytes instead,
212/// see [`CqlVarintBorrowed`] documentation.
213///
214/// # DB data format
215/// Notice that [constructors](CqlVarint#impl-CqlVarint)
216/// don't perform any normalization on the provided data.
217/// This means that underlying bytes may contain leading zeros.
218///
219/// Currently, Scylla and Cassandra support non-normalized `varint` values.
220/// Bytes provided by the user via constructor are passed to DB as is.
221///
222/// The implementation of [`PartialEq`], however, normalizes the underlying bytes
223/// before comparison. For details, check [examples](#impl-PartialEq-for-CqlVarint).
224#[derive(Clone, Eq, Debug)]
225pub struct CqlVarint(Vec<u8>);
226
227/// A borrowed version of native CQL `varint` representation.
228///
229/// Refer to the documentation of [`CqlVarint`].
230/// Especially, see the disclaimer about [non-normalized values](CqlVarint#db-data-format).
231#[derive(Clone, Eq, Debug)]
232pub struct CqlVarintBorrowed<'b>(&'b [u8]);
233
234/// Constructors from bytes
235impl CqlVarint {
236    /// Creates a [`CqlVarint`] from an array of bytes in
237    /// two's complement big-endian binary representation.
238    ///
239    /// See: disclaimer about [non-normalized values](CqlVarint#db-data-format).
240    pub fn from_signed_bytes_be(digits: Vec<u8>) -> Self {
241        Self(digits)
242    }
243
244    /// Creates a [`CqlVarint`] from a slice of bytes in
245    /// two's complement binary big-endian representation.
246    ///
247    /// See: disclaimer about [non-normalized values](CqlVarint#db-data-format).
248    pub fn from_signed_bytes_be_slice(digits: &[u8]) -> Self {
249        Self::from_signed_bytes_be(digits.to_vec())
250    }
251}
252
253/// Constructors from bytes
254impl<'b> CqlVarintBorrowed<'b> {
255    /// Creates a [`CqlVarintBorrowed`] from a slice of bytes in
256    /// two's complement binary big-endian representation.
257    ///
258    /// See: disclaimer about [non-normalized values](CqlVarint#db-data-format).
259    pub fn from_signed_bytes_be_slice(digits: &'b [u8]) -> Self {
260        Self(digits)
261    }
262}
263
264/// Conversion to bytes
265impl CqlVarint {
266    /// Converts [`CqlVarint`] to an array of bytes in two's
267    /// complement binary big-endian representation.
268    pub fn into_signed_bytes_be(self) -> Vec<u8> {
269        self.0
270    }
271
272    /// Returns a slice of bytes in two's complement
273    /// binary big-endian representation.
274    pub fn as_signed_bytes_be_slice(&self) -> &[u8] {
275        &self.0
276    }
277}
278
279/// Conversion to bytes
280impl CqlVarintBorrowed<'_> {
281    /// Returns a slice of bytes in two's complement
282    /// binary big-endian representation.
283    pub fn as_signed_bytes_be_slice(&self) -> &[u8] {
284        self.0
285    }
286}
287
288/// An internal utility trait used to implement [`AsNormalizedVarintSlice`]
289/// for both [`CqlVarint`] and [`CqlVarintBorrowed`].
290trait AsVarintSlice {
291    fn as_slice(&self) -> &[u8];
292}
293impl AsVarintSlice for CqlVarint {
294    fn as_slice(&self) -> &[u8] {
295        self.as_signed_bytes_be_slice()
296    }
297}
298impl AsVarintSlice for CqlVarintBorrowed<'_> {
299    fn as_slice(&self) -> &[u8] {
300        self.as_signed_bytes_be_slice()
301    }
302}
303
304/// An internal utility trait used to implement [`PartialEq`] and [`std::hash::Hash`]
305/// for [`CqlVarint`] and [`CqlVarintBorrowed`].
306trait AsNormalizedVarintSlice {
307    fn as_normalized_slice(&self) -> &[u8];
308}
309impl<V: AsVarintSlice> AsNormalizedVarintSlice for V {
310    fn as_normalized_slice(&self) -> &[u8] {
311        let digits = self.as_slice();
312        if digits.is_empty() {
313            // num-bigint crate normalizes empty vector to 0.
314            // We will follow the same approach.
315            return &[0];
316        }
317
318        let non_zero_position = match digits.iter().position(|b| *b != 0) {
319            Some(pos) => pos,
320            None => {
321                // Vector is filled with zeros. Represent it as 0.
322                return &[0];
323            }
324        };
325
326        if non_zero_position > 0 {
327            // There were some leading zeros.
328            // Now, there are two cases:
329            let zeros_to_remove = if digits[non_zero_position] > 0x7f {
330                // Most significant bit is 1, so we need to include one of the leading
331                // zeros as originally it represented a positive number.
332                non_zero_position - 1
333            } else {
334                // Most significant bit is 0 - positive number with no leading zeros.
335                non_zero_position
336            };
337            return &digits[zeros_to_remove..];
338        }
339
340        // There were no leading zeros at all - leave as is.
341        digits
342    }
343}
344
345/// Compares two [`CqlVarint`] values after normalization.
346///
347/// # Example
348///
349/// ```rust
350/// # use scylla_cql::value::CqlVarint;
351/// let non_normalized_bytes = vec![0x00, 0x01];
352/// let normalized_bytes = vec![0x01];
353/// assert_eq!(
354///     CqlVarint::from_signed_bytes_be(non_normalized_bytes),
355///     CqlVarint::from_signed_bytes_be(normalized_bytes)
356/// );
357/// ```
358impl PartialEq for CqlVarint {
359    fn eq(&self, other: &Self) -> bool {
360        self.as_normalized_slice() == other.as_normalized_slice()
361    }
362}
363
364/// Computes the hash of normalized [`CqlVarint`].
365impl std::hash::Hash for CqlVarint {
366    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
367        self.as_normalized_slice().hash(state)
368    }
369}
370
371/// Compares two [`CqlVarintBorrowed`] values after normalization.
372///
373/// # Example
374///
375/// ```rust
376/// # use scylla_cql::value::CqlVarintBorrowed;
377/// let non_normalized_bytes = &[0x00, 0x01];
378/// let normalized_bytes = &[0x01];
379/// assert_eq!(
380///     CqlVarintBorrowed::from_signed_bytes_be_slice(non_normalized_bytes),
381///     CqlVarintBorrowed::from_signed_bytes_be_slice(normalized_bytes)
382/// );
383/// ```
384impl PartialEq for CqlVarintBorrowed<'_> {
385    fn eq(&self, other: &Self) -> bool {
386        self.as_normalized_slice() == other.as_normalized_slice()
387    }
388}
389
390/// Computes the hash of normalized [`CqlVarintBorrowed`].
391impl std::hash::Hash for CqlVarintBorrowed<'_> {
392    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
393        self.as_normalized_slice().hash(state)
394    }
395}
396
397#[cfg(feature = "num-bigint-03")]
398impl From<num_bigint_03::BigInt> for CqlVarint {
399    fn from(value: num_bigint_03::BigInt) -> Self {
400        Self(value.to_signed_bytes_be())
401    }
402}
403
404#[cfg(feature = "num-bigint-03")]
405impl From<CqlVarint> for num_bigint_03::BigInt {
406    fn from(val: CqlVarint) -> Self {
407        num_bigint_03::BigInt::from_signed_bytes_be(&val.0)
408    }
409}
410
411#[cfg(feature = "num-bigint-03")]
412impl From<CqlVarintBorrowed<'_>> for num_bigint_03::BigInt {
413    fn from(val: CqlVarintBorrowed<'_>) -> Self {
414        num_bigint_03::BigInt::from_signed_bytes_be(val.0)
415    }
416}
417
418#[cfg(feature = "num-bigint-04")]
419impl From<num_bigint_04::BigInt> for CqlVarint {
420    fn from(value: num_bigint_04::BigInt) -> Self {
421        Self(value.to_signed_bytes_be())
422    }
423}
424
425#[cfg(feature = "num-bigint-04")]
426impl From<CqlVarint> for num_bigint_04::BigInt {
427    fn from(val: CqlVarint) -> Self {
428        num_bigint_04::BigInt::from_signed_bytes_be(&val.0)
429    }
430}
431
432#[cfg(feature = "num-bigint-04")]
433impl From<CqlVarintBorrowed<'_>> for num_bigint_04::BigInt {
434    fn from(val: CqlVarintBorrowed<'_>) -> Self {
435        num_bigint_04::BigInt::from_signed_bytes_be(val.0)
436    }
437}
438
439/// Native CQL `decimal` representation.
440///
441/// Represented as a pair:
442/// - a [`CqlVarint`] value
443/// - 32-bit integer which determines the position of the decimal point
444///
445/// This struct holds owned bytes. If you wish to borrow the bytes instead,
446/// see [`CqlDecimalBorrowed`] documentation.
447///
448/// The type is not very useful in most use cases.
449/// However, users can make use of more complex types
450/// such as `bigdecimal::BigDecimal` (v0.4).
451/// The library support (e.g. conversion from [`CqlValue`]) for the type is
452/// enabled via `bigdecimal-04` crate feature.
453///
454/// # DB data format
455/// Notice that [constructors](CqlDecimal#impl-CqlDecimal)
456/// don't perform any normalization on the provided data.
457/// For more details, see [`CqlVarint`] documentation.
458#[derive(Clone, PartialEq, Eq, Debug)]
459pub struct CqlDecimal {
460    int_val: CqlVarint,
461    scale: i32,
462}
463
464/// Borrowed version of native CQL `decimal` representation.
465///
466/// Represented as a pair:
467/// - a [`CqlVarintBorrowed`] value
468/// - 32-bit integer which determines the position of the decimal point
469///
470/// Refer to the documentation of [`CqlDecimal`].
471/// Especially, see the disclaimer about [non-normalized values](CqlDecimal#db-data-format).
472#[derive(Clone, PartialEq, Eq, Debug)]
473pub struct CqlDecimalBorrowed<'b> {
474    int_val: CqlVarintBorrowed<'b>,
475    scale: i32,
476}
477
478/// Constructors
479impl CqlDecimal {
480    /// Creates a [`CqlDecimal`] from an array of bytes
481    /// representing [`CqlVarint`] and a 32-bit scale.
482    ///
483    /// See: disclaimer about [non-normalized values](CqlVarint#db-data-format).
484    pub fn from_signed_be_bytes_and_exponent(bytes: Vec<u8>, scale: i32) -> Self {
485        Self {
486            int_val: CqlVarint::from_signed_bytes_be(bytes),
487            scale,
488        }
489    }
490
491    /// Creates a [`CqlDecimal`] from a slice of bytes
492    /// representing [`CqlVarint`] and a 32-bit scale.
493    ///
494    /// See: disclaimer about [non-normalized values](CqlVarint#db-data-format).
495    pub fn from_signed_be_bytes_slice_and_exponent(bytes: &[u8], scale: i32) -> Self {
496        Self::from_signed_be_bytes_and_exponent(bytes.to_vec(), scale)
497    }
498}
499
500/// Constructors
501impl<'b> CqlDecimalBorrowed<'b> {
502    /// Creates a [`CqlDecimalBorrowed`] from a slice of bytes
503    /// representing [`CqlVarintBorrowed`] and a 32-bit scale.
504    ///
505    /// See: disclaimer about [non-normalized values](CqlVarint#db-data-format).
506    pub fn from_signed_be_bytes_slice_and_exponent(bytes: &'b [u8], scale: i32) -> Self {
507        Self {
508            int_val: CqlVarintBorrowed::from_signed_bytes_be_slice(bytes),
509            scale,
510        }
511    }
512}
513
514/// Conversion to raw bytes
515impl CqlDecimal {
516    /// Returns a slice of bytes in two's complement
517    /// binary big-endian representation and a scale.
518    pub fn as_signed_be_bytes_slice_and_exponent(&self) -> (&[u8], i32) {
519        (self.int_val.as_signed_bytes_be_slice(), self.scale)
520    }
521
522    /// Converts [`CqlDecimal`] to an array of bytes in two's
523    /// complement binary big-endian representation and a scale.
524    pub fn into_signed_be_bytes_and_exponent(self) -> (Vec<u8>, i32) {
525        (self.int_val.into_signed_bytes_be(), self.scale)
526    }
527}
528
529/// Conversion to raw bytes
530impl CqlDecimalBorrowed<'_> {
531    /// Returns a slice of bytes in two's complement
532    /// binary big-endian representation and a scale.
533    pub fn as_signed_be_bytes_slice_and_exponent(&self) -> (&[u8], i32) {
534        (self.int_val.as_signed_bytes_be_slice(), self.scale)
535    }
536}
537
538#[cfg(feature = "bigdecimal-04")]
539impl From<CqlDecimal> for bigdecimal_04::BigDecimal {
540    fn from(value: CqlDecimal) -> Self {
541        Self::from((
542            bigdecimal_04::num_bigint::BigInt::from_signed_bytes_be(
543                value.int_val.as_signed_bytes_be_slice(),
544            ),
545            value.scale as i64,
546        ))
547    }
548}
549
550#[cfg(feature = "bigdecimal-04")]
551impl From<CqlDecimalBorrowed<'_>> for bigdecimal_04::BigDecimal {
552    fn from(value: CqlDecimalBorrowed) -> Self {
553        Self::from((
554            bigdecimal_04::num_bigint::BigInt::from_signed_bytes_be(
555                value.int_val.as_signed_bytes_be_slice(),
556            ),
557            value.scale as i64,
558        ))
559    }
560}
561
562#[cfg(feature = "bigdecimal-04")]
563impl TryFrom<bigdecimal_04::BigDecimal> for CqlDecimal {
564    type Error = <i64 as TryInto<i32>>::Error;
565
566    fn try_from(value: bigdecimal_04::BigDecimal) -> Result<Self, Self::Error> {
567        let (bigint, scale) = value.into_bigint_and_exponent();
568        let bytes = bigint.to_signed_bytes_be();
569        Ok(Self::from_signed_be_bytes_and_exponent(
570            bytes,
571            scale.try_into()?,
572        ))
573    }
574}
575
576/// Native CQL date representation that allows for a bigger range of dates (-262145-1-1 to 262143-12-31).
577///
578/// Represented as number of days since -5877641-06-23 i.e. 2^31 days before unix epoch.
579#[derive(Clone, Copy, PartialEq, Eq, Debug)]
580pub struct CqlDate(pub u32);
581
582/// Native CQL timestamp representation that allows full supported timestamp range.
583///
584/// Represented as signed milliseconds since unix epoch.
585#[derive(Clone, Copy, PartialEq, Eq, Debug)]
586pub struct CqlTimestamp(pub i64);
587
588/// Native CQL time representation.
589///
590/// Represented as nanoseconds since midnight.
591#[derive(Clone, Copy, PartialEq, Eq, Debug)]
592pub struct CqlTime(pub i64);
593
594impl CqlDate {
595    fn try_to_chrono_04_naive_date(&self) -> Result<chrono_04::NaiveDate, ValueOverflow> {
596        let days_since_unix_epoch = self.0 as i64 - (1 << 31);
597
598        // date_days is u32 then converted to i64 then we subtract 2^31;
599        // Max value is 2^31, min value is -2^31. Both values can safely fit in chrono::Duration, this call won't panic
600        let duration_since_unix_epoch =
601            chrono_04::Duration::try_days(days_since_unix_epoch).unwrap();
602
603        chrono_04::NaiveDate::from_yo_opt(1970, 1)
604            .unwrap()
605            .checked_add_signed(duration_since_unix_epoch)
606            .ok_or(ValueOverflow)
607    }
608}
609
610#[cfg(feature = "chrono-04")]
611impl From<chrono_04::NaiveDate> for CqlDate {
612    fn from(value: chrono_04::NaiveDate) -> Self {
613        let unix_epoch = chrono_04::NaiveDate::from_yo_opt(1970, 1).unwrap();
614
615        // `NaiveDate` range is -262145-01-01 to 262143-12-31
616        // Both values are well within supported range
617        let days = ((1 << 31) + value.signed_duration_since(unix_epoch).num_days()) as u32;
618
619        Self(days)
620    }
621}
622
623#[cfg(feature = "chrono-04")]
624impl TryInto<chrono_04::NaiveDate> for CqlDate {
625    type Error = ValueOverflow;
626
627    fn try_into(self) -> Result<chrono_04::NaiveDate, Self::Error> {
628        self.try_to_chrono_04_naive_date()
629    }
630}
631
632impl CqlTimestamp {
633    fn try_to_chrono_04_datetime_utc(
634        &self,
635    ) -> Result<chrono_04::DateTime<chrono_04::Utc>, ValueOverflow> {
636        use chrono_04::TimeZone;
637        match chrono_04::Utc.timestamp_millis_opt(self.0) {
638            chrono_04::LocalResult::Single(datetime) => Ok(datetime),
639            _ => Err(ValueOverflow),
640        }
641    }
642}
643
644#[cfg(feature = "chrono-04")]
645impl From<chrono_04::DateTime<chrono_04::Utc>> for CqlTimestamp {
646    fn from(value: chrono_04::DateTime<chrono_04::Utc>) -> Self {
647        Self(value.timestamp_millis())
648    }
649}
650
651#[cfg(feature = "chrono-04")]
652impl TryInto<chrono_04::DateTime<chrono_04::Utc>> for CqlTimestamp {
653    type Error = ValueOverflow;
654
655    fn try_into(self) -> Result<chrono_04::DateTime<chrono_04::Utc>, Self::Error> {
656        self.try_to_chrono_04_datetime_utc()
657    }
658}
659
660#[cfg(feature = "chrono-04")]
661impl TryFrom<chrono_04::NaiveTime> for CqlTime {
662    type Error = ValueOverflow;
663
664    fn try_from(value: chrono_04::NaiveTime) -> Result<Self, Self::Error> {
665        let nanos = value
666            .signed_duration_since(chrono_04::NaiveTime::MIN)
667            .num_nanoseconds()
668            .unwrap();
669
670        // Value can exceed max CQL time in case of leap second
671        if nanos <= 86399999999999 {
672            Ok(Self(nanos))
673        } else {
674            Err(ValueOverflow)
675        }
676    }
677}
678
679#[cfg(feature = "chrono-04")]
680impl TryInto<chrono_04::NaiveTime> for CqlTime {
681    type Error = ValueOverflow;
682
683    fn try_into(self) -> Result<chrono_04::NaiveTime, Self::Error> {
684        let secs = (self.0 / 1_000_000_000)
685            .try_into()
686            .map_err(|_| ValueOverflow)?;
687        let nanos = (self.0 % 1_000_000_000)
688            .try_into()
689            .map_err(|_| ValueOverflow)?;
690        chrono_04::NaiveTime::from_num_seconds_from_midnight_opt(secs, nanos).ok_or(ValueOverflow)
691    }
692}
693
694#[cfg(feature = "time-03")]
695impl From<time_03::Date> for CqlDate {
696    fn from(value: time_03::Date) -> Self {
697        const JULIAN_DAY_OFFSET: i64 =
698            (1 << 31) - time_03::OffsetDateTime::UNIX_EPOCH.date().to_julian_day() as i64;
699
700        // Statically assert that no possible value will ever overflow
701        const _: () = assert!(
702            time_03::Date::MAX.to_julian_day() as i64 + JULIAN_DAY_OFFSET < u32::MAX as i64
703        );
704        const _: () = assert!(
705            time_03::Date::MIN.to_julian_day() as i64 + JULIAN_DAY_OFFSET > u32::MIN as i64
706        );
707
708        let days = value.to_julian_day() as i64 + JULIAN_DAY_OFFSET;
709
710        Self(days as u32)
711    }
712}
713
714#[cfg(feature = "time-03")]
715impl TryInto<time_03::Date> for CqlDate {
716    type Error = ValueOverflow;
717
718    fn try_into(self) -> Result<time_03::Date, Self::Error> {
719        const JULIAN_DAY_OFFSET: i64 =
720            (1 << 31) - time_03::OffsetDateTime::UNIX_EPOCH.date().to_julian_day() as i64;
721
722        let julian_days = (self.0 as i64 - JULIAN_DAY_OFFSET)
723            .try_into()
724            .map_err(|_| ValueOverflow)?;
725
726        time_03::Date::from_julian_day(julian_days).map_err(|_| ValueOverflow)
727    }
728}
729
730#[cfg(feature = "time-03")]
731impl From<time_03::OffsetDateTime> for CqlTimestamp {
732    fn from(value: time_03::OffsetDateTime) -> Self {
733        // Statically assert that no possible value will ever overflow. OffsetDateTime doesn't allow offset to overflow
734        // the UTC PrimitiveDateTime value value
735        const _: () = assert!(
736            time_03::PrimitiveDateTime::MAX
737                .assume_utc()
738                .unix_timestamp_nanos()
739                // Nanos to millis
740                / 1_000_000
741                < i64::MAX as i128
742        );
743        const _: () = assert!(
744            time_03::PrimitiveDateTime::MIN
745                .assume_utc()
746                .unix_timestamp_nanos()
747                / 1_000_000
748                > i64::MIN as i128
749        );
750
751        // Edge cases were statically asserted above, checked math is not required
752        Self(value.unix_timestamp() * 1000 + value.millisecond() as i64)
753    }
754}
755
756#[cfg(feature = "time-03")]
757impl TryInto<time_03::OffsetDateTime> for CqlTimestamp {
758    type Error = ValueOverflow;
759
760    fn try_into(self) -> Result<time_03::OffsetDateTime, Self::Error> {
761        time_03::OffsetDateTime::from_unix_timestamp_nanos(self.0 as i128 * 1_000_000)
762            .map_err(|_| ValueOverflow)
763    }
764}
765
766#[cfg(feature = "time-03")]
767impl From<time_03::Time> for CqlTime {
768    fn from(value: time_03::Time) -> Self {
769        let (h, m, s, n) = value.as_hms_nano();
770
771        // no need for checked arithmetic as all these types are guaranteed to fit in i64 without overflow
772        let nanos = (h as i64 * 3600 + m as i64 * 60 + s as i64) * 1_000_000_000 + n as i64;
773
774        Self(nanos)
775    }
776}
777
778#[cfg(feature = "time-03")]
779impl TryInto<time_03::Time> for CqlTime {
780    type Error = ValueOverflow;
781
782    fn try_into(self) -> Result<time_03::Time, Self::Error> {
783        let h = self.0 / 3_600_000_000_000;
784        let m = self.0 / 60_000_000_000 % 60;
785        let s = self.0 / 1_000_000_000 % 60;
786        let n = self.0 % 1_000_000_000;
787
788        time_03::Time::from_hms_nano(
789            h.try_into().map_err(|_| ValueOverflow)?,
790            m as u8,
791            s as u8,
792            n as u32,
793        )
794        .map_err(|_| ValueOverflow)
795    }
796}
797
798/// Represents a CQL Duration value
799#[derive(Clone, Debug, Copy, PartialEq, Eq)]
800pub struct CqlDuration {
801    pub months: i32,
802    pub days: i32,
803    pub nanoseconds: i64,
804}
805
806#[derive(Clone, Debug, PartialEq)]
807#[non_exhaustive]
808pub enum CqlValue {
809    Ascii(String),
810    Boolean(bool),
811    Blob(Vec<u8>),
812    Counter(Counter),
813    Decimal(CqlDecimal),
814    /// Days since -5877641-06-23 i.e. 2^31 days before unix epoch
815    /// Can be converted to chrono::NaiveDate (-262145-1-1 to 262143-12-31) using as_date
816    Date(CqlDate),
817    Double(f64),
818    Duration(CqlDuration),
819    Empty,
820    Float(f32),
821    Int(i32),
822    BigInt(i64),
823    Text(String),
824    /// Milliseconds since unix epoch
825    Timestamp(CqlTimestamp),
826    Inet(IpAddr),
827    List(Vec<CqlValue>),
828    Map(Vec<(CqlValue, CqlValue)>),
829    Set(Vec<CqlValue>),
830    UserDefinedType {
831        keyspace: String,
832        name: String,
833        /// Order of `fields` vector must match the order of fields as defined in the UDT. The
834        /// driver does not check it by itself, so incorrect data will be written if the order is
835        /// wrong.
836        fields: Vec<(String, Option<CqlValue>)>,
837    },
838    SmallInt(i16),
839    TinyInt(i8),
840    /// Nanoseconds since midnight
841    Time(CqlTime),
842    Timeuuid(CqlTimeuuid),
843    Tuple(Vec<Option<CqlValue>>),
844    Uuid(Uuid),
845    Varint(CqlVarint),
846}
847
848impl CqlValue {
849    pub fn as_ascii(&self) -> Option<&String> {
850        match self {
851            Self::Ascii(s) => Some(s),
852            _ => None,
853        }
854    }
855
856    pub fn as_cql_date(&self) -> Option<CqlDate> {
857        match self {
858            Self::Date(d) => Some(*d),
859            _ => None,
860        }
861    }
862
863    #[cfg(test)]
864    #[cfg(feature = "chrono-04")]
865    pub(crate) fn as_naive_date_04(&self) -> Option<chrono_04::NaiveDate> {
866        self.as_cql_date().and_then(|date| date.try_into().ok())
867    }
868
869    #[cfg(test)]
870    #[cfg(feature = "time-03")]
871    pub(crate) fn as_date_03(&self) -> Option<time_03::Date> {
872        self.as_cql_date().and_then(|date| date.try_into().ok())
873    }
874
875    pub fn as_cql_timestamp(&self) -> Option<CqlTimestamp> {
876        match self {
877            Self::Timestamp(i) => Some(*i),
878            _ => None,
879        }
880    }
881
882    #[cfg(test)]
883    #[cfg(feature = "chrono-04")]
884    pub(crate) fn as_datetime_04(&self) -> Option<chrono_04::DateTime<chrono_04::Utc>> {
885        self.as_cql_timestamp().and_then(|ts| ts.try_into().ok())
886    }
887
888    #[cfg(test)]
889    #[cfg(feature = "time-03")]
890    pub(crate) fn as_offset_date_time_03(&self) -> Option<time_03::OffsetDateTime> {
891        self.as_cql_timestamp().and_then(|ts| ts.try_into().ok())
892    }
893
894    pub fn as_cql_time(&self) -> Option<CqlTime> {
895        match self {
896            Self::Time(i) => Some(*i),
897            _ => None,
898        }
899    }
900
901    #[cfg(test)]
902    #[cfg(feature = "chrono-04")]
903    pub(crate) fn as_naive_time_04(&self) -> Option<chrono_04::NaiveTime> {
904        self.as_cql_time().and_then(|ts| ts.try_into().ok())
905    }
906
907    #[cfg(test)]
908    #[cfg(feature = "time-03")]
909    pub(crate) fn as_time_03(&self) -> Option<time_03::Time> {
910        self.as_cql_time().and_then(|ts| ts.try_into().ok())
911    }
912
913    pub fn as_cql_duration(&self) -> Option<CqlDuration> {
914        match self {
915            Self::Duration(i) => Some(*i),
916            _ => None,
917        }
918    }
919
920    pub fn as_counter(&self) -> Option<Counter> {
921        match self {
922            Self::Counter(i) => Some(*i),
923            _ => None,
924        }
925    }
926
927    pub fn as_boolean(&self) -> Option<bool> {
928        match self {
929            Self::Boolean(i) => Some(*i),
930            _ => None,
931        }
932    }
933
934    pub fn as_double(&self) -> Option<f64> {
935        match self {
936            Self::Double(d) => Some(*d),
937            _ => None,
938        }
939    }
940
941    pub fn as_uuid(&self) -> Option<Uuid> {
942        match self {
943            Self::Uuid(u) => Some(*u),
944            _ => None,
945        }
946    }
947
948    pub fn as_float(&self) -> Option<f32> {
949        match self {
950            Self::Float(f) => Some(*f),
951            _ => None,
952        }
953    }
954
955    pub fn as_int(&self) -> Option<i32> {
956        match self {
957            Self::Int(i) => Some(*i),
958            _ => None,
959        }
960    }
961
962    pub fn as_bigint(&self) -> Option<i64> {
963        match self {
964            Self::BigInt(i) => Some(*i),
965            _ => None,
966        }
967    }
968
969    pub fn as_tinyint(&self) -> Option<i8> {
970        match self {
971            Self::TinyInt(i) => Some(*i),
972            _ => None,
973        }
974    }
975
976    pub fn as_smallint(&self) -> Option<i16> {
977        match self {
978            Self::SmallInt(i) => Some(*i),
979            _ => None,
980        }
981    }
982
983    pub fn as_blob(&self) -> Option<&Vec<u8>> {
984        match self {
985            Self::Blob(v) => Some(v),
986            _ => None,
987        }
988    }
989
990    pub fn as_text(&self) -> Option<&String> {
991        match self {
992            Self::Text(s) => Some(s),
993            _ => None,
994        }
995    }
996
997    pub fn as_timeuuid(&self) -> Option<CqlTimeuuid> {
998        match self {
999            Self::Timeuuid(u) => Some(*u),
1000            _ => None,
1001        }
1002    }
1003
1004    pub fn into_string(self) -> Option<String> {
1005        match self {
1006            Self::Ascii(s) => Some(s),
1007            Self::Text(s) => Some(s),
1008            _ => None,
1009        }
1010    }
1011
1012    pub fn into_blob(self) -> Option<Vec<u8>> {
1013        match self {
1014            Self::Blob(b) => Some(b),
1015            _ => None,
1016        }
1017    }
1018
1019    pub fn as_inet(&self) -> Option<IpAddr> {
1020        match self {
1021            Self::Inet(a) => Some(*a),
1022            _ => None,
1023        }
1024    }
1025
1026    pub fn as_list(&self) -> Option<&Vec<CqlValue>> {
1027        match self {
1028            Self::List(s) => Some(s),
1029            _ => None,
1030        }
1031    }
1032
1033    pub fn as_set(&self) -> Option<&Vec<CqlValue>> {
1034        match self {
1035            Self::Set(s) => Some(s),
1036            _ => None,
1037        }
1038    }
1039
1040    pub fn as_map(&self) -> Option<&Vec<(CqlValue, CqlValue)>> {
1041        match self {
1042            Self::Map(s) => Some(s),
1043            _ => None,
1044        }
1045    }
1046
1047    pub fn as_udt(&self) -> Option<&Vec<(String, Option<CqlValue>)>> {
1048        match self {
1049            Self::UserDefinedType { fields, .. } => Some(fields),
1050            _ => None,
1051        }
1052    }
1053
1054    pub fn into_vec(self) -> Option<Vec<CqlValue>> {
1055        match self {
1056            Self::List(s) => Some(s),
1057            Self::Set(s) => Some(s),
1058            _ => None,
1059        }
1060    }
1061
1062    pub fn into_pair_vec(self) -> Option<Vec<(CqlValue, CqlValue)>> {
1063        match self {
1064            Self::Map(s) => Some(s),
1065            _ => None,
1066        }
1067    }
1068
1069    pub fn into_udt_pair_vec(self) -> Option<Vec<(String, Option<CqlValue>)>> {
1070        match self {
1071            Self::UserDefinedType { fields, .. } => Some(fields),
1072            _ => None,
1073        }
1074    }
1075
1076    pub fn into_cql_varint(self) -> Option<CqlVarint> {
1077        match self {
1078            Self::Varint(i) => Some(i),
1079            _ => None,
1080        }
1081    }
1082
1083    pub fn into_cql_decimal(self) -> Option<CqlDecimal> {
1084        match self {
1085            Self::Decimal(i) => Some(i),
1086            _ => None,
1087        }
1088    }
1089    // TODO
1090}
1091
1092/// Displays a CqlValue. The syntax should resemble the CQL literal syntax
1093/// (but no guarantee is given that it's always the same).
1094impl std::fmt::Display for CqlValue {
1095    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1096        use crate::pretty::{
1097            CqlStringLiteralDisplayer, HexBytes, MaybeNullDisplayer, PairDisplayer,
1098        };
1099        use itertools::Itertools;
1100
1101        match self {
1102            // Scalar types
1103            CqlValue::Ascii(a) => write!(f, "{}", CqlStringLiteralDisplayer(a))?,
1104            CqlValue::Text(t) => write!(f, "{}", CqlStringLiteralDisplayer(t))?,
1105            CqlValue::Blob(b) => write!(f, "0x{:x}", HexBytes(b))?,
1106            CqlValue::Empty => write!(f, "0x")?,
1107            CqlValue::Decimal(d) => {
1108                let (bytes, scale) = d.as_signed_be_bytes_slice_and_exponent();
1109                write!(
1110                    f,
1111                    "blobAsDecimal(0x{:x}{:x})",
1112                    HexBytes(&scale.to_be_bytes()),
1113                    HexBytes(bytes)
1114                )?
1115            }
1116            CqlValue::Float(fl) => write!(f, "{}", fl)?,
1117            CqlValue::Double(d) => write!(f, "{}", d)?,
1118            CqlValue::Boolean(b) => write!(f, "{}", b)?,
1119            CqlValue::Int(i) => write!(f, "{}", i)?,
1120            CqlValue::BigInt(bi) => write!(f, "{}", bi)?,
1121            CqlValue::Inet(i) => write!(f, "'{}'", i)?,
1122            CqlValue::SmallInt(si) => write!(f, "{}", si)?,
1123            CqlValue::TinyInt(ti) => write!(f, "{}", ti)?,
1124            CqlValue::Varint(vi) => write!(
1125                f,
1126                "blobAsVarint(0x{:x})",
1127                HexBytes(vi.as_signed_bytes_be_slice())
1128            )?,
1129            CqlValue::Counter(c) => write!(f, "{}", c.0)?,
1130            CqlValue::Date(d) => {
1131                // TODO: chrono::NaiveDate does not handle the whole range
1132                // supported by the `date` datatype
1133                match d.try_to_chrono_04_naive_date() {
1134                    Ok(d) => write!(f, "'{}'", d)?,
1135                    Err(_) => f.write_str("<date out of representable range>")?,
1136                }
1137            }
1138            CqlValue::Duration(d) => write!(f, "{}mo{}d{}ns", d.months, d.days, d.nanoseconds)?,
1139            CqlValue::Time(CqlTime(t)) => {
1140                write!(
1141                    f,
1142                    "'{:02}:{:02}:{:02}.{:09}'",
1143                    t / 3_600_000_000_000,
1144                    t / 60_000_000_000 % 60,
1145                    t / 1_000_000_000 % 60,
1146                    t % 1_000_000_000,
1147                )?;
1148            }
1149            CqlValue::Timestamp(ts) => match ts.try_to_chrono_04_datetime_utc() {
1150                Ok(d) => write!(f, "{}", d.format("'%Y-%m-%d %H:%M:%S%.3f%z'"))?,
1151                Err(_) => f.write_str("<timestamp out of representable range>")?,
1152            },
1153            CqlValue::Timeuuid(t) => write!(f, "{}", t)?,
1154            CqlValue::Uuid(u) => write!(f, "{}", u)?,
1155
1156            // Compound types
1157            CqlValue::Tuple(t) => {
1158                f.write_str("(")?;
1159                t.iter()
1160                    .map(|x| MaybeNullDisplayer(x.as_ref()))
1161                    .format(",")
1162                    .fmt(f)?;
1163                f.write_str(")")?;
1164            }
1165            CqlValue::List(v) => {
1166                f.write_str("[")?;
1167                v.iter().format(",").fmt(f)?;
1168                f.write_str("]")?;
1169            }
1170            CqlValue::Set(v) => {
1171                f.write_str("{")?;
1172                v.iter().format(",").fmt(f)?;
1173                f.write_str("}")?;
1174            }
1175            CqlValue::Map(m) => {
1176                f.write_str("{")?;
1177                m.iter()
1178                    .map(|(k, v)| PairDisplayer(k, v))
1179                    .format(",")
1180                    .fmt(f)?;
1181                f.write_str("}")?;
1182            }
1183            CqlValue::UserDefinedType {
1184                keyspace: _,
1185                name: _,
1186                fields,
1187            } => {
1188                f.write_str("{")?;
1189                fields
1190                    .iter()
1191                    .map(|(k, v)| PairDisplayer(k, MaybeNullDisplayer(v.as_ref())))
1192                    .format(",")
1193                    .fmt(f)?;
1194                f.write_str("}")?;
1195            }
1196        }
1197        Ok(())
1198    }
1199}
1200
1201pub fn deser_cql_value(
1202    typ: &ColumnType,
1203    buf: &mut &[u8],
1204) -> StdResult<CqlValue, DeserializationError> {
1205    use crate::frame::response::result::ColumnType::*;
1206    use crate::frame::response::result::NativeType::*;
1207
1208    if buf.is_empty() {
1209        match typ {
1210            Native(Ascii) | Native(Blob) | Native(Text) => {
1211                // can't be empty
1212            }
1213            _ => return Ok(CqlValue::Empty),
1214        }
1215    }
1216    // The `new_borrowed` version of FrameSlice is deficient in that it does not hold
1217    // a `Bytes` reference to the frame, only a slice.
1218    // This is not a problem here, fortunately, because none of CqlValue variants contain
1219    // any `Bytes` - only exclusively owned types - so we never call FrameSlice::to_bytes().
1220    let v = Some(FrameSlice::new_borrowed(buf));
1221
1222    Ok(match typ {
1223        Native(Ascii) => {
1224            let s = String::deserialize(typ, v)?;
1225            CqlValue::Ascii(s)
1226        }
1227        Native(Boolean) => {
1228            let b = bool::deserialize(typ, v)?;
1229            CqlValue::Boolean(b)
1230        }
1231        Native(Blob) => {
1232            let b = Vec::<u8>::deserialize(typ, v)?;
1233            CqlValue::Blob(b)
1234        }
1235        Native(Date) => {
1236            let d = CqlDate::deserialize(typ, v)?;
1237            CqlValue::Date(d)
1238        }
1239        Native(Counter) => {
1240            let c = crate::value::Counter::deserialize(typ, v)?;
1241            CqlValue::Counter(c)
1242        }
1243        Native(Decimal) => {
1244            let d = CqlDecimal::deserialize(typ, v)?;
1245            CqlValue::Decimal(d)
1246        }
1247        Native(Double) => {
1248            let d = f64::deserialize(typ, v)?;
1249            CqlValue::Double(d)
1250        }
1251        Native(Float) => {
1252            let f = f32::deserialize(typ, v)?;
1253            CqlValue::Float(f)
1254        }
1255        Native(Int) => {
1256            let i = i32::deserialize(typ, v)?;
1257            CqlValue::Int(i)
1258        }
1259        Native(SmallInt) => {
1260            let si = i16::deserialize(typ, v)?;
1261            CqlValue::SmallInt(si)
1262        }
1263        Native(TinyInt) => {
1264            let ti = i8::deserialize(typ, v)?;
1265            CqlValue::TinyInt(ti)
1266        }
1267        Native(BigInt) => {
1268            let bi = i64::deserialize(typ, v)?;
1269            CqlValue::BigInt(bi)
1270        }
1271        Native(Text) => {
1272            let s = String::deserialize(typ, v)?;
1273            CqlValue::Text(s)
1274        }
1275        Native(Timestamp) => {
1276            let t = CqlTimestamp::deserialize(typ, v)?;
1277            CqlValue::Timestamp(t)
1278        }
1279        Native(Time) => {
1280            let t = CqlTime::deserialize(typ, v)?;
1281            CqlValue::Time(t)
1282        }
1283        Native(Timeuuid) => {
1284            let t = CqlTimeuuid::deserialize(typ, v)?;
1285            CqlValue::Timeuuid(t)
1286        }
1287        Native(Duration) => {
1288            let d = CqlDuration::deserialize(typ, v)?;
1289            CqlValue::Duration(d)
1290        }
1291        Native(Inet) => {
1292            let i = IpAddr::deserialize(typ, v)?;
1293            CqlValue::Inet(i)
1294        }
1295        Native(Uuid) => {
1296            let uuid = uuid::Uuid::deserialize(typ, v)?;
1297            CqlValue::Uuid(uuid)
1298        }
1299        Native(Varint) => {
1300            let vi = CqlVarint::deserialize(typ, v)?;
1301            CqlValue::Varint(vi)
1302        }
1303        Collection {
1304            typ: CollectionType::List(_type_name),
1305            ..
1306        } => {
1307            let l = Vec::<CqlValue>::deserialize(typ, v)?;
1308            CqlValue::List(l)
1309        }
1310        Collection {
1311            typ: CollectionType::Map(_key_type, _value_type),
1312            ..
1313        } => {
1314            let iter = MapIterator::<'_, '_, CqlValue, CqlValue>::deserialize(typ, v)?;
1315            let m: Vec<(CqlValue, CqlValue)> = iter.collect::<StdResult<_, _>>()?;
1316            CqlValue::Map(m)
1317        }
1318        Collection {
1319            typ: CollectionType::Set(_type_name),
1320            ..
1321        } => {
1322            let s = Vec::<CqlValue>::deserialize(typ, v)?;
1323            CqlValue::Set(s)
1324        }
1325        Vector { .. } => {
1326            return Err(mk_deser_err::<CqlValue>(
1327                typ,
1328                BuiltinDeserializationErrorKind::Unsupported,
1329            ))
1330        }
1331        UserDefinedType {
1332            definition: udt, ..
1333        } => {
1334            let iter = UdtIterator::deserialize(typ, v)?;
1335            let fields: Vec<(String, Option<CqlValue>)> = iter
1336                .map(|((col_name, col_type), res)| {
1337                    res.and_then(|v| {
1338                        let val = Option::<CqlValue>::deserialize(col_type, v.flatten())?;
1339                        Ok((col_name.clone().into_owned(), val))
1340                    })
1341                })
1342                .collect::<StdResult<_, _>>()?;
1343
1344            CqlValue::UserDefinedType {
1345                keyspace: udt.keyspace.clone().into_owned(),
1346                name: udt.name.clone().into_owned(),
1347                fields,
1348            }
1349        }
1350        Tuple(type_names) => {
1351            let t = type_names
1352                .iter()
1353                .map(|typ| -> StdResult<_, DeserializationError> {
1354                    let raw = types::read_bytes_opt(buf).map_err(|e| {
1355                        mk_deser_err::<CqlValue>(
1356                            typ,
1357                            BuiltinDeserializationErrorKind::RawCqlBytesReadError(e),
1358                        )
1359                    })?;
1360                    raw.map(|v| CqlValue::deserialize(typ, Some(FrameSlice::new_borrowed(v))))
1361                        .transpose()
1362                })
1363                .collect::<StdResult<_, _>>()?;
1364            CqlValue::Tuple(t)
1365        }
1366    })
1367}
1368
1369#[derive(Debug, Default, PartialEq)]
1370pub struct Row {
1371    pub columns: Vec<Option<CqlValue>>,
1372}
1373
1374#[cfg(test)]
1375mod tests {
1376    use std::str::FromStr as _;
1377
1378    use super::*;
1379
1380    #[test]
1381    fn timeuuid_msb_byte_order() {
1382        let uuid = CqlTimeuuid::from_str("00010203-0405-0607-0809-0a0b0c0d0e0f").unwrap();
1383
1384        assert_eq!(0x0607040500010203, uuid.msb());
1385    }
1386
1387    #[test]
1388    fn timeuuid_msb_clears_version_bits() {
1389        // UUID version nibble should be cleared
1390        let uuid = CqlTimeuuid::from_str("ffffffff-ffff-ffff-ffff-ffffffffffff").unwrap();
1391
1392        assert_eq!(0x0fffffffffffffff, uuid.msb());
1393    }
1394
1395    #[test]
1396    fn timeuuid_lsb_byte_order() {
1397        let uuid = CqlTimeuuid::from_str("00010203-0405-0607-0809-0a0b0c0d0e0f").unwrap();
1398
1399        assert_eq!(0x08090a0b0c0d0e0f, uuid.lsb());
1400    }
1401
1402    #[test]
1403    fn timeuuid_lsb_modifies_no_bits() {
1404        let uuid = CqlTimeuuid::from_str("ffffffff-ffff-ffff-ffff-ffffffffffff").unwrap();
1405
1406        assert_eq!(0xffffffffffffffff, uuid.lsb());
1407    }
1408
1409    #[test]
1410    fn test_cql_value_displayer() {
1411        assert_eq!(format!("{}", CqlValue::Boolean(true)), "true");
1412        assert_eq!(format!("{}", CqlValue::Int(123)), "123");
1413        assert_eq!(
1414            format!(
1415                "{}",
1416                // 123.456
1417                CqlValue::Decimal(CqlDecimal::from_signed_be_bytes_and_exponent(
1418                    vec![0x01, 0xE2, 0x40],
1419                    3
1420                ))
1421            ),
1422            "blobAsDecimal(0x0000000301e240)"
1423        );
1424        assert_eq!(format!("{}", CqlValue::Float(12.75)), "12.75");
1425        assert_eq!(
1426            format!("{}", CqlValue::Text("Ala ma kota".to_owned())),
1427            "'Ala ma kota'"
1428        );
1429        assert_eq!(
1430            format!("{}", CqlValue::Text("Foo's".to_owned())),
1431            "'Foo''s'"
1432        );
1433
1434        // Time types are the most tricky
1435        assert_eq!(
1436            format!("{}", CqlValue::Date(CqlDate(40 + (1 << 31)))),
1437            "'1970-02-10'"
1438        );
1439        assert_eq!(
1440            format!(
1441                "{}",
1442                CqlValue::Duration(CqlDuration {
1443                    months: 1,
1444                    days: 2,
1445                    nanoseconds: 3,
1446                })
1447            ),
1448            "1mo2d3ns"
1449        );
1450        let t = chrono_04::NaiveTime::from_hms_nano_opt(6, 5, 4, 123)
1451            .unwrap()
1452            .signed_duration_since(chrono_04::NaiveTime::MIN);
1453        let t = t.num_nanoseconds().unwrap();
1454        assert_eq!(
1455            format!("{}", CqlValue::Time(CqlTime(t))),
1456            "'06:05:04.000000123'"
1457        );
1458
1459        let t = chrono_04::NaiveDate::from_ymd_opt(2005, 4, 2)
1460            .unwrap()
1461            .and_time(chrono_04::NaiveTime::from_hms_opt(19, 37, 42).unwrap());
1462        assert_eq!(
1463            format!(
1464                "{}",
1465                CqlValue::Timestamp(CqlTimestamp(
1466                    t.signed_duration_since(chrono_04::NaiveDateTime::default())
1467                        .num_milliseconds()
1468                ))
1469            ),
1470            "'2005-04-02 19:37:42.000+0000'"
1471        );
1472
1473        // Compound types
1474        let list_or_set = vec![CqlValue::Int(1), CqlValue::Int(3), CqlValue::Int(2)];
1475        assert_eq!(
1476            format!("{}", CqlValue::List(list_or_set.clone())),
1477            "[1,3,2]"
1478        );
1479        assert_eq!(format!("{}", CqlValue::Set(list_or_set.clone())), "{1,3,2}");
1480
1481        let tuple: Vec<_> = list_or_set
1482            .into_iter()
1483            .map(Some)
1484            .chain(std::iter::once(None))
1485            .collect();
1486        assert_eq!(format!("{}", CqlValue::Tuple(tuple)), "(1,3,2,null)");
1487
1488        let map = vec![
1489            (CqlValue::Text("foo".to_owned()), CqlValue::Int(123)),
1490            (CqlValue::Text("bar".to_owned()), CqlValue::Int(321)),
1491        ];
1492        assert_eq!(format!("{}", CqlValue::Map(map)), "{'foo':123,'bar':321}");
1493
1494        let fields = vec![
1495            ("foo".to_owned(), Some(CqlValue::Int(123))),
1496            ("bar".to_owned(), Some(CqlValue::Int(321))),
1497        ];
1498        assert_eq!(
1499            format!(
1500                "{}",
1501                CqlValue::UserDefinedType {
1502                    keyspace: "ks".to_owned(),
1503                    name: "typ".to_owned(),
1504                    fields,
1505                }
1506            ),
1507            "{foo:123,bar:321}"
1508        );
1509    }
1510}