Skip to content

Commit

Permalink
Merge pull request #1119 from wprzytula/unify-ser-deser-macro-attrs
Browse files Browse the repository at this point in the history
Unify macro attributes between serialization and deserialization derive macros
  • Loading branch information
wprzytula authored Nov 14, 2024
2 parents 8ccc597 + 0c9a8cc commit 98fc02a
Show file tree
Hide file tree
Showing 18 changed files with 630 additions and 134 deletions.
9 changes: 5 additions & 4 deletions docs/source/data-types/udt.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ and `DeserializeValue` macros documentation.
```rust
# extern crate scylla;
# async fn check_only_compiles() {
use scylla::macros::{FromUserType, SerializeValue};
use scylla::macros::{DeserializeValue, SerializeValue};

// Define a custom struct that matches the User Defined Type created earlier.
// Fields must be in the same order as they are in the database and also
// have the same names.
// Fields don't have to be in the same order as they are in the database.
// By default, they must have the same names, but this can be worked around
// using `#[rename] field attribute.
// Wrapping a field in Option will gracefully handle null field values.
#[derive(Debug, FromUserType, SerializeValue)]
#[derive(Debug, DeserializeValue, SerializeValue)]
struct MyType {
int_val: i32,
text_val: Option<String>,
Expand Down
2 changes: 0 additions & 2 deletions docs/source/migration-guides/0.11-serialization.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ The driver comes a set of `impl`s of those traits which allow to represent any C

By default, the `SerializeRow` and `SerializeValue` **will match the fields in the Rust struct by name to bind marker names** (in case of `SerializeRow`) **or UDT field names** (in case of `SerializeValue`). This is different from the old `ValueList` and `IntoUserType` macros which did not look at the field names at all and would expect the user to order the fields correctly. While the new behavior is much more ergonomic, you might have reasons not to use it.

> **NOTE:** The deserialization macro counterparts `FromRow` and `FromUserType` have the same limitation as the old serialization macros - they require struct fields to be properly ordered. While a similar rework is planned for the deserialization traits in a future release, for the time being it might not be worth keeping the column names in sync with the database.
In order to bring the old behavior to the new macros (the only difference being type checking which cannot be disabled right now) you can configure it using attributes, as shown in the snippet below:

```rust
Expand Down
4 changes: 2 additions & 2 deletions scylla-cql/src/types/deserialize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ macro_rules! make_error_replace_rust_name {
use make_error_replace_rust_name;

#[cfg(test)]
mod tests {
pub(crate) mod tests {
use bytes::{Bytes, BytesMut};

use crate::frame::response::result::{ColumnSpec, ColumnType, TableSpec};
Expand All @@ -342,7 +342,7 @@ mod tests {
bytes.freeze()
}

pub(super) const fn spec<'a>(name: &'a str, typ: ColumnType<'a>) -> ColumnSpec<'a> {
pub(crate) const fn spec<'a>(name: &'a str, typ: ColumnType<'a>) -> ColumnSpec<'a> {
ColumnSpec::borrowed(name, typ, TableSpec::borrowed("ks", "tbl"))
}
}
2 changes: 1 addition & 1 deletion scylla-cql/src/types/deserialize/row.rs
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ impl Display for BuiltinDeserializationErrorKind {

#[cfg(test)]
#[path = "row_tests.rs"]
mod tests;
pub(crate) mod tests;

/// ```compile_fail
///
Expand Down
10 changes: 5 additions & 5 deletions scylla-cql/src/types/deserialize/row_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ struct TestUdtWithNoFieldsUnordered {}

#[allow(unused)]
#[derive(DeserializeRow)]
#[scylla(crate = crate, enforce_order)]
#[scylla(crate = crate, flavor = "enforce_order")]
struct TestUdtWithNoFieldsOrdered {}

#[test]
Expand Down Expand Up @@ -143,7 +143,7 @@ fn test_struct_deserialization_loose_ordering() {
#[test]
fn test_struct_deserialization_strict_ordering() {
#[derive(DeserializeRow, PartialEq, Eq, Debug)]
#[scylla(crate = "crate", enforce_order)]
#[scylla(crate = "crate", flavor = "enforce_order")]
struct MyRow<'a> {
a: &'a str,
b: Option<i32>,
Expand Down Expand Up @@ -180,7 +180,7 @@ fn test_struct_deserialization_strict_ordering() {
#[test]
fn test_struct_deserialization_no_name_check() {
#[derive(DeserializeRow, PartialEq, Eq, Debug)]
#[scylla(crate = "crate", enforce_order, skip_name_checks)]
#[scylla(crate = "crate", flavor = "enforce_order", skip_name_checks)]
struct MyRow<'a> {
a: &'a str,
b: Option<i32>,
Expand Down Expand Up @@ -251,7 +251,7 @@ fn val_str(s: &str) -> Option<Vec<u8>> {
Some(s.as_bytes().to_vec())
}

fn deserialize<'frame, 'metadata, R>(
pub(crate) fn deserialize<'frame, 'metadata, R>(
specs: &'metadata [ColumnSpec<'metadata>],
byts: &'frame Bytes,
) -> Result<R, DeserializationError>
Expand Down Expand Up @@ -623,7 +623,7 @@ fn test_struct_deserialization_errors() {
// Strict ordering
{
#[derive(scylla_macros::DeserializeRow, PartialEq, Eq, Debug)]
#[scylla(crate = "crate", enforce_order)]
#[scylla(crate = "crate", flavor = "enforce_order")]
struct MyRow<'a> {
a: &'a str,
#[scylla(skip)]
Expand Down
6 changes: 3 additions & 3 deletions scylla-cql/src/types/deserialize/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1950,7 +1950,7 @@ impl From<UdtDeserializationErrorKind> for BuiltinDeserializationErrorKind {

#[cfg(test)]
#[path = "value_tests.rs"]
pub(super) mod tests;
pub(crate) mod tests;

/// ```compile_fail
///
Expand All @@ -1963,7 +1963,7 @@ fn _test_udt_bad_attributes_skip_name_check_requires_enforce_order() {}
/// ```compile_fail
///
/// #[derive(scylla_macros::DeserializeValue)]
/// #[scylla(crate = scylla_cql, enforce_order, skip_name_checks)]
/// #[scylla(crate = scylla_cql, flavor = "enforce_order", skip_name_checks)]
/// struct TestUdt {
/// #[scylla(rename = "b")]
/// a: i32,
Expand Down Expand Up @@ -1999,7 +1999,7 @@ fn _test_udt_bad_attributes_rename_collision_with_another_rename() {}
/// ```compile_fail
///
/// #[derive(scylla_macros::DeserializeValue)]
/// #[scylla(crate = scylla_cql, enforce_order, skip_name_checks)]
/// #[scylla(crate = scylla_cql, flavor = "enforce_order", skip_name_checks)]
/// struct TestUdt {
/// a: i32,
/// #[scylla(allow_missing)]
Expand Down
14 changes: 7 additions & 7 deletions scylla-cql/src/types/deserialize/value_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ fn test_tuples() {
);
}

fn udt_def_with_fields(
pub(crate) fn udt_def_with_fields(
fields: impl IntoIterator<Item = (impl Into<Cow<'static, str>>, ColumnType<'static>)>,
) -> ColumnType<'static> {
ColumnType::UserDefinedType {
Expand Down Expand Up @@ -632,7 +632,7 @@ struct TestUdtWithNoFieldsUnordered {}

#[allow(unused)]
#[derive(scylla_macros::DeserializeValue)]
#[scylla(crate = crate, enforce_order)]
#[scylla(crate = crate, flavor = "enforce_order")]
struct TestUdtWithNoFieldsOrdered {}

#[test]
Expand Down Expand Up @@ -786,7 +786,7 @@ fn test_udt_loose_ordering() {
#[test]
fn test_udt_strict_ordering() {
#[derive(scylla_macros::DeserializeValue, PartialEq, Eq, Debug)]
#[scylla(crate = "crate", enforce_order)]
#[scylla(crate = "crate", flavor = "enforce_order")]
struct Udt<'a> {
#[scylla(default_when_null)]
a: &'a str,
Expand Down Expand Up @@ -860,7 +860,7 @@ fn test_udt_strict_ordering() {
// An excess field at the end of UDT, when such are forbidden
{
#[derive(scylla_macros::DeserializeValue, PartialEq, Eq, Debug)]
#[scylla(crate = "crate", enforce_order, forbid_excess_udt_fields)]
#[scylla(crate = "crate", flavor = "enforce_order", forbid_excess_udt_fields)]
struct Udt<'a> {
a: &'a str,
#[scylla(skip)]
Expand Down Expand Up @@ -934,7 +934,7 @@ fn test_udt_strict_ordering() {
#[test]
fn test_udt_no_name_check() {
#[derive(scylla_macros::DeserializeValue, PartialEq, Eq, Debug)]
#[scylla(crate = "crate", enforce_order, skip_name_checks)]
#[scylla(crate = "crate", flavor = "enforce_order", skip_name_checks)]
struct Udt<'a> {
a: &'a str,
#[scylla(skip)]
Expand Down Expand Up @@ -1044,7 +1044,7 @@ fn test_custom_type_parser() {
assert_eq!(tup, SwappedPair("foo", 42));
}

fn deserialize<'frame, 'metadata, T>(
pub(crate) fn deserialize<'frame, 'metadata, T>(
typ: &'metadata ColumnType<'metadata>,
bytes: &'frame Bytes,
) -> Result<T, DeserializationError>
Expand Down Expand Up @@ -1762,7 +1762,7 @@ fn test_udt_errors() {
// Strict ordering
{
#[derive(scylla_macros::DeserializeValue, PartialEq, Eq, Debug)]
#[scylla(crate = "crate", enforce_order, forbid_excess_udt_fields)]
#[scylla(crate = "crate", flavor = "enforce_order", forbid_excess_udt_fields)]
struct Udt<'a> {
a: &'a str,
#[scylla(skip)]
Expand Down
3 changes: 3 additions & 0 deletions scylla-cql/src/types/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
pub mod deserialize;
pub mod serialize;

#[cfg(test)]
mod types_tests;
51 changes: 49 additions & 2 deletions scylla-cql/src/types/serialize/row.rs
Original file line number Diff line number Diff line change
Expand Up @@ -867,8 +867,55 @@ impl<'a> Iterator for SerializedValuesIterator<'a> {
}
}

mod doctests {

/// ```compile_fail
///
/// #[derive(scylla_macros::SerializeRow)]
/// #[scylla(crate = scylla_cql, skip_name_checks)]
/// struct TestRow {}
/// ```
fn _test_struct_deserialization_name_check_skip_requires_enforce_order() {}

/// ```compile_fail
///
/// #[derive(scylla_macros::SerializeRow)]
/// #[scylla(crate = scylla_cql, skip_name_checks)]
/// struct TestRow {
/// #[scylla(rename = "b")]
/// a: i32,
/// }
/// ```
fn _test_struct_deserialization_skip_name_check_conflicts_with_rename() {}

/// ```compile_fail
///
/// #[derive(scylla_macros::SerializeRow)]
/// #[scylla(crate = scylla_cql)]
/// struct TestRow {
/// #[scylla(rename = "b")]
/// a: i32,
/// b: String,
/// }
/// ```
fn _test_struct_deserialization_skip_rename_collision_with_field() {}

/// ```compile_fail
///
/// #[derive(scylla_macros::SerializeRow)]
/// #[scylla(crate = scylla_cql)]
/// struct TestRow {
/// #[scylla(rename = "c")]
/// a: i32,
/// #[scylla(rename = "c")]
/// b: String,
/// }
/// ```
fn _test_struct_deserialization_rename_collision_with_another_rename() {}
}

#[cfg(test)]
mod tests {
pub(crate) mod tests {
use std::borrow::Cow;
use std::collections::BTreeMap;

Expand Down Expand Up @@ -990,7 +1037,7 @@ mod tests {
assert_eq!(typed_data, erased_data);
}

fn do_serialize<T: SerializeRow>(t: T, columns: &[ColumnSpec]) -> Vec<u8> {
pub(crate) fn do_serialize<T: SerializeRow>(t: T, columns: &[ColumnSpec]) -> Vec<u8> {
let ctx = RowSerializationContext { columns };
let mut ret = Vec::new();
let mut builder = RowWriter::new(&mut ret);
Expand Down
105 changes: 101 additions & 4 deletions scylla-cql/src/types/serialize/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1496,8 +1496,105 @@ pub enum ValueToSerializeValueAdapterError {
},
}

mod doctests {
/// ```compile_fail
///
/// #[derive(scylla_macros::SerializeValue)]
/// #[scylla(crate = scylla_cql, skip_name_checks)]
/// struct TestUdt {}
/// ```
fn _test_udt_bad_attributes_skip_name_check_requires_enforce_order() {}

/// ```compile_fail
///
/// #[derive(scylla_macros::SerializeValue)]
/// #[scylla(crate = scylla_cql, flavor = "enforce_order", skip_name_checks)]
/// struct TestUdt {
/// #[scylla(rename = "b")]
/// a: i32,
/// }
/// ```
fn _test_udt_bad_attributes_skip_name_check_conflicts_with_rename() {}

/// ```compile_fail
///
/// #[derive(scylla_macros::SerializeValue)]
/// #[scylla(crate = scylla_cql)]
/// struct TestUdt {
/// #[scylla(rename = "b")]
/// a: i32,
/// b: String,
/// }
/// ```
fn _test_udt_bad_attributes_rename_collision_with_field() {}

/// ```compile_fail
///
/// #[derive(scylla_macros::SerializeValue)]
/// #[scylla(crate = scylla_cql)]
/// struct TestUdt {
/// #[scylla(rename = "c")]
/// a: i32,
/// #[scylla(rename = "c")]
/// b: String,
/// }
/// ```
fn _test_udt_bad_attributes_rename_collision_with_another_rename() {}

/// ```compile_fail
///
/// #[derive(scylla_macros::SerializeValue)]
/// #[scylla(crate = scylla_cql, flavor = "enforce_order", skip_name_checks)]
/// struct TestUdt {
/// a: i32,
/// #[scylla(allow_missing)]
/// b: bool,
/// c: String,
/// }
/// ```
fn _test_udt_bad_attributes_name_skip_name_checks_limitations_on_allow_missing() {}

/// ```
///
/// #[derive(scylla_macros::SerializeValue)]
/// #[scylla(crate = scylla_cql, flavor = "enforce_order", skip_name_checks)]
/// struct TestUdt {
/// a: i32,
/// #[scylla(allow_missing)]
/// b: bool,
/// #[scylla(allow_missing)]
/// c: String,
/// }
/// ```
fn _test_udt_good_attributes_name_skip_name_checks_limitations_on_allow_missing() {}

/// ```
/// #[derive(scylla_macros::SerializeValue)]
/// #[scylla(crate = scylla_cql)]
/// struct TestUdt {
/// a: i32,
/// #[scylla(allow_missing)]
/// b: bool,
/// c: String,
/// }
/// ```
fn _test_udt_unordered_flavour_no_limitations_on_allow_missing() {}

/// ```
/// #[derive(scylla_macros::SerializeValue)]
/// #[scylla(crate = scylla_cql)]
/// struct TestUdt {
/// a: i32,
/// #[scylla(default_when_null)]
/// b: bool,
/// c: String,
/// }
/// ```
fn _test_udt_default_when_null_is_accepted() {}
}

#[cfg(test)]
mod tests {
pub(crate) mod tests {
use std::collections::BTreeMap;

use crate::frame::response::result::{ColumnType, CqlValue};
Expand Down Expand Up @@ -1557,7 +1654,7 @@ mod tests {
t.serialize(typ, writer).map(|_| ()).map(|()| ret)
}

fn do_serialize<T: SerializeValue>(t: T, typ: &ColumnType) -> Vec<u8> {
pub(crate) fn do_serialize<T: SerializeValue>(t: T, typ: &ColumnType) -> Vec<u8> {
do_serialize_result(t, typ).unwrap()
}

Expand Down Expand Up @@ -2591,7 +2688,7 @@ mod tests {
}

#[derive(SerializeValue, Debug, PartialEq, Eq, Default)]
#[scylla(crate = crate, force_exact_match)]
#[scylla(crate = crate, forbid_excess_udt_fields)]
struct TestStrictUdtWithFieldSorting {
a: String,
b: i32,
Expand Down Expand Up @@ -2647,7 +2744,7 @@ mod tests {
}

#[derive(SerializeValue, Debug, PartialEq, Eq, Default)]
#[scylla(crate = crate, flavor = "enforce_order", force_exact_match)]
#[scylla(crate = crate, flavor = "enforce_order", forbid_excess_udt_fields)]
struct TestStrictUdtWithEnforcedOrder {
a: String,
b: i32,
Expand Down
Loading

0 comments on commit 98fc02a

Please sign in to comment.