diff --git a/scylla-cql/src/types/deserialize/row_tests.rs b/scylla-cql/src/types/deserialize/row_tests.rs index 9fdbc8586f..eb10e3f03c 100644 --- a/scylla-cql/src/types/deserialize/row_tests.rs +++ b/scylla-cql/src/types/deserialize/row_tests.rs @@ -91,7 +91,7 @@ struct TestUdtWithNoFieldsUnordered {} #[allow(unused)] #[derive(DeserializeRow)] -#[scylla(crate = crate, enforce_order)] +#[scylla(crate = crate, flavor = "enforce_order")] struct TestUdtWithNoFieldsOrdered {} #[test] @@ -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, @@ -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, @@ -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)] diff --git a/scylla-macros/src/deserialize/row.rs b/scylla-macros/src/deserialize/row.rs index 7fe56edd22..01ba2a0e4e 100644 --- a/scylla-macros/src/deserialize/row.rs +++ b/scylla-macros/src/deserialize/row.rs @@ -5,6 +5,8 @@ use proc_macro2::Span; use syn::ext::IdentExt; use syn::parse_quote; +use crate::Flavor; + use super::{DeserializeCommonFieldAttrs, DeserializeCommonStructAttrs}; #[derive(FromAttributes)] @@ -13,12 +15,8 @@ struct StructAttrs { #[darling(rename = "crate")] crate_path: Option, - // If true, then the type checking code will require the order of the fields - // to be the same in both the Rust struct and the columns. This allows the - // deserialization to be slightly faster because looking struct fields up - // by name can be avoided, though it is less convenient. #[darling(default)] - enforce_order: bool, + flavor: Flavor, // If true, then the type checking code won't verify the column names. // Columns will be matched to struct fields based solely on the order. @@ -94,7 +92,7 @@ fn validate_attrs(attrs: &StructAttrs, fields: &[Field]) -> Result<(), darling:: if attrs.skip_name_checks { // Skipping name checks is only available in enforce_order mode - if !attrs.enforce_order { + if attrs.flavor != Flavor::EnforceOrder { let error = darling::Error::custom("attribute requires ."); errors.push(error); @@ -153,18 +151,16 @@ type StructDesc = super::StructDescForDeserialize; impl StructDesc { fn generate_type_check_method(&self) -> syn::ImplItemFn { - if self.attrs.enforce_order { - TypeCheckAssumeOrderGenerator(self).generate() - } else { - TypeCheckUnorderedGenerator(self).generate() + match self.attrs.flavor { + Flavor::MatchByName => TypeCheckUnorderedGenerator(self).generate(), + Flavor::EnforceOrder => TypeCheckAssumeOrderGenerator(self).generate(), } } fn generate_deserialize_method(&self) -> syn::ImplItemFn { - if self.attrs.enforce_order { - DeserializeAssumeOrderGenerator(self).generate() - } else { - DeserializeUnorderedGenerator(self).generate() + match self.attrs.flavor { + Flavor::MatchByName => DeserializeUnorderedGenerator(self).generate(), + Flavor::EnforceOrder => DeserializeAssumeOrderGenerator(self).generate(), } } } diff --git a/scylla/src/macros.rs b/scylla/src/macros.rs index 5bcd80bf9e..0e5076eeb2 100644 --- a/scylla/src/macros.rs +++ b/scylla/src/macros.rs @@ -435,19 +435,25 @@ pub use scylla_macros::DeserializeValue; /// macro itself, so in those cases the user must provide an alternative path /// to either the `scylla` or `scylla-cql` crate. /// -/// `#[scylla(enforce_order)]` +/// `#[scylla(flavor = "flavor_name")]` /// -/// By default, the generated deserialization code will be insensitive -/// to the column order - when processing a column, the corresponding Rust field -/// will be looked up and the column will be deserialized based on its type. -/// However, if the column order and the Rust field order is known to be the -/// same, then the `enforce_order` annotation can be used so that a more -/// efficient implementation that does not perform lookups is be generated. -/// The generated code will still check that the column and field names match. +/// Allows to choose one of the possible "flavors", i.e. the way how the +/// generated code will approach deserialization. Possible flavors are: +/// +/// - `"match_by_name"` (default) - the generated implementation _does not +/// require_ the fields in the Rust struct to be in the same order as the +/// columns in the row. During deserialization, the implementation will take +/// care to deserialize the columns in the order which the database provided. +/// - `"enforce_order"` - the generated implementation _requires_ the fields +/// in the Rust struct to be in the same order as the columns in the row. +/// If the order is incorrect, type checking/deserialization will fail. +/// This is a less robust flavor than `"match_by_name"`, but should be +/// slightly more performant as it doesn't need to perform lookups by name. +/// The generated code will still check that the column and field names match. /// /// #[(scylla(skip_name_checks))] /// -/// This attribute only works when used with `enforce_order`. +/// This attribute only works when used with `flavor = "enforce_order"`. /// /// If set, the generated implementation will not verify the column names at /// all. Because it only works with `enforce_order`, it will deserialize first