1- use std:: { convert:: TryInto , net:: IpAddr } ;
1+ use std:: { collections :: HashMap , convert:: TryInto , net:: IpAddr } ;
22
33use scylla:: {
44 frame:: { response:: result:: ColumnType , value:: CqlDate } ,
@@ -7,7 +7,8 @@ use scylla::{
77 BuiltinSerializationError , BuiltinSerializationErrorKind , BuiltinTypeCheckError ,
88 BuiltinTypeCheckErrorKind , MapSerializationErrorKind , MapTypeCheckErrorKind ,
99 SerializeCql , SetOrListSerializationErrorKind , SetOrListTypeCheckErrorKind ,
10- TupleSerializationErrorKind , TupleTypeCheckErrorKind ,
10+ TupleSerializationErrorKind , TupleTypeCheckErrorKind , UdtSerializationErrorKind ,
11+ UdtTypeCheckErrorKind ,
1112 } ,
1213 writers:: WrittenCellProof ,
1314 CellWriter , SerializationError ,
@@ -108,7 +109,7 @@ impl SerializeCql for CassCqlValue {
108109 keyspace,
109110 type_name,
110111 fields,
111- } => todo ! ( ) ,
112+ } => serialize_udt ( typ , keyspace , type_name , fields , writer ) ,
112113 }
113114 }
114115}
@@ -357,3 +358,79 @@ fn serialize_mapping<'t, 'b, K: SerializeCql + 't, V: SerializeCql + 't>(
357358 . finish ( )
358359 . map_err ( |_| mk_ser_err_named ( rust_name, typ, BuiltinSerializationErrorKind :: SizeOverflow ) )
359360}
361+
362+ fn serialize_udt < ' b > (
363+ typ : & ColumnType ,
364+ keyspace : & str ,
365+ type_name : & str ,
366+ values : & [ ( String , Option < CassCqlValue > ) ] ,
367+ writer : CellWriter < ' b > ,
368+ ) -> Result < WrittenCellProof < ' b > , SerializationError > {
369+ let ( dst_type_name, dst_keyspace, field_types) = match typ {
370+ ColumnType :: UserDefinedType {
371+ type_name,
372+ keyspace,
373+ field_types,
374+ } => ( type_name, keyspace, field_types) ,
375+ _ => {
376+ return Err ( mk_typck_err :: < CassCqlValue > (
377+ typ,
378+ UdtTypeCheckErrorKind :: NotUdt ,
379+ ) )
380+ }
381+ } ;
382+
383+ if keyspace != dst_keyspace || type_name != dst_type_name {
384+ return Err ( mk_typck_err :: < CassCqlValue > (
385+ typ,
386+ UdtTypeCheckErrorKind :: NameMismatch {
387+ keyspace : dst_keyspace. clone ( ) ,
388+ type_name : dst_type_name. clone ( ) ,
389+ } ,
390+ ) ) ;
391+ }
392+
393+ // Allow columns present in the CQL type which are not present in CqlValue,
394+ // but not the other way around
395+ let mut indexed_fields: HashMap < _ , _ > = values. iter ( ) . map ( |( k, v) | ( k. as_str ( ) , v) ) . collect ( ) ;
396+
397+ let mut builder = writer. into_value_builder ( ) ;
398+ for ( fname, ftyp) in field_types {
399+ // Take a value from the original list.
400+ // If a field is missing, write null instead.
401+ let fvalue = indexed_fields
402+ . remove ( fname. as_str ( ) )
403+ . and_then ( |x| x. as_ref ( ) ) ;
404+
405+ let writer = builder. make_sub_writer ( ) ;
406+ match fvalue {
407+ None => writer. set_null ( ) ,
408+ Some ( v) => <_ as SerializeCql >:: serialize ( v, ftyp, writer) . map_err ( |err| {
409+ mk_ser_err :: < CassCqlValue > (
410+ typ,
411+ UdtSerializationErrorKind :: FieldSerializationFailed {
412+ field_name : fname. clone ( ) ,
413+ err,
414+ } ,
415+ )
416+ } ) ?,
417+ } ;
418+ }
419+
420+ // If there are some leftover fields, it's an error.
421+ if !indexed_fields. is_empty ( ) {
422+ // In order to have deterministic errors, return an error about
423+ // the lexicographically smallest field.
424+ let fname = indexed_fields. keys ( ) . min ( ) . unwrap ( ) ;
425+ return Err ( mk_typck_err :: < CassCqlValue > (
426+ typ,
427+ UdtTypeCheckErrorKind :: NoSuchFieldInUdt {
428+ field_name : fname. to_string ( ) ,
429+ } ,
430+ ) ) ;
431+ }
432+
433+ builder
434+ . finish ( )
435+ . map_err ( |_| mk_ser_err :: < CassCqlValue > ( typ, BuiltinSerializationErrorKind :: SizeOverflow ) )
436+ }
0 commit comments