@@ -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
9911100fn 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
11251255impl 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+
11391276impl 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
12731465impl 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) ]
13721598mod 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