use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::fmt::Display;
use std::hash::BuildHasher;
use std::net::IpAddr;
use std::ops::Deref as _;
use std::sync::Arc;
use thiserror::Error;
use uuid::Uuid;
use crate::frame::response::result::{ColumnType, CqlValue};
use crate::frame::types::vint_encode;
#[allow(deprecated)]
use crate::frame::value::{
Counter, CqlDate, CqlDecimal, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid, CqlVarint,
MaybeUnset, Unset, Value,
};
#[cfg(feature = "chrono-04")]
use crate::frame::value::ValueOverflow;
use super::writers::WrittenCellProof;
use super::{CellWriter, SerializationError};
pub trait SerializeValue {
fn serialize<'b>(
&self,
typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError>;
}
macro_rules! exact_type_check {
($typ:ident, $($cql:tt),*) => {
match $typ {
$(ColumnType::$cql)|* => {},
_ => return Err(mk_typck_err::<Self>(
$typ,
BuiltinTypeCheckErrorKind::MismatchedType {
expected: &[$(ColumnType::$cql),*],
}
))
}
};
}
macro_rules! impl_serialize_via_writer {
(|$me:ident, $writer:ident| $e:expr) => {
impl_serialize_via_writer!(|$me, _typ, $writer| $e);
};
(|$me:ident, $typ:ident, $writer:ident| $e:expr) => {
fn serialize<'b>(
&self,
typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
let $writer = writer;
let $typ = typ;
let $me = self;
let proof = $e;
Ok(proof)
}
};
}
impl SerializeValue for i8 {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, TinyInt);
writer.set_value(me.to_be_bytes().as_slice()).unwrap()
});
}
impl SerializeValue for i16 {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, SmallInt);
writer.set_value(me.to_be_bytes().as_slice()).unwrap()
});
}
impl SerializeValue for i32 {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Int);
writer.set_value(me.to_be_bytes().as_slice()).unwrap()
});
}
impl SerializeValue for i64 {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, BigInt);
writer.set_value(me.to_be_bytes().as_slice()).unwrap()
});
}
impl SerializeValue for CqlDecimal {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Decimal);
let mut builder = writer.into_value_builder();
let (bytes, scale) = me.as_signed_be_bytes_slice_and_exponent();
builder.append_bytes(&scale.to_be_bytes());
builder.append_bytes(bytes);
builder
.finish()
.map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
});
}
#[cfg(feature = "bigdecimal-04")]
impl SerializeValue for bigdecimal_04::BigDecimal {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Decimal);
let mut builder = writer.into_value_builder();
let (value, scale) = me.as_bigint_and_exponent();
let scale: i32 = scale
.try_into()
.map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::ValueOverflow))?;
builder.append_bytes(&scale.to_be_bytes());
builder.append_bytes(&value.to_signed_bytes_be());
builder
.finish()
.map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
});
}
impl SerializeValue for CqlDate {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Date);
writer.set_value(me.0.to_be_bytes().as_slice()).unwrap()
});
}
impl SerializeValue for CqlTimestamp {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Timestamp);
writer.set_value(me.0.to_be_bytes().as_slice()).unwrap()
});
}
impl SerializeValue for CqlTime {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Time);
writer.set_value(me.0.to_be_bytes().as_slice()).unwrap()
});
}
#[cfg(feature = "chrono-04")]
impl SerializeValue for chrono_04::NaiveDate {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Date);
<CqlDate as SerializeValue>::serialize(&(*me).into(), typ, writer)?
});
}
#[cfg(feature = "chrono-04")]
impl SerializeValue for chrono_04::DateTime<chrono_04::Utc> {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Timestamp);
<CqlTimestamp as SerializeValue>::serialize(&(*me).into(), typ, writer)?
});
}
#[cfg(feature = "chrono-04")]
impl SerializeValue for chrono_04::NaiveTime {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Time);
let cql_time = CqlTime::try_from(*me).map_err(|_: ValueOverflow| {
mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::ValueOverflow)
})?;
<CqlTime as SerializeValue>::serialize(&cql_time, typ, writer)?
});
}
#[cfg(feature = "time-03")]
impl SerializeValue for time_03::Date {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Date);
<CqlDate as SerializeValue>::serialize(&(*me).into(), typ, writer)?
});
}
#[cfg(feature = "time-03")]
impl SerializeValue for time_03::OffsetDateTime {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Timestamp);
<CqlTimestamp as SerializeValue>::serialize(&(*me).into(), typ, writer)?
});
}
#[cfg(feature = "time-03")]
impl SerializeValue for time_03::Time {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Time);
<CqlTime as SerializeValue>::serialize(&(*me).into(), typ, writer)?
});
}
#[cfg(feature = "secrecy-08")]
impl<V: SerializeValue + secrecy_08::Zeroize> SerializeValue for secrecy_08::Secret<V> {
fn serialize<'b>(
&self,
typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
use secrecy_08::ExposeSecret;
V::serialize(self.expose_secret(), typ, writer)
}
}
impl SerializeValue for bool {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Boolean);
writer.set_value(&[*me as u8]).unwrap()
});
}
impl SerializeValue for f32 {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Float);
writer.set_value(me.to_be_bytes().as_slice()).unwrap()
});
}
impl SerializeValue for f64 {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Double);
writer.set_value(me.to_be_bytes().as_slice()).unwrap()
});
}
impl SerializeValue for Uuid {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Uuid);
writer.set_value(me.as_bytes().as_ref()).unwrap()
});
}
impl SerializeValue for CqlTimeuuid {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Timeuuid);
writer.set_value(me.as_bytes().as_ref()).unwrap()
});
}
impl SerializeValue for CqlVarint {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Varint);
writer
.set_value(me.as_signed_bytes_be_slice())
.map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
});
}
#[cfg(feature = "num-bigint-03")]
impl SerializeValue for num_bigint_03::BigInt {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Varint);
writer
.set_value(me.to_signed_bytes_be().as_slice())
.map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
});
}
#[cfg(feature = "num-bigint-04")]
impl SerializeValue for num_bigint_04::BigInt {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Varint);
writer
.set_value(me.to_signed_bytes_be().as_slice())
.map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
});
}
impl SerializeValue for &str {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Ascii, Text);
writer
.set_value(me.as_bytes())
.map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
});
}
impl SerializeValue for Vec<u8> {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Blob);
writer
.set_value(me.as_ref())
.map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
});
}
impl SerializeValue for &[u8] {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Blob);
writer
.set_value(me)
.map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
});
}
impl<const N: usize> SerializeValue for [u8; N] {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Blob);
writer
.set_value(me.as_ref())
.map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
});
}
impl SerializeValue for IpAddr {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Inet);
match me {
IpAddr::V4(ip) => writer.set_value(&ip.octets()).unwrap(),
IpAddr::V6(ip) => writer.set_value(&ip.octets()).unwrap(),
}
});
}
impl SerializeValue for String {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Ascii, Text);
writer
.set_value(me.as_bytes())
.map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
});
}
impl<T: SerializeValue> SerializeValue for Option<T> {
fn serialize<'b>(
&self,
typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
match self {
Some(v) => v.serialize(typ, writer),
None => Ok(writer.set_null()),
}
}
}
impl SerializeValue for Unset {
impl_serialize_via_writer!(|_me, writer| writer.set_unset());
}
impl SerializeValue for Counter {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Counter);
writer.set_value(me.0.to_be_bytes().as_slice()).unwrap()
});
}
impl SerializeValue for CqlDuration {
impl_serialize_via_writer!(|me, typ, writer| {
exact_type_check!(typ, Duration);
let mut buf = Vec::with_capacity(27); vint_encode(me.months as i64, &mut buf);
vint_encode(me.days as i64, &mut buf);
vint_encode(me.nanoseconds, &mut buf);
writer.set_value(buf.as_slice()).unwrap()
});
}
impl<V: SerializeValue> SerializeValue for MaybeUnset<V> {
fn serialize<'b>(
&self,
typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
match self {
MaybeUnset::Set(v) => v.serialize(typ, writer),
MaybeUnset::Unset => Ok(writer.set_unset()),
}
}
}
impl<T: SerializeValue + ?Sized> SerializeValue for &T {
fn serialize<'b>(
&self,
typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
T::serialize(*self, typ, writer)
}
}
impl<T: SerializeValue + ?Sized> SerializeValue for Box<T> {
fn serialize<'b>(
&self,
typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
T::serialize(&**self, typ, writer)
}
}
impl<V: SerializeValue, S: BuildHasher + Default> SerializeValue for HashSet<V, S> {
fn serialize<'b>(
&self,
typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
serialize_sequence(
std::any::type_name::<Self>(),
self.len(),
self.iter(),
typ,
writer,
)
}
}
impl<K: SerializeValue, V: SerializeValue, S: BuildHasher> SerializeValue for HashMap<K, V, S> {
fn serialize<'b>(
&self,
typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
serialize_mapping(
std::any::type_name::<Self>(),
self.len(),
self.iter(),
typ,
writer,
)
}
}
impl<V: SerializeValue> SerializeValue for BTreeSet<V> {
fn serialize<'b>(
&self,
typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
serialize_sequence(
std::any::type_name::<Self>(),
self.len(),
self.iter(),
typ,
writer,
)
}
}
impl<K: SerializeValue, V: SerializeValue> SerializeValue for BTreeMap<K, V> {
fn serialize<'b>(
&self,
typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
serialize_mapping(
std::any::type_name::<Self>(),
self.len(),
self.iter(),
typ,
writer,
)
}
}
impl<T: SerializeValue> SerializeValue for Vec<T> {
fn serialize<'b>(
&self,
typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
serialize_sequence(
std::any::type_name::<Self>(),
self.len(),
self.iter(),
typ,
writer,
)
}
}
impl<'a, T: SerializeValue + 'a> SerializeValue for &'a [T] {
fn serialize<'b>(
&self,
typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
serialize_sequence(
std::any::type_name::<Self>(),
self.len(),
self.iter(),
typ,
writer,
)
}
}
impl SerializeValue for CqlValue {
fn serialize<'b>(
&self,
typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
serialize_cql_value(self, typ, writer).map_err(fix_cql_value_name_in_err)
}
}
fn serialize_cql_value<'b>(
value: &CqlValue,
typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
if let ColumnType::Custom(_) = typ {
return Err(mk_typck_err::<CqlValue>(
typ,
BuiltinTypeCheckErrorKind::CustomTypeUnsupported,
));
}
match value {
CqlValue::Ascii(a) => <_ as SerializeValue>::serialize(&a, typ, writer),
CqlValue::Boolean(b) => <_ as SerializeValue>::serialize(&b, typ, writer),
CqlValue::Blob(b) => <_ as SerializeValue>::serialize(&b, typ, writer),
CqlValue::Counter(c) => <_ as SerializeValue>::serialize(&c, typ, writer),
CqlValue::Decimal(d) => <_ as SerializeValue>::serialize(&d, typ, writer),
CqlValue::Date(d) => <_ as SerializeValue>::serialize(&d, typ, writer),
CqlValue::Double(d) => <_ as SerializeValue>::serialize(&d, typ, writer),
CqlValue::Duration(d) => <_ as SerializeValue>::serialize(&d, typ, writer),
CqlValue::Empty => {
if !typ.supports_special_empty_value() {
return Err(mk_typck_err::<CqlValue>(
typ,
BuiltinTypeCheckErrorKind::NotEmptyable,
));
}
Ok(writer.set_value(&[]).unwrap())
}
CqlValue::Float(f) => <_ as SerializeValue>::serialize(&f, typ, writer),
CqlValue::Int(i) => <_ as SerializeValue>::serialize(&i, typ, writer),
CqlValue::BigInt(b) => <_ as SerializeValue>::serialize(&b, typ, writer),
CqlValue::Text(t) => <_ as SerializeValue>::serialize(&t, typ, writer),
CqlValue::Timestamp(t) => <_ as SerializeValue>::serialize(&t, typ, writer),
CqlValue::Inet(i) => <_ as SerializeValue>::serialize(&i, typ, writer),
CqlValue::List(l) => <_ as SerializeValue>::serialize(&l, typ, writer),
CqlValue::Map(m) => serialize_mapping(
std::any::type_name::<CqlValue>(),
m.len(),
m.iter().map(|p| (&p.0, &p.1)),
typ,
writer,
),
CqlValue::Set(s) => <_ as SerializeValue>::serialize(&s, typ, writer),
CqlValue::UserDefinedType {
keyspace,
type_name,
fields,
} => serialize_udt(typ, keyspace, type_name, fields, writer),
CqlValue::SmallInt(s) => <_ as SerializeValue>::serialize(&s, typ, writer),
CqlValue::TinyInt(t) => <_ as SerializeValue>::serialize(&t, typ, writer),
CqlValue::Time(t) => <_ as SerializeValue>::serialize(&t, typ, writer),
CqlValue::Timeuuid(t) => <_ as SerializeValue>::serialize(&t, typ, writer),
CqlValue::Tuple(t) => {
let fields = match typ {
ColumnType::Tuple(fields) => {
if fields.len() < t.len() {
return Err(mk_typck_err::<CqlValue>(
typ,
TupleTypeCheckErrorKind::WrongElementCount {
rust_type_el_count: t.len(),
cql_type_el_count: fields.len(),
},
));
}
fields
}
_ => {
return Err(mk_typck_err::<CqlValue>(
typ,
TupleTypeCheckErrorKind::NotTuple,
))
}
};
serialize_tuple_like(typ, fields.iter(), t.iter(), writer)
}
CqlValue::Uuid(u) => <_ as SerializeValue>::serialize(&u, typ, writer),
CqlValue::Varint(v) => <_ as SerializeValue>::serialize(&v, typ, writer),
}
}
fn fix_cql_value_name_in_err(mut err: SerializationError) -> SerializationError {
let rust_name = std::any::type_name::<CqlValue>();
match Arc::get_mut(&mut err.0) {
Some(err_mut) => {
if let Some(err) = err_mut.downcast_mut::<BuiltinTypeCheckError>() {
err.rust_name = rust_name;
} else if let Some(err) = err_mut.downcast_mut::<BuiltinSerializationError>() {
err.rust_name = rust_name;
}
}
None => {
if let Some(err) = err.0.downcast_ref::<BuiltinTypeCheckError>() {
if err.rust_name != rust_name {
return SerializationError::new(BuiltinTypeCheckError {
rust_name,
..err.clone()
});
}
}
if let Some(err) = err.0.downcast_ref::<BuiltinSerializationError>() {
if err.rust_name != rust_name {
return SerializationError::new(BuiltinSerializationError {
rust_name,
..err.clone()
});
}
}
}
};
err
}
fn serialize_udt<'b>(
typ: &ColumnType,
keyspace: &str,
type_name: &str,
values: &[(String, Option<CqlValue>)],
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
let (dst_type_name, dst_keyspace, field_types) = match typ {
ColumnType::UserDefinedType {
type_name,
keyspace,
field_types,
} => (type_name, keyspace, field_types),
_ => return Err(mk_typck_err::<CqlValue>(typ, UdtTypeCheckErrorKind::NotUdt)),
};
if keyspace != dst_keyspace || type_name != dst_type_name {
return Err(mk_typck_err::<CqlValue>(
typ,
UdtTypeCheckErrorKind::NameMismatch {
keyspace: dst_keyspace.clone().into_owned(),
type_name: dst_type_name.clone().into_owned(),
},
));
}
let mut indexed_fields: HashMap<_, _> = values.iter().map(|(k, v)| (k.as_str(), v)).collect();
let mut builder = writer.into_value_builder();
for (fname, ftyp) in field_types {
let fvalue = indexed_fields
.remove(fname.deref())
.and_then(|x| x.as_ref());
let writer = builder.make_sub_writer();
match fvalue {
None => writer.set_null(),
Some(v) => serialize_cql_value(v, ftyp, writer).map_err(|err| {
let err = fix_cql_value_name_in_err(err);
mk_ser_err::<CqlValue>(
typ,
UdtSerializationErrorKind::FieldSerializationFailed {
field_name: fname.clone().into_owned(),
err,
},
)
})?,
};
}
if !indexed_fields.is_empty() {
let fname = indexed_fields.keys().min().unwrap();
return Err(mk_typck_err::<CqlValue>(
typ,
UdtTypeCheckErrorKind::NoSuchFieldInUdt {
field_name: fname.to_string(),
},
));
}
builder
.finish()
.map_err(|_| mk_ser_err::<CqlValue>(typ, BuiltinSerializationErrorKind::SizeOverflow))
}
fn serialize_tuple_like<'t, 'b>(
typ: &ColumnType,
field_types: impl Iterator<Item = &'t ColumnType<'t>>,
field_values: impl Iterator<Item = &'t Option<CqlValue>>,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
let mut builder = writer.into_value_builder();
for (index, (el, el_typ)) in field_values.zip(field_types).enumerate() {
let sub = builder.make_sub_writer();
match el {
None => sub.set_null(),
Some(el) => serialize_cql_value(el, el_typ, sub).map_err(|err| {
let err = fix_cql_value_name_in_err(err);
mk_ser_err::<CqlValue>(
typ,
TupleSerializationErrorKind::ElementSerializationFailed { index, err },
)
})?,
};
}
builder
.finish()
.map_err(|_| mk_ser_err::<CqlValue>(typ, BuiltinSerializationErrorKind::SizeOverflow))
}
macro_rules! impl_tuple {
(
$($typs:ident),*;
$($fidents:ident),*;
$($tidents:ident),*;
$length:expr
) => {
impl<$($typs: SerializeValue),*> SerializeValue for ($($typs,)*) {
fn serialize<'b>(
&self,
typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
let ($($tidents,)*) = match typ {
ColumnType::Tuple(typs) => match typs.as_slice() {
[$($tidents),*] => ($($tidents,)*),
_ => return Err(mk_typck_err::<Self>(
typ,
TupleTypeCheckErrorKind::WrongElementCount {
rust_type_el_count: $length,
cql_type_el_count: typs.len(),
}
))
}
_ => return Err(mk_typck_err::<Self>(
typ,
TupleTypeCheckErrorKind::NotTuple,
))
};
let ($($fidents,)*) = self;
let mut builder = writer.into_value_builder();
let index = 0;
$(
<$typs as SerializeValue>::serialize($fidents, $tidents, builder.make_sub_writer())
.map_err(|err| mk_ser_err::<Self>(
typ,
TupleSerializationErrorKind::ElementSerializationFailed {
index,
err,
}
))?;
let index = index + 1;
)*
let _ = index;
builder
.finish()
.map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))
}
}
};
}
macro_rules! impl_tuples {
(;;;$length:expr) => {};
(
$typ:ident$(, $($typs:ident),*)?;
$fident:ident$(, $($fidents:ident),*)?;
$tident:ident$(, $($tidents:ident),*)?;
$length:expr
) => {
impl_tuples!(
$($($typs),*)?;
$($($fidents),*)?;
$($($tidents),*)?;
$length - 1
);
impl_tuple!(
$typ$(, $($typs),*)?;
$fident$(, $($fidents),*)?;
$tident$(, $($tidents),*)?;
$length
);
};
}
impl_tuples!(
T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15;
f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15;
t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15;
16
);
fn serialize_sequence<'t, 'b, T: SerializeValue + 't>(
rust_name: &'static str,
len: usize,
iter: impl Iterator<Item = &'t T>,
typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
let elt = match typ {
ColumnType::List(elt) | ColumnType::Set(elt) => elt,
_ => {
return Err(mk_typck_err_named(
rust_name,
typ,
SetOrListTypeCheckErrorKind::NotSetOrList,
));
}
};
let mut builder = writer.into_value_builder();
let element_count: i32 = len.try_into().map_err(|_| {
mk_ser_err_named(
rust_name,
typ,
SetOrListSerializationErrorKind::TooManyElements,
)
})?;
builder.append_bytes(&element_count.to_be_bytes());
for el in iter {
T::serialize(el, elt, builder.make_sub_writer()).map_err(|err| {
mk_ser_err_named(
rust_name,
typ,
SetOrListSerializationErrorKind::ElementSerializationFailed(err),
)
})?;
}
builder
.finish()
.map_err(|_| mk_ser_err_named(rust_name, typ, BuiltinSerializationErrorKind::SizeOverflow))
}
fn serialize_mapping<'t, 'b, K: SerializeValue + 't, V: SerializeValue + 't>(
rust_name: &'static str,
len: usize,
iter: impl Iterator<Item = (&'t K, &'t V)>,
typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
let (ktyp, vtyp) = match typ {
ColumnType::Map(k, v) => (k, v),
_ => {
return Err(mk_typck_err_named(
rust_name,
typ,
MapTypeCheckErrorKind::NotMap,
));
}
};
let mut builder = writer.into_value_builder();
let element_count: i32 = len.try_into().map_err(|_| {
mk_ser_err_named(rust_name, typ, MapSerializationErrorKind::TooManyElements)
})?;
builder.append_bytes(&element_count.to_be_bytes());
for (k, v) in iter {
K::serialize(k, ktyp, builder.make_sub_writer()).map_err(|err| {
mk_ser_err_named(
rust_name,
typ,
MapSerializationErrorKind::KeySerializationFailed(err),
)
})?;
V::serialize(v, vtyp, builder.make_sub_writer()).map_err(|err| {
mk_ser_err_named(
rust_name,
typ,
MapSerializationErrorKind::ValueSerializationFailed(err),
)
})?;
}
builder
.finish()
.map_err(|_| mk_ser_err_named(rust_name, typ, BuiltinSerializationErrorKind::SizeOverflow))
}
#[deprecated(
since = "0.15.1",
note = "Legacy serialization API is not type-safe and is going to be removed soon"
)]
#[macro_export]
macro_rules! impl_serialize_value_via_value {
($t:ident$(<$($targ:tt $(: $tbound:tt)?),*>)?) => {
impl $(<$($targ $(: $tbound)?),*>)? $crate::types::serialize::value::SerializeValue
for $t$(<$($targ),*>)?
where
Self: $crate::frame::value::Value,
{
fn serialize<'b>(
&self,
_typ: &$crate::frame::response::result::ColumnType,
writer: $crate::types::serialize::writers::CellWriter<'b>,
) -> ::std::result::Result<
$crate::types::serialize::writers::WrittenCellProof<'b>,
$crate::types::serialize::SerializationError,
> {
$crate::types::serialize::value::serialize_legacy_value(self, writer)
}
}
};
}
#[deprecated(
since = "0.15.1",
note = "Legacy serialization API is not type-safe and is going to be removed soon"
)]
pub struct ValueAdapter<T>(pub T);
#[allow(deprecated)]
impl<T> SerializeValue for ValueAdapter<T>
where
T: Value,
{
#[inline]
fn serialize<'b>(
&self,
_typ: &ColumnType,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
serialize_legacy_value(&self.0, writer)
}
}
#[deprecated(
since = "0.15.1",
note = "Legacy serialization API is not type-safe and is going to be removed soon"
)]
#[allow(deprecated)]
pub fn serialize_legacy_value<'b, T: Value>(
v: &T,
writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
let mut buf = Vec::new();
<T as Value>::serialize(v, &mut buf)
.map_err(|_| SerializationError::new(ValueToSerializeValueAdapterError::TooBig))?;
if buf.len() < 4 {
return Err(SerializationError(Arc::new(
ValueToSerializeValueAdapterError::TooShort { size: buf.len() },
)));
}
let (len_bytes, contents) = buf.split_at(4);
let len = i32::from_be_bytes(len_bytes.try_into().unwrap());
match len {
-2 => Ok(writer.set_unset()),
-1 => Ok(writer.set_null()),
len if len >= 0 => {
if contents.len() != len as usize {
Err(SerializationError(Arc::new(
ValueToSerializeValueAdapterError::DeclaredVsActualSizeMismatch {
declared: len as usize,
actual: contents.len(),
},
)))
} else {
Ok(writer.set_value(contents).unwrap()) }
}
_ => Err(SerializationError(Arc::new(
ValueToSerializeValueAdapterError::InvalidDeclaredSize { size: len },
))),
}
}
#[derive(Debug, Error, Clone)]
#[error("Failed to type check Rust type {rust_name} against CQL type {got:?}: {kind}")]
pub struct BuiltinTypeCheckError {
pub rust_name: &'static str,
pub got: ColumnType<'static>,
pub kind: BuiltinTypeCheckErrorKind,
}
fn mk_typck_err<T>(
got: &ColumnType,
kind: impl Into<BuiltinTypeCheckErrorKind>,
) -> SerializationError {
mk_typck_err_named(std::any::type_name::<T>(), got, kind)
}
fn mk_typck_err_named(
name: &'static str,
got: &ColumnType,
kind: impl Into<BuiltinTypeCheckErrorKind>,
) -> SerializationError {
SerializationError::new(BuiltinTypeCheckError {
rust_name: name,
got: got.clone().into_owned(),
kind: kind.into(),
})
}
#[derive(Debug, Error, Clone)]
#[error("Failed to serialize Rust type {rust_name} into CQL type {got:?}: {kind}")]
pub struct BuiltinSerializationError {
pub rust_name: &'static str,
pub got: ColumnType<'static>,
pub kind: BuiltinSerializationErrorKind,
}
fn mk_ser_err<T>(
got: &ColumnType,
kind: impl Into<BuiltinSerializationErrorKind>,
) -> SerializationError {
mk_ser_err_named(std::any::type_name::<T>(), got, kind)
}
fn mk_ser_err_named(
name: &'static str,
got: &ColumnType,
kind: impl Into<BuiltinSerializationErrorKind>,
) -> SerializationError {
SerializationError::new(BuiltinSerializationError {
rust_name: name,
got: got.clone().into_owned(),
kind: kind.into(),
})
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum BuiltinTypeCheckErrorKind {
MismatchedType {
expected: &'static [ColumnType<'static>],
},
NotEmptyable,
SetOrListError(SetOrListTypeCheckErrorKind),
MapError(MapTypeCheckErrorKind),
TupleError(TupleTypeCheckErrorKind),
UdtError(UdtTypeCheckErrorKind),
CustomTypeUnsupported,
}
impl From<SetOrListTypeCheckErrorKind> for BuiltinTypeCheckErrorKind {
fn from(value: SetOrListTypeCheckErrorKind) -> Self {
BuiltinTypeCheckErrorKind::SetOrListError(value)
}
}
impl From<MapTypeCheckErrorKind> for BuiltinTypeCheckErrorKind {
fn from(value: MapTypeCheckErrorKind) -> Self {
BuiltinTypeCheckErrorKind::MapError(value)
}
}
impl From<TupleTypeCheckErrorKind> for BuiltinTypeCheckErrorKind {
fn from(value: TupleTypeCheckErrorKind) -> Self {
BuiltinTypeCheckErrorKind::TupleError(value)
}
}
impl From<UdtTypeCheckErrorKind> for BuiltinTypeCheckErrorKind {
fn from(value: UdtTypeCheckErrorKind) -> Self {
BuiltinTypeCheckErrorKind::UdtError(value)
}
}
impl Display for BuiltinTypeCheckErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BuiltinTypeCheckErrorKind::MismatchedType { expected } => {
write!(f, "expected one of the CQL types: {expected:?}")
}
BuiltinTypeCheckErrorKind::NotEmptyable => {
f.write_str("the separate empty representation is not valid for this type")
}
BuiltinTypeCheckErrorKind::SetOrListError(err) => err.fmt(f),
BuiltinTypeCheckErrorKind::MapError(err) => err.fmt(f),
BuiltinTypeCheckErrorKind::TupleError(err) => err.fmt(f),
BuiltinTypeCheckErrorKind::UdtError(err) => err.fmt(f),
BuiltinTypeCheckErrorKind::CustomTypeUnsupported => {
f.write_str("custom CQL types are unsupported")
}
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum BuiltinSerializationErrorKind {
SizeOverflow,
ValueOverflow,
SetOrListError(SetOrListSerializationErrorKind),
MapError(MapSerializationErrorKind),
TupleError(TupleSerializationErrorKind),
UdtError(UdtSerializationErrorKind),
}
impl From<SetOrListSerializationErrorKind> for BuiltinSerializationErrorKind {
fn from(value: SetOrListSerializationErrorKind) -> Self {
BuiltinSerializationErrorKind::SetOrListError(value)
}
}
impl From<MapSerializationErrorKind> for BuiltinSerializationErrorKind {
fn from(value: MapSerializationErrorKind) -> Self {
BuiltinSerializationErrorKind::MapError(value)
}
}
impl From<TupleSerializationErrorKind> for BuiltinSerializationErrorKind {
fn from(value: TupleSerializationErrorKind) -> Self {
BuiltinSerializationErrorKind::TupleError(value)
}
}
impl From<UdtSerializationErrorKind> for BuiltinSerializationErrorKind {
fn from(value: UdtSerializationErrorKind) -> Self {
BuiltinSerializationErrorKind::UdtError(value)
}
}
impl Display for BuiltinSerializationErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BuiltinSerializationErrorKind::SizeOverflow => {
f.write_str("the Rust value is too big to be serialized in the CQL protocol format")
}
BuiltinSerializationErrorKind::ValueOverflow => {
f.write_str("the Rust value is out of range supported by the CQL type")
}
BuiltinSerializationErrorKind::SetOrListError(err) => err.fmt(f),
BuiltinSerializationErrorKind::MapError(err) => err.fmt(f),
BuiltinSerializationErrorKind::TupleError(err) => err.fmt(f),
BuiltinSerializationErrorKind::UdtError(err) => err.fmt(f),
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum MapTypeCheckErrorKind {
NotMap,
}
impl Display for MapTypeCheckErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MapTypeCheckErrorKind::NotMap => f.write_str(
"the CQL type the Rust type was attempted to be type checked against was not a map",
),
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum MapSerializationErrorKind {
TooManyElements,
KeySerializationFailed(SerializationError),
ValueSerializationFailed(SerializationError),
}
impl Display for MapSerializationErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MapSerializationErrorKind::TooManyElements => {
f.write_str("the map contains too many elements to fit in CQL representation")
}
MapSerializationErrorKind::KeySerializationFailed(err) => {
write!(f, "failed to serialize one of the keys: {}", err)
}
MapSerializationErrorKind::ValueSerializationFailed(err) => {
write!(f, "failed to serialize one of the values: {}", err)
}
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum SetOrListTypeCheckErrorKind {
NotSetOrList,
}
impl Display for SetOrListTypeCheckErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SetOrListTypeCheckErrorKind::NotSetOrList => {
f.write_str("the CQL type the Rust type was attempted to be type checked against was neither a set or a list")
}
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum SetOrListSerializationErrorKind {
TooManyElements,
ElementSerializationFailed(SerializationError),
}
impl Display for SetOrListSerializationErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SetOrListSerializationErrorKind::TooManyElements => f.write_str(
"the collection contains too many elements to fit in CQL representation",
),
SetOrListSerializationErrorKind::ElementSerializationFailed(err) => {
write!(f, "failed to serialize one of the elements: {err}")
}
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum TupleTypeCheckErrorKind {
NotTuple,
WrongElementCount {
rust_type_el_count: usize,
cql_type_el_count: usize,
},
}
impl Display for TupleTypeCheckErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TupleTypeCheckErrorKind::NotTuple => f.write_str(
"the CQL type the Rust type was attempted to be type checked against is not a tuple"
),
TupleTypeCheckErrorKind::WrongElementCount { rust_type_el_count, cql_type_el_count } => write!(
f,
"wrong tuple element count: CQL type has {cql_type_el_count}, the Rust tuple has {rust_type_el_count}"
),
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum TupleSerializationErrorKind {
ElementSerializationFailed {
index: usize,
err: SerializationError,
},
}
impl Display for TupleSerializationErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TupleSerializationErrorKind::ElementSerializationFailed { index, err } => {
write!(f, "element no. {index} failed to serialize: {err}")
}
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum UdtTypeCheckErrorKind {
NotUdt,
NameMismatch {
keyspace: String,
type_name: String,
},
ValueMissingForUdtField {
field_name: String,
},
NoSuchFieldInUdt {
field_name: String,
},
FieldNameMismatch {
rust_field_name: String,
db_field_name: String,
},
}
impl Display for UdtTypeCheckErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
UdtTypeCheckErrorKind::NotUdt => f.write_str("the CQL type the Rust type was attempted to be type checked against is not a UDT"),
UdtTypeCheckErrorKind::NameMismatch {
keyspace,
type_name,
} => write!(
f,
"the Rust UDT name does not match the actual CQL UDT name ({keyspace}.{type_name})"
),
UdtTypeCheckErrorKind::ValueMissingForUdtField { field_name } => {
write!(f, "the field {field_name} is missing in the Rust data but is required by the CQL UDT type")
}
UdtTypeCheckErrorKind::NoSuchFieldInUdt { field_name } => write!(
f,
"the field {field_name} that is present in the Rust data is not present in the CQL type"
),
UdtTypeCheckErrorKind::FieldNameMismatch { rust_field_name, db_field_name } => write!(
f,
"expected field with name {db_field_name} at given position, but the Rust field name is {rust_field_name}"
),
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum UdtSerializationErrorKind {
FieldSerializationFailed {
field_name: String,
err: SerializationError,
},
}
impl Display for UdtSerializationErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
UdtSerializationErrorKind::FieldSerializationFailed { field_name, err } => {
write!(f, "field {field_name} failed to serialize: {err}")
}
}
}
}
#[deprecated(
since = "0.15.1",
note = "Legacy serialization API is not type-safe and is going to be removed soon"
)]
#[allow(deprecated)]
#[derive(Error, Debug)]
pub enum ValueToSerializeValueAdapterError {
#[error("The value is too big to be serialized as it exceeds the maximum 2GB size limit")]
TooBig,
#[error("Output produced by the Value trait is too short to be considered a value: {size} < 4 minimum bytes")]
TooShort {
size: usize,
},
#[error("Mismatch between the declared value size vs. actual size: {declared} != {actual}")]
DeclaredVsActualSizeMismatch {
declared: usize,
actual: usize,
},
#[error("Invalid declared value size: {size}")]
InvalidDeclaredSize {
size: i32,
},
}
mod doctests {
fn _test_udt_bad_attributes_skip_name_check_requires_enforce_order() {}
fn _test_udt_bad_attributes_skip_name_check_conflicts_with_rename() {}
fn _test_udt_bad_attributes_rename_collision_with_field() {}
fn _test_udt_bad_attributes_rename_collision_with_another_rename() {}
fn _test_udt_bad_attributes_name_skip_name_checks_limitations_on_allow_missing() {}
fn _test_udt_good_attributes_name_skip_name_checks_limitations_on_allow_missing() {}
fn _test_udt_unordered_flavour_no_limitations_on_allow_missing() {}
fn _test_udt_default_when_null_is_accepted() {}
}
#[cfg(test)]
pub(crate) mod tests {
use std::collections::BTreeMap;
use crate::frame::response::result::{ColumnType, CqlValue};
#[allow(deprecated)]
use crate::frame::value::{Counter, MaybeUnset, Unset, Value, ValueTooBig};
#[allow(deprecated)]
use crate::types::serialize::value::{
BuiltinSerializationError, BuiltinSerializationErrorKind, BuiltinTypeCheckError,
BuiltinTypeCheckErrorKind, MapSerializationErrorKind, MapTypeCheckErrorKind,
SetOrListSerializationErrorKind, SetOrListTypeCheckErrorKind, TupleSerializationErrorKind,
TupleTypeCheckErrorKind, ValueAdapter,
};
use crate::types::serialize::{CellWriter, SerializationError};
use assert_matches::assert_matches;
use scylla_macros::SerializeValue;
use super::{SerializeValue, UdtSerializationErrorKind, UdtTypeCheckErrorKind};
#[allow(deprecated)]
fn check_compat<V: Value + SerializeValue>(v: V) {
let mut legacy_data = Vec::new();
<V as Value>::serialize(&v, &mut legacy_data).unwrap();
let mut new_data = Vec::new();
let new_data_writer = CellWriter::new(&mut new_data);
<V as SerializeValue>::serialize(&v, &ColumnType::Int, new_data_writer).unwrap();
assert_eq!(legacy_data, new_data);
}
#[test]
fn test_legacy_fallback() {
check_compat(123i32);
check_compat(None::<i32>);
check_compat(MaybeUnset::Unset::<i32>);
}
#[test]
fn test_dyn_serialize_value() {
let v: i32 = 123;
let mut typed_data = Vec::new();
let typed_data_writer = CellWriter::new(&mut typed_data);
<_ as SerializeValue>::serialize(&v, &ColumnType::Int, typed_data_writer).unwrap();
let v = &v as &dyn SerializeValue;
let mut erased_data = Vec::new();
let erased_data_writer = CellWriter::new(&mut erased_data);
<_ as SerializeValue>::serialize(&v, &ColumnType::Int, erased_data_writer).unwrap();
assert_eq!(typed_data, erased_data);
}
fn do_serialize_result<T: SerializeValue>(
t: T,
typ: &ColumnType,
) -> Result<Vec<u8>, SerializationError> {
let mut ret = Vec::new();
let writer = CellWriter::new(&mut ret);
t.serialize(typ, writer).map(|_| ()).map(|()| ret)
}
pub(crate) fn do_serialize<T: SerializeValue>(t: T, typ: &ColumnType) -> Vec<u8> {
do_serialize_result(t, typ).unwrap()
}
fn do_serialize_err<T: SerializeValue>(t: T, typ: &ColumnType) -> SerializationError {
do_serialize_result(t, typ).unwrap_err()
}
#[allow(deprecated)]
#[test]
fn test_legacy_wrapper() {
struct Foo;
impl Value for Foo {
fn serialize(&self, buf: &mut Vec<u8>) -> Result<(), ValueTooBig> {
let s = "Ala ma kota";
buf.extend_from_slice(&(s.len() as i32).to_be_bytes());
buf.extend_from_slice(s.as_bytes());
Ok(())
}
}
let buf = do_serialize(ValueAdapter(Foo), &ColumnType::Text);
let expected = vec![
0, 0, 0, 11, 65, 108, 97, 32, 109, 97, 32, 107, 111, 116, 97, ];
assert_eq!(buf, expected);
}
fn get_typeck_err(err: &SerializationError) -> &BuiltinTypeCheckError {
match err.0.downcast_ref() {
Some(err) => err,
None => panic!("not a BuiltinTypeCheckError: {}", err),
}
}
fn get_ser_err(err: &SerializationError) -> &BuiltinSerializationError {
match err.0.downcast_ref() {
Some(err) => err,
None => panic!("not a BuiltinSerializationError: {}", err),
}
}
#[test]
fn test_native_errors() {
let v = 123_i32;
let err = do_serialize_err(v, &ColumnType::Double);
let err = get_typeck_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<i32>());
assert_eq!(err.got, ColumnType::Double);
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::MismatchedType {
expected: &[ColumnType::Int],
}
);
let v = "Ala ma kota";
let err = do_serialize_err(v, &ColumnType::Double);
let err = get_typeck_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<&str>());
assert_eq!(err.got, ColumnType::Double);
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::MismatchedType {
expected: &[ColumnType::Ascii, ColumnType::Text],
}
);
}
#[cfg(feature = "bigdecimal-04")]
#[test]
fn test_native_errors_bigdecimal_04() {
use bigdecimal_04::num_bigint::BigInt;
use bigdecimal_04::BigDecimal;
let v = BigDecimal::new(BigInt::from(123), 1i64 << 40);
let err = do_serialize_err(v, &ColumnType::Decimal);
let err = get_ser_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<BigDecimal>());
assert_eq!(err.got, ColumnType::Decimal);
assert_matches!(err.kind, BuiltinSerializationErrorKind::ValueOverflow);
}
#[test]
fn test_set_or_list_errors() {
let v = vec![123_i32];
let err = do_serialize_err(v, &ColumnType::Double);
let err = get_typeck_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<Vec<i32>>());
assert_eq!(err.got, ColumnType::Double);
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::SetOrListError(SetOrListTypeCheckErrorKind::NotSetOrList)
);
let v = &[Unset; 1 << 33] as &[Unset];
let typ = ColumnType::List(Box::new(ColumnType::Int));
let err = do_serialize_err(v, &typ);
let err = get_ser_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<&[Unset]>());
assert_eq!(err.got, typ);
assert_matches!(
err.kind,
BuiltinSerializationErrorKind::SetOrListError(
SetOrListSerializationErrorKind::TooManyElements
)
);
let v = vec![123_i32];
let typ = ColumnType::List(Box::new(ColumnType::Double));
let err = do_serialize_err(v, &typ);
let err = get_ser_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<Vec<i32>>());
assert_eq!(err.got, typ);
let BuiltinSerializationErrorKind::SetOrListError(
SetOrListSerializationErrorKind::ElementSerializationFailed(err),
) = &err.kind
else {
panic!("unexpected error kind: {}", err.kind)
};
let err = get_typeck_err(err);
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::MismatchedType {
expected: &[ColumnType::Int],
}
);
}
#[test]
fn test_map_errors() {
let v = BTreeMap::from([("foo", "bar")]);
let err = do_serialize_err(v, &ColumnType::Double);
let err = get_typeck_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<BTreeMap<&str, &str>>());
assert_eq!(err.got, ColumnType::Double);
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::MapError(MapTypeCheckErrorKind::NotMap)
);
let v = BTreeMap::from([(123_i32, 456_i32)]);
let typ = ColumnType::Map(Box::new(ColumnType::Double), Box::new(ColumnType::Int));
let err = do_serialize_err(v, &typ);
let err = get_ser_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<BTreeMap<i32, i32>>());
assert_eq!(err.got, typ);
let BuiltinSerializationErrorKind::MapError(
MapSerializationErrorKind::KeySerializationFailed(err),
) = &err.kind
else {
panic!("unexpected error kind: {}", err.kind)
};
let err = get_typeck_err(err);
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::MismatchedType {
expected: &[ColumnType::Int],
}
);
let v = BTreeMap::from([(123_i32, 456_i32)]);
let typ = ColumnType::Map(Box::new(ColumnType::Int), Box::new(ColumnType::Double));
let err = do_serialize_err(v, &typ);
let err = get_ser_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<BTreeMap<i32, i32>>());
assert_eq!(err.got, typ);
let BuiltinSerializationErrorKind::MapError(
MapSerializationErrorKind::ValueSerializationFailed(err),
) = &err.kind
else {
panic!("unexpected error kind: {}", err.kind)
};
let err = get_typeck_err(err);
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::MismatchedType {
expected: &[ColumnType::Int],
}
);
}
#[test]
fn test_tuple_errors() {
let v = (123_i32, 456_i32, 789_i32);
let err = do_serialize_err(v, &ColumnType::Double);
let err = get_typeck_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<(i32, i32, i32)>());
assert_eq!(err.got, ColumnType::Double);
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::TupleError(TupleTypeCheckErrorKind::NotTuple)
);
let v = (123_i32, 456_i32, 789_i32);
let typ = ColumnType::Tuple(vec![ColumnType::Int; 2]);
let err = do_serialize_err(v, &typ);
let err = get_typeck_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<(i32, i32, i32)>());
assert_eq!(err.got, typ);
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::TupleError(TupleTypeCheckErrorKind::WrongElementCount {
rust_type_el_count: 3,
cql_type_el_count: 2,
})
);
let v = (123_i32, "Ala ma kota", 789.0_f64);
let typ = ColumnType::Tuple(vec![ColumnType::Int, ColumnType::Text, ColumnType::Uuid]);
let err = do_serialize_err(v, &typ);
let err = get_ser_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<(i32, &str, f64)>());
assert_eq!(err.got, typ);
let BuiltinSerializationErrorKind::TupleError(
TupleSerializationErrorKind::ElementSerializationFailed { index: 2, err },
) = &err.kind
else {
panic!("unexpected error kind: {}", err.kind)
};
let err = get_typeck_err(err);
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::MismatchedType {
expected: &[ColumnType::Double],
}
);
}
#[test]
fn test_cql_value_errors() {
let v = CqlValue::Empty;
let err = do_serialize_err(v, &ColumnType::Counter);
let err = get_typeck_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<CqlValue>());
assert_eq!(err.got, ColumnType::Counter);
assert_matches!(err.kind, BuiltinTypeCheckErrorKind::NotEmptyable);
}
#[test]
fn test_cql_value_tuple_errors() {
let v = CqlValue::Tuple(vec![
Some(CqlValue::Int(123_i32)),
Some(CqlValue::Int(456_i32)),
Some(CqlValue::Int(789_i32)),
]);
let err = do_serialize_err(v, &ColumnType::Double);
let err = get_typeck_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<CqlValue>());
assert_eq!(err.got, ColumnType::Double);
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::TupleError(TupleTypeCheckErrorKind::NotTuple)
);
let v = CqlValue::Tuple(vec![
Some(CqlValue::Int(123_i32)),
Some(CqlValue::Int(456_i32)),
Some(CqlValue::Int(789_i32)),
]);
let typ = ColumnType::Tuple(vec![ColumnType::Int; 2]);
let err = do_serialize_err(v, &typ);
let err = get_typeck_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<CqlValue>());
assert_eq!(err.got, typ);
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::TupleError(TupleTypeCheckErrorKind::WrongElementCount {
rust_type_el_count: 3,
cql_type_el_count: 2,
})
);
let v = CqlValue::Tuple(vec![
Some(CqlValue::Int(123_i32)),
Some(CqlValue::Text("Ala ma kota".to_string())),
Some(CqlValue::Double(789_f64)),
]);
let typ = ColumnType::Tuple(vec![ColumnType::Int, ColumnType::Text, ColumnType::Uuid]);
let err = do_serialize_err(v, &typ);
let err = get_ser_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<CqlValue>());
assert_eq!(err.got, typ);
let BuiltinSerializationErrorKind::TupleError(
TupleSerializationErrorKind::ElementSerializationFailed { index: 2, err },
) = &err.kind
else {
panic!("unexpected error kind: {}", err.kind)
};
let err = get_typeck_err(err);
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::MismatchedType {
expected: &[ColumnType::Double],
}
);
}
#[test]
fn test_cql_value_udt_errors() {
let v = CqlValue::UserDefinedType {
keyspace: "ks".to_string(),
type_name: "udt".to_string(),
fields: vec![
("a".to_string(), Some(CqlValue::Int(123_i32))),
("b".to_string(), Some(CqlValue::Int(456_i32))),
("c".to_string(), Some(CqlValue::Int(789_i32))),
],
};
let err = do_serialize_err(v, &ColumnType::Double);
let err = get_typeck_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<CqlValue>());
assert_eq!(err.got, ColumnType::Double);
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::UdtError(UdtTypeCheckErrorKind::NotUdt)
);
let v = CqlValue::UserDefinedType {
keyspace: "ks".to_string(),
type_name: "udt".to_string(),
fields: vec![
("a".to_string(), Some(CqlValue::Int(123_i32))),
("b".to_string(), Some(CqlValue::Int(456_i32))),
("c".to_string(), Some(CqlValue::Int(789_i32))),
],
};
let typ = ColumnType::UserDefinedType {
type_name: "udt2".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Int),
("b".into(), ColumnType::Int),
("c".into(), ColumnType::Int),
],
};
let err = do_serialize_err(v, &typ);
let err = get_typeck_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<CqlValue>());
assert_eq!(err.got, typ);
let BuiltinTypeCheckErrorKind::UdtError(UdtTypeCheckErrorKind::NameMismatch {
keyspace,
type_name,
}) = &err.kind
else {
panic!("unexpected error kind: {}", err.kind)
};
assert_eq!(keyspace, "ks");
assert_eq!(type_name, "udt2");
let v = CqlValue::UserDefinedType {
keyspace: "ks".to_string(),
type_name: "udt".to_string(),
fields: vec![
("a".to_string(), Some(CqlValue::Int(123_i32))),
("b".to_string(), Some(CqlValue::Int(456_i32))),
("c".to_string(), Some(CqlValue::Int(789_i32))),
],
};
let typ = ColumnType::UserDefinedType {
type_name: "udt".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Int),
("b".into(), ColumnType::Int),
],
};
let err = do_serialize_err(v, &typ);
let err = get_typeck_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<CqlValue>());
assert_eq!(err.got, typ);
let BuiltinTypeCheckErrorKind::UdtError(UdtTypeCheckErrorKind::NoSuchFieldInUdt {
field_name,
}) = &err.kind
else {
panic!("unexpected error kind: {}", err.kind)
};
assert_eq!(field_name, "c");
let v = CqlValue::UserDefinedType {
keyspace: "ks".to_string(),
type_name: "udt".to_string(),
fields: vec![
("a".to_string(), Some(CqlValue::Int(123_i32))),
("b".to_string(), Some(CqlValue::Int(456_i32))),
("c".to_string(), Some(CqlValue::Int(789_i32))),
],
};
let typ = ColumnType::UserDefinedType {
type_name: "udt".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Int),
("b".into(), ColumnType::Int),
("c".into(), ColumnType::Double),
],
};
let err = do_serialize_err(v, &typ);
let err = get_ser_err(&err);
assert_eq!(err.rust_name, std::any::type_name::<CqlValue>());
assert_eq!(err.got, typ);
let BuiltinSerializationErrorKind::UdtError(
UdtSerializationErrorKind::FieldSerializationFailed { field_name, err },
) = &err.kind
else {
panic!("unexpected error kind: {}", err.kind)
};
assert_eq!(field_name, "c");
let err = get_typeck_err(err);
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::MismatchedType {
expected: &[ColumnType::Int],
}
);
}
#[allow(unused)]
#[derive(SerializeValue)]
#[scylla(crate = crate)]
struct TestUdtWithNoFields {}
#[derive(SerializeValue, Debug, PartialEq, Eq, Default)]
#[scylla(crate = crate)]
struct TestUdtWithFieldSorting {
a: String,
b: i32,
c: Vec<i64>,
}
#[test]
fn test_udt_serialization_with_field_sorting_correct_order() {
let typ = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Text),
("b".into(), ColumnType::Int),
("c".into(), ColumnType::List(Box::new(ColumnType::BigInt))),
],
};
let reference = do_serialize(
CqlValue::UserDefinedType {
keyspace: "ks".to_string(),
type_name: "typ".to_string(),
fields: vec![
(
"a".to_string(),
Some(CqlValue::Text(String::from("Ala ma kota"))),
),
("b".to_string(), Some(CqlValue::Int(42))),
(
"c".to_string(),
Some(CqlValue::List(vec![
CqlValue::BigInt(1),
CqlValue::BigInt(2),
CqlValue::BigInt(3),
])),
),
],
},
&typ,
);
let udt = do_serialize(
TestUdtWithFieldSorting {
a: "Ala ma kota".to_owned(),
b: 42,
c: vec![1, 2, 3],
},
&typ,
);
assert_eq!(reference, udt);
}
#[test]
fn test_udt_serialization_with_field_sorting_incorrect_order() {
let typ = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("b".into(), ColumnType::Int),
("a".into(), ColumnType::Text),
("c".into(), ColumnType::List(Box::new(ColumnType::BigInt))),
],
};
let reference = do_serialize(
CqlValue::UserDefinedType {
keyspace: "ks".to_string(),
type_name: "typ".to_string(),
fields: vec![
("b".to_string(), Some(CqlValue::Int(42))),
(
"a".to_string(),
Some(CqlValue::Text(String::from("Ala ma kota"))),
),
(
"c".to_string(),
Some(CqlValue::List(vec![
CqlValue::BigInt(1),
CqlValue::BigInt(2),
CqlValue::BigInt(3),
])),
),
],
},
&typ,
);
let udt = do_serialize(
TestUdtWithFieldSorting {
a: "Ala ma kota".to_owned(),
b: 42,
c: vec![1, 2, 3],
},
&typ,
);
assert_eq!(reference, udt);
}
#[test]
fn test_udt_serialization_with_missing_rust_fields_at_end() {
let udt = TestUdtWithFieldSorting::default();
let typ_normal = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Text),
("b".into(), ColumnType::Int),
("c".into(), ColumnType::List(Box::new(ColumnType::BigInt))),
],
};
let typ_unexpected_field = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Text),
("b".into(), ColumnType::Int),
("c".into(), ColumnType::List(Box::new(ColumnType::BigInt))),
("d".into(), ColumnType::Counter),
("e".into(), ColumnType::Counter),
],
};
let result_normal = do_serialize(&udt, &typ_normal);
let result_additional_field = do_serialize(&udt, &typ_unexpected_field);
assert_eq!(result_normal, result_additional_field);
}
#[derive(SerializeValue, Debug, PartialEq, Default)]
#[scylla(crate = crate)]
struct TestUdtWithFieldSorting2 {
a: String,
b: i32,
d: Option<Counter>,
c: Vec<i64>,
}
#[derive(SerializeValue, Debug, PartialEq, Default)]
#[scylla(crate = crate)]
struct TestUdtWithFieldSorting3 {
a: String,
b: i32,
d: Option<Counter>,
e: Option<f32>,
c: Vec<i64>,
}
#[test]
fn test_udt_serialization_with_missing_rust_field_in_middle() {
let udt = TestUdtWithFieldSorting::default();
let udt2 = TestUdtWithFieldSorting2::default();
let udt3 = TestUdtWithFieldSorting3::default();
let typ = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Text),
("b".into(), ColumnType::Int),
("d".into(), ColumnType::Counter),
("e".into(), ColumnType::Float),
("c".into(), ColumnType::List(Box::new(ColumnType::BigInt))),
],
};
let result_1 = do_serialize(udt, &typ);
let result_2 = do_serialize(udt2, &typ);
let result_3 = do_serialize(udt3, &typ);
assert_eq!(result_1, result_2);
assert_eq!(result_2, result_3);
}
#[test]
fn test_udt_serialization_failing_type_check() {
let typ_not_udt = ColumnType::Ascii;
let udt = TestUdtWithFieldSorting::default();
let mut data = Vec::new();
let err = udt
.serialize(&typ_not_udt, CellWriter::new(&mut data))
.unwrap_err();
let err = err.0.downcast_ref::<BuiltinTypeCheckError>().unwrap();
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::UdtError(UdtTypeCheckErrorKind::NotUdt)
);
let typ_without_c = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Text),
("b".into(), ColumnType::Int),
],
};
let err = udt
.serialize(&typ_without_c, CellWriter::new(&mut data))
.unwrap_err();
let err = err.0.downcast_ref::<BuiltinTypeCheckError>().unwrap();
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::UdtError(
UdtTypeCheckErrorKind::ValueMissingForUdtField { .. }
)
);
let typ_wrong_type = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Text),
("b".into(), ColumnType::Int),
("c".into(), ColumnType::TinyInt), ],
};
let err = udt
.serialize(&typ_wrong_type, CellWriter::new(&mut data))
.unwrap_err();
let err = err.0.downcast_ref::<BuiltinSerializationError>().unwrap();
assert_matches!(
err.kind,
BuiltinSerializationErrorKind::UdtError(
UdtSerializationErrorKind::FieldSerializationFailed { .. }
)
);
}
#[derive(SerializeValue)]
#[scylla(crate = crate)]
struct TestUdtWithGenerics<'a, T: SerializeValue> {
a: &'a str,
b: T,
}
#[test]
fn test_udt_serialization_with_generics() {
fn check_with_type<T: SerializeValue>(typ: ColumnType, t: T, cql_t: CqlValue) {
let typ = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![("a".into(), ColumnType::Text), ("b".into(), typ)],
};
let reference = do_serialize(
CqlValue::UserDefinedType {
keyspace: "ks".to_string(),
type_name: "typ".to_string(),
fields: vec![
(
"a".to_string(),
Some(CqlValue::Text(String::from("Ala ma kota"))),
),
("b".to_string(), Some(cql_t)),
],
},
&typ,
);
let udt = do_serialize(
TestUdtWithGenerics {
a: "Ala ma kota",
b: t,
},
&typ,
);
assert_eq!(reference, udt);
}
check_with_type(ColumnType::Int, 123_i32, CqlValue::Int(123_i32));
check_with_type(ColumnType::Double, 123_f64, CqlValue::Double(123_f64));
}
#[derive(SerializeValue, Debug, PartialEq, Eq, Default)]
#[scylla(crate = crate, flavor = "enforce_order")]
struct TestUdtWithEnforcedOrder {
a: String,
b: i32,
c: Vec<i64>,
}
#[test]
fn test_udt_serialization_with_enforced_order_correct_order() {
let typ = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Text),
("b".into(), ColumnType::Int),
("c".into(), ColumnType::List(Box::new(ColumnType::BigInt))),
],
};
let reference = do_serialize(
CqlValue::UserDefinedType {
keyspace: "ks".to_string(),
type_name: "typ".to_string(),
fields: vec![
(
"a".to_string(),
Some(CqlValue::Text(String::from("Ala ma kota"))),
),
("b".to_string(), Some(CqlValue::Int(42))),
(
"c".to_string(),
Some(CqlValue::List(vec![
CqlValue::BigInt(1),
CqlValue::BigInt(2),
CqlValue::BigInt(3),
])),
),
],
},
&typ,
);
let udt = do_serialize(
TestUdtWithEnforcedOrder {
a: "Ala ma kota".to_owned(),
b: 42,
c: vec![1, 2, 3],
},
&typ,
);
assert_eq!(reference, udt);
}
#[test]
fn test_udt_serialization_with_enforced_order_additional_field() {
let udt = TestUdtWithEnforcedOrder::default();
let typ_normal = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Text),
("b".into(), ColumnType::Int),
("c".into(), ColumnType::List(Box::new(ColumnType::BigInt))),
],
};
let typ_unexpected_field = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Text),
("b".into(), ColumnType::Int),
("c".into(), ColumnType::List(Box::new(ColumnType::BigInt))),
("d".into(), ColumnType::Counter),
],
};
let result_normal = do_serialize(&udt, &typ_normal);
let result_additional_field = do_serialize(&udt, &typ_unexpected_field);
assert_eq!(result_normal, result_additional_field);
}
#[test]
fn test_udt_serialization_with_enforced_order_failing_type_check() {
let typ_not_udt = ColumnType::Ascii;
let udt = TestUdtWithEnforcedOrder::default();
let mut data = Vec::new();
let err = <_ as SerializeValue>::serialize(&udt, &typ_not_udt, CellWriter::new(&mut data))
.unwrap_err();
let err = err.0.downcast_ref::<BuiltinTypeCheckError>().unwrap();
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::UdtError(UdtTypeCheckErrorKind::NotUdt)
);
let typ = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("b".into(), ColumnType::Int),
("a".into(), ColumnType::Text),
("c".into(), ColumnType::List(Box::new(ColumnType::BigInt))),
],
};
let err =
<_ as SerializeValue>::serialize(&udt, &typ, CellWriter::new(&mut data)).unwrap_err();
let err = err.0.downcast_ref::<BuiltinTypeCheckError>().unwrap();
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::UdtError(UdtTypeCheckErrorKind::FieldNameMismatch { .. })
);
let typ_without_c = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Text),
("b".into(), ColumnType::Int),
],
};
let err =
<_ as SerializeValue>::serialize(&udt, &typ_without_c, CellWriter::new(&mut data))
.unwrap_err();
let err = err.0.downcast_ref::<BuiltinTypeCheckError>().unwrap();
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::UdtError(
UdtTypeCheckErrorKind::ValueMissingForUdtField { .. }
)
);
let typ_unexpected_field = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Text),
("b".into(), ColumnType::Int),
("c".into(), ColumnType::TinyInt), ],
};
let err = <_ as SerializeValue>::serialize(
&udt,
&typ_unexpected_field,
CellWriter::new(&mut data),
)
.unwrap_err();
let err = err.0.downcast_ref::<BuiltinSerializationError>().unwrap();
assert_matches!(
err.kind,
BuiltinSerializationErrorKind::UdtError(
UdtSerializationErrorKind::FieldSerializationFailed { .. }
)
);
}
#[derive(SerializeValue, Debug)]
#[scylla(crate = crate)]
struct TestUdtWithFieldRename {
a: String,
#[scylla(rename = "x")]
b: i32,
}
#[derive(SerializeValue, Debug)]
#[scylla(crate = crate, flavor = "enforce_order")]
struct TestUdtWithFieldRenameAndEnforceOrder {
a: String,
#[scylla(rename = "x")]
b: i32,
}
#[test]
fn test_udt_serialization_with_field_rename() {
let typ = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("x".into(), ColumnType::Int),
("a".into(), ColumnType::Text),
],
};
let mut reference = Vec::new();
reference.extend_from_slice(&23i32.to_be_bytes());
reference.extend_from_slice(&4i32.to_be_bytes());
reference.extend_from_slice(&42i32.to_be_bytes());
reference.extend_from_slice(&("Ala ma kota".len() as i32).to_be_bytes());
reference.extend_from_slice("Ala ma kota".as_bytes());
let udt = do_serialize(
TestUdtWithFieldRename {
a: "Ala ma kota".to_owned(),
b: 42,
},
&typ,
);
assert_eq!(reference, udt);
}
#[test]
fn test_udt_serialization_with_field_rename_and_enforce_order() {
let typ = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Text),
("x".into(), ColumnType::Int),
],
};
let mut reference = Vec::new();
reference.extend_from_slice(&23i32.to_be_bytes());
reference.extend_from_slice(&("Ala ma kota".len() as i32).to_be_bytes());
reference.extend_from_slice("Ala ma kota".as_bytes());
reference.extend_from_slice(&4i32.to_be_bytes());
reference.extend_from_slice(&42i32.to_be_bytes());
let udt = do_serialize(
TestUdtWithFieldRenameAndEnforceOrder {
a: "Ala ma kota".to_owned(),
b: 42,
},
&typ,
);
assert_eq!(reference, udt);
}
#[allow(unused)]
#[derive(SerializeValue, Debug)]
#[scylla(crate = crate, flavor = "enforce_order", skip_name_checks)]
struct TestUdtWithSkippedNameChecks {
a: String,
b: i32,
}
#[test]
fn test_udt_serialization_with_skipped_name_checks() {
let typ = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Text),
("x".into(), ColumnType::Int),
],
};
let mut reference = Vec::new();
reference.extend_from_slice(&23i32.to_be_bytes());
reference.extend_from_slice(&("Ala ma kota".len() as i32).to_be_bytes());
reference.extend_from_slice("Ala ma kota".as_bytes());
reference.extend_from_slice(&4i32.to_be_bytes());
reference.extend_from_slice(&42i32.to_be_bytes());
let udt = do_serialize(
TestUdtWithFieldRenameAndEnforceOrder {
a: "Ala ma kota".to_owned(),
b: 42,
},
&typ,
);
assert_eq!(reference, udt);
}
#[derive(SerializeValue, Debug, PartialEq, Eq, Default)]
#[scylla(crate = crate, forbid_excess_udt_fields)]
struct TestStrictUdtWithFieldSorting {
a: String,
b: i32,
c: Vec<i64>,
}
#[test]
fn test_strict_udt_with_field_sorting_rejects_additional_field() {
let udt = TestStrictUdtWithFieldSorting::default();
let mut data = Vec::new();
let typ_unexpected_field = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Text),
("b".into(), ColumnType::Int),
("c".into(), ColumnType::List(Box::new(ColumnType::BigInt))),
("d".into(), ColumnType::Counter),
],
};
let err = udt
.serialize(&typ_unexpected_field, CellWriter::new(&mut data))
.unwrap_err();
let err = err.0.downcast_ref::<BuiltinTypeCheckError>().unwrap();
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::UdtError(UdtTypeCheckErrorKind::NoSuchFieldInUdt { .. })
);
let typ_unexpected_field_middle = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Text),
("b".into(), ColumnType::Int),
("b_c".into(), ColumnType::Counter),
("c".into(), ColumnType::List(Box::new(ColumnType::BigInt))),
],
};
let err = udt
.serialize(&typ_unexpected_field_middle, CellWriter::new(&mut data))
.unwrap_err();
let err = err.0.downcast_ref::<BuiltinTypeCheckError>().unwrap();
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::UdtError(UdtTypeCheckErrorKind::NoSuchFieldInUdt { .. })
);
}
#[derive(SerializeValue, Debug, PartialEq, Eq, Default)]
#[scylla(crate = crate, flavor = "enforce_order", forbid_excess_udt_fields)]
struct TestStrictUdtWithEnforcedOrder {
a: String,
b: i32,
c: Vec<i64>,
}
#[test]
fn test_strict_udt_with_enforced_order_rejects_additional_field() {
let udt = TestStrictUdtWithEnforcedOrder::default();
let mut data = Vec::new();
let typ_unexpected_field = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Text),
("b".into(), ColumnType::Int),
("c".into(), ColumnType::List(Box::new(ColumnType::BigInt))),
("d".into(), ColumnType::Counter),
],
};
let err = <_ as SerializeValue>::serialize(
&udt,
&typ_unexpected_field,
CellWriter::new(&mut data),
)
.unwrap_err();
let err = err.0.downcast_ref::<BuiltinTypeCheckError>().unwrap();
assert_matches!(
err.kind,
BuiltinTypeCheckErrorKind::UdtError(UdtTypeCheckErrorKind::NoSuchFieldInUdt { .. })
);
}
#[derive(SerializeValue, Debug)]
#[scylla(crate = crate, flavor = "enforce_order", skip_name_checks)]
struct TestUdtWithSkippedFields {
a: String,
b: i32,
#[scylla(skip)]
#[allow(dead_code)]
skipped: Vec<String>,
c: Vec<i64>,
}
#[test]
fn test_row_serialization_with_skipped_field() {
let typ = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![
("a".into(), ColumnType::Text),
("b".into(), ColumnType::Int),
("c".into(), ColumnType::List(Box::new(ColumnType::BigInt))),
],
};
let reference = do_serialize(
TestUdtWithFieldSorting {
a: "Ala ma kota".to_owned(),
b: 42,
c: vec![1, 2, 3],
},
&typ,
);
let row = do_serialize(
TestUdtWithSkippedFields {
a: "Ala ma kota".to_owned(),
b: 42,
skipped: vec!["abcd".to_owned(), "efgh".to_owned()],
c: vec![1, 2, 3],
},
&typ,
);
assert_eq!(reference, row);
}
#[test]
fn test_udt_with_non_rust_ident() {
#[derive(SerializeValue, Debug)]
#[scylla(crate = crate)]
struct UdtWithNonRustIdent {
#[scylla(rename = "a$a")]
a: i32,
}
let typ = ColumnType::UserDefinedType {
type_name: "typ".into(),
keyspace: "ks".into(),
field_types: vec![("a$a".into(), ColumnType::Int)],
};
let value = UdtWithNonRustIdent { a: 42 };
let mut reference = Vec::new();
reference.extend_from_slice(&8i32.to_be_bytes());
reference.extend_from_slice(&(std::mem::size_of_val(&value.a) as i32).to_be_bytes());
reference.extend_from_slice(&value.a.to_be_bytes());
let udt = do_serialize(value, &typ);
assert_eq!(reference, udt);
}
}