Skip to content

Commit 0d5f7d2

Browse files
committed
ser: udt
1 parent 5a5cb5f commit 0d5f7d2

File tree

1 file changed

+80
-3
lines changed

1 file changed

+80
-3
lines changed

scylla-rust-wrapper/src/value.rs

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{convert::TryInto, net::IpAddr};
1+
use std::{collections::HashMap, convert::TryInto, net::IpAddr};
22

33
use 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

Comments
 (0)