Skip to content

Commit

Permalink
macros: DeserializeRow uses Flavor
Browse files Browse the repository at this point in the history
Instead of taking `enforce_order` as boolean flag, DeserializeRow now
takes a string (e.g., `flavor = "enforce_order"`) and converts it to
a variant of the Flavor enum. This way DeserializeRow is unified with
SerializeRow wrt flavor selection.
  • Loading branch information
wprzytula committed Nov 11, 2024
1 parent e8131aa commit ec8f95b
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 27 deletions.
8 changes: 4 additions & 4 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 @@ -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
24 changes: 10 additions & 14 deletions scylla-macros/src/deserialize/row.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -13,12 +15,8 @@ struct StructAttrs {
#[darling(rename = "crate")]
crate_path: Option<syn::Path>,

// 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.
Expand Down Expand Up @@ -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 <skip_name_checks> requires <enforce_order>.");
errors.push(error);
Expand Down Expand Up @@ -153,18 +151,16 @@ type StructDesc = super::StructDescForDeserialize<StructAttrs, Field>;

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(),
}
}
}
Expand Down
24 changes: 15 additions & 9 deletions scylla/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit ec8f95b

Please sign in to comment.