Skip to content

Commit 2bf4ca0

Browse files
wprzytulapiodul
andcommitted
value: impl DeserializeValue for tuples
Co-authored-by: Piotr Dulikowski <[email protected]>
1 parent 92a4324 commit 2bf4ca0

File tree

1 file changed

+241
-0
lines changed
  • scylla-cql/src/types/deserialize

1 file changed

+241
-0
lines changed

scylla-cql/src/types/deserialize/value.rs

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,115 @@ where
986986
}
987987
}
988988

989+
// tuples
990+
991+
// Implements tuple deserialization.
992+
// The generated impl expects that the serialized data contains exactly the given amount of values.
993+
macro_rules! impl_tuple {
994+
($($Ti:ident),*; $($idx:literal),*; $($idf:ident),*) => {
995+
impl<'frame, $($Ti),*> DeserializeValue<'frame> for ($($Ti,)*)
996+
where
997+
$($Ti: DeserializeValue<'frame>),*
998+
{
999+
fn type_check(typ: &ColumnType) -> Result<(), TypeCheckError> {
1000+
const TUPLE_LEN: usize = (&[$($idx),*] as &[i32]).len();
1001+
let [$($idf),*] = ensure_tuple_type::<($($Ti,)*), TUPLE_LEN>(typ)?;
1002+
$(
1003+
<$Ti>::type_check($idf).map_err(|err| mk_typck_err::<Self>(
1004+
typ,
1005+
TupleTypeCheckErrorKind::FieldTypeCheckFailed {
1006+
position: $idx,
1007+
err,
1008+
}
1009+
))?;
1010+
)*
1011+
Ok(())
1012+
}
1013+
1014+
fn deserialize(typ: &'frame ColumnType, v: Option<FrameSlice<'frame>>) -> Result<Self, DeserializationError> {
1015+
const TUPLE_LEN: usize = (&[$($idx),*] as &[i32]).len();
1016+
// Safety: we are allowed to assume that type_check() was already called.
1017+
let [$($idf),*] = ensure_tuple_type::<($($Ti,)*), TUPLE_LEN>(typ)
1018+
.expect("Type check should have prevented this!");
1019+
1020+
// Ignore the warning for the zero-sized tuple
1021+
#[allow(unused)]
1022+
let mut v = ensure_not_null_frame_slice::<Self>(typ, v)?;
1023+
let ret = (
1024+
$(
1025+
v.read_cql_bytes()
1026+
.map_err(|err| DeserializationError::new(err))
1027+
.and_then(|cql_bytes| <$Ti>::deserialize($idf, cql_bytes))
1028+
.map_err(|err| mk_deser_err::<Self>(
1029+
typ,
1030+
TupleDeserializationErrorKind::FieldDeserializationFailed {
1031+
position: $idx,
1032+
err,
1033+
}
1034+
)
1035+
)?,
1036+
)*
1037+
);
1038+
Ok(ret)
1039+
}
1040+
}
1041+
}
1042+
}
1043+
1044+
// Implements tuple deserialization for all tuple sizes up to predefined size.
1045+
// Accepts 3 lists, (see usage below the definition):
1046+
// - type parameters for the consecutive fields,
1047+
// - indices of the consecutive fields,
1048+
// - consecutive names for variables corresponding to each field.
1049+
//
1050+
// The idea is to recursively build prefixes of those lists (starting with an empty prefix)
1051+
// and for each prefix, implement deserialization for generic tuple represented by it.
1052+
// The < > brackets aid syntactically to separate the prefixes (positioned inside them)
1053+
// from the remaining suffixes (positioned beyond them).
1054+
macro_rules! impl_tuple_multiple {
1055+
// The entry point to the macro.
1056+
// Begins with implementing deserialization for (), then proceeds to the main recursive call.
1057+
($($Ti:ident),*; $($idx:literal),*; $($idf:ident),*) => {
1058+
impl_tuple!(;;);
1059+
impl_tuple_multiple!(
1060+
$($Ti),* ; < > ;
1061+
$($idx),*; < > ;
1062+
$($idf),*; < >
1063+
);
1064+
};
1065+
1066+
// The termination condition. No more fields given to extend the tuple with.
1067+
(;< $($Ti:ident,)* >;;< $($idx:literal,)* >;;< $($idf:ident,)* >) => {};
1068+
1069+
// The recursion. Upon each call, a new field is appended to the tuple
1070+
// and deserialization is implemented for it.
1071+
(
1072+
$T_head:ident $(,$T_suffix:ident)*; < $($T_prefix:ident,)* > ;
1073+
$idx_head:literal $(,$idx_suffix:literal)*; < $($idx_prefix:literal,)* >;
1074+
$idf_head:ident $(,$idf_suffix:ident)* ; <$($idf_prefix:ident,)*>
1075+
) => {
1076+
impl_tuple!(
1077+
$($T_prefix,)* $T_head;
1078+
$($idx_prefix, )* $idx_head;
1079+
$($idf_prefix, )* $idf_head
1080+
);
1081+
impl_tuple_multiple!(
1082+
$($T_suffix),* ; < $($T_prefix,)* $T_head, > ;
1083+
$($idx_suffix),*; < $($idx_prefix, )* $idx_head, > ;
1084+
$($idf_suffix),*; < $($idf_prefix, )* $idf_head, >
1085+
);
1086+
}
1087+
}
1088+
1089+
pub(super) use impl_tuple_multiple;
1090+
1091+
// Implements tuple deserialization for all tuple sizes up to 16.
1092+
impl_tuple_multiple!(
1093+
T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15;
1094+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15;
1095+
t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15
1096+
);
1097+
9891098
// Utilities
9901099

9911100
fn ensure_not_null_frame_slice<'frame, T>(
@@ -1024,6 +1133,24 @@ fn ensure_exact_length<'frame, T, const SIZE: usize>(
10241133
})
10251134
}
10261135

1136+
fn ensure_tuple_type<T, const SIZE: usize>(
1137+
typ: &ColumnType,
1138+
) -> Result<&[ColumnType; SIZE], TypeCheckError> {
1139+
if let ColumnType::Tuple(typs_v) = typ {
1140+
typs_v.as_slice().try_into().map_err(|_| {
1141+
BuiltinTypeCheckErrorKind::TupleError(TupleTypeCheckErrorKind::WrongElementCount {
1142+
rust_type_el_count: SIZE,
1143+
cql_type_el_count: typs_v.len(),
1144+
})
1145+
})
1146+
} else {
1147+
Err(BuiltinTypeCheckErrorKind::TupleError(
1148+
TupleTypeCheckErrorKind::NotTuple,
1149+
))
1150+
}
1151+
.map_err(|kind| mk_typck_err::<T>(typ, kind))
1152+
}
1153+
10271154
// Helper iterators
10281155

10291156
/// Iterates over a sequence of `[bytes]` items from a frame subslice, expecting
@@ -1120,6 +1247,9 @@ pub enum BuiltinTypeCheckErrorKind {
11201247

11211248
/// A type check failure specific to a CQL map.
11221249
MapError(MapTypeCheckErrorKind),
1250+
1251+
/// A type check failure specific to a CQL tuple.
1252+
TupleError(TupleTypeCheckErrorKind),
11231253
}
11241254

11251255
impl From<SetOrListTypeCheckErrorKind> for BuiltinTypeCheckErrorKind {
@@ -1136,6 +1266,13 @@ impl From<MapTypeCheckErrorKind> for BuiltinTypeCheckErrorKind {
11361266
}
11371267
}
11381268

1269+
impl From<TupleTypeCheckErrorKind> for BuiltinTypeCheckErrorKind {
1270+
#[inline]
1271+
fn from(value: TupleTypeCheckErrorKind) -> Self {
1272+
BuiltinTypeCheckErrorKind::TupleError(value)
1273+
}
1274+
}
1275+
11391276
impl Display for BuiltinTypeCheckErrorKind {
11401277
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
11411278
match self {
@@ -1144,6 +1281,7 @@ impl Display for BuiltinTypeCheckErrorKind {
11441281
}
11451282
BuiltinTypeCheckErrorKind::SetOrListError(err) => err.fmt(f),
11461283
BuiltinTypeCheckErrorKind::MapError(err) => err.fmt(f),
1284+
BuiltinTypeCheckErrorKind::TupleError(err) => err.fmt(f),
11471285
}
11481286
}
11491287
}
@@ -1204,6 +1342,57 @@ impl Display for MapTypeCheckErrorKind {
12041342
}
12051343
}
12061344

1345+
/// Describes why type checking of a tuple failed.
1346+
#[derive(Debug, Clone)]
1347+
#[non_exhaustive]
1348+
pub enum TupleTypeCheckErrorKind {
1349+
/// The CQL type is not a tuple.
1350+
NotTuple,
1351+
1352+
/// The tuple has the wrong element count.
1353+
WrongElementCount {
1354+
/// The number of elements that the Rust tuple has.
1355+
rust_type_el_count: usize,
1356+
1357+
/// The number of elements that the CQL tuple type has.
1358+
cql_type_el_count: usize,
1359+
},
1360+
1361+
/// The CQL type and the Rust type of a tuple field failed to type check against each other.
1362+
FieldTypeCheckFailed {
1363+
/// The index of the field whose type check failed.
1364+
position: usize,
1365+
1366+
/// The type check error that occured.
1367+
err: TypeCheckError,
1368+
},
1369+
}
1370+
1371+
impl Display for TupleTypeCheckErrorKind {
1372+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1373+
match self {
1374+
TupleTypeCheckErrorKind::NotTuple => write!(
1375+
f,
1376+
"the CQL type the tuple was attempted to be serialized to is not a tuple"
1377+
),
1378+
TupleTypeCheckErrorKind::WrongElementCount {
1379+
rust_type_el_count,
1380+
cql_type_el_count,
1381+
} => write!(
1382+
f,
1383+
"wrong tuple element count: CQL type has {cql_type_el_count}, the Rust tuple has {rust_type_el_count}"
1384+
),
1385+
1386+
TupleTypeCheckErrorKind::FieldTypeCheckFailed { position, err } => write!(
1387+
f,
1388+
"the CQL type and the Rust type of the tuple field {} failed to type check against each other: {}",
1389+
position,
1390+
err
1391+
)
1392+
}
1393+
}
1394+
}
1395+
12071396
/// Deserialization of one of the built-in types failed.
12081397
#[derive(Debug, Error)]
12091398
#[error("Failed to deserialize Rust type {rust_name} from CQL type {cql_type:?}: {kind}")]
@@ -1268,6 +1457,9 @@ pub enum BuiltinDeserializationErrorKind {
12681457

12691458
/// A deserialization failure specific to a CQL map.
12701459
MapError(MapDeserializationErrorKind),
1460+
1461+
/// A deserialization failure specific to a CQL tuple.
1462+
TupleError(TupleDeserializationErrorKind),
12711463
}
12721464

12731465
impl Display for BuiltinDeserializationErrorKind {
@@ -1297,6 +1489,7 @@ impl Display for BuiltinDeserializationErrorKind {
12971489
),
12981490
BuiltinDeserializationErrorKind::SetOrListError(err) => err.fmt(f),
12991491
BuiltinDeserializationErrorKind::MapError(err) => err.fmt(f),
1492+
BuiltinDeserializationErrorKind::TupleError(err) => err.fmt(f),
13001493
}
13011494
}
13021495
}
@@ -1368,6 +1561,39 @@ impl From<MapDeserializationErrorKind> for BuiltinDeserializationErrorKind {
13681561
}
13691562
}
13701563

1564+
/// Describes why deserialization of a tuple failed.
1565+
#[derive(Debug, Clone)]
1566+
#[non_exhaustive]
1567+
pub enum TupleDeserializationErrorKind {
1568+
/// One of the tuple fields failed to deserialize.
1569+
FieldDeserializationFailed {
1570+
/// Index of the tuple field that failed to deserialize.
1571+
position: usize,
1572+
1573+
/// The error that caused the tuple field deserialization to fail.
1574+
err: DeserializationError,
1575+
},
1576+
}
1577+
1578+
impl Display for TupleDeserializationErrorKind {
1579+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1580+
match self {
1581+
TupleDeserializationErrorKind::FieldDeserializationFailed {
1582+
position: index,
1583+
err,
1584+
} => {
1585+
write!(f, "field no. {index} failed to deserialize: {err}")
1586+
}
1587+
}
1588+
}
1589+
}
1590+
1591+
impl From<TupleDeserializationErrorKind> for BuiltinDeserializationErrorKind {
1592+
fn from(err: TupleDeserializationErrorKind) -> Self {
1593+
Self::TupleError(err)
1594+
}
1595+
}
1596+
13711597
#[cfg(test)]
13721598
mod tests {
13731599
use bytes::{BufMut, Bytes, BytesMut};
@@ -1901,6 +2127,21 @@ mod tests {
19012127
assert_eq!(decoded_btree_string, expected_string.into_iter().collect(),);
19022128
}
19032129

2130+
#[test]
2131+
fn test_tuples() {
2132+
let mut tuple_contents = BytesMut::new();
2133+
append_bytes(&mut tuple_contents, &42i32.to_be_bytes());
2134+
append_bytes(&mut tuple_contents, "foo".as_bytes());
2135+
append_null(&mut tuple_contents);
2136+
2137+
let tuple = make_bytes(&tuple_contents);
2138+
2139+
let typ = ColumnType::Tuple(vec![ColumnType::Int, ColumnType::Ascii, ColumnType::Uuid]);
2140+
2141+
let tup = deserialize::<(i32, &str, Option<Uuid>)>(&typ, &tuple).unwrap();
2142+
assert_eq!(tup, (42, "foo", None));
2143+
}
2144+
19042145
// Checks that both new and old serialization framework
19052146
// produces the same results in this case
19062147
fn compat_check<T>(typ: &ColumnType, raw: Bytes)

0 commit comments

Comments
 (0)