1
- use std:: { convert:: TryInto , net:: IpAddr } ;
1
+ use std:: { collections :: HashMap , convert:: TryInto , net:: IpAddr } ;
2
2
3
3
use scylla:: {
4
4
frame:: { response:: result:: ColumnType , value:: CqlDate } ,
@@ -7,7 +7,8 @@ use scylla::{
7
7
BuiltinSerializationError , BuiltinSerializationErrorKind , BuiltinTypeCheckError ,
8
8
BuiltinTypeCheckErrorKind , MapSerializationErrorKind , MapTypeCheckErrorKind ,
9
9
SerializeCql , SetOrListSerializationErrorKind , SetOrListTypeCheckErrorKind ,
10
- TupleSerializationErrorKind , TupleTypeCheckErrorKind ,
10
+ TupleSerializationErrorKind , TupleTypeCheckErrorKind , UdtSerializationErrorKind ,
11
+ UdtTypeCheckErrorKind ,
11
12
} ,
12
13
writers:: WrittenCellProof ,
13
14
CellWriter , SerializationError ,
@@ -108,7 +109,7 @@ impl SerializeCql for CassCqlValue {
108
109
keyspace,
109
110
type_name,
110
111
fields,
111
- } => todo ! ( ) ,
112
+ } => serialize_udt ( typ , keyspace , type_name , fields , writer ) ,
112
113
}
113
114
}
114
115
}
@@ -357,3 +358,79 @@ fn serialize_mapping<'t, 'b, K: SerializeCql + 't, V: SerializeCql + 't>(
357
358
. finish ( )
358
359
. map_err ( |_| mk_ser_err_named ( rust_name, typ, BuiltinSerializationErrorKind :: SizeOverflow ) )
359
360
}
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