Skip to content

Commit

Permalink
Merge pull request #882 from piodul/macros-name-checking-improvements
Browse files Browse the repository at this point in the history
scylla-macros: attributes for better control over name checks
  • Loading branch information
Lorak-mmk authored Dec 15, 2023
2 parents 10b49ef + 39908a6 commit 8f360c5
Show file tree
Hide file tree
Showing 6 changed files with 423 additions and 23 deletions.
37 changes: 35 additions & 2 deletions scylla-cql/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub use scylla_macros::ValueList;
/// }
/// ```
///
/// # Attributes
/// # Struct attributes
///
/// `#[scylla(flavor = "flavor_name")]`
///
Expand Down Expand Up @@ -86,6 +86,22 @@ pub use scylla_macros::ValueList;
/// It's not possible to automatically resolve those issues in the procedural
/// macro itself, so in those cases the user must provide an alternative path
/// to either the `scylla` or `scylla-cql` crate.
///
/// `#[scylla(skip_name_checks)]
///
/// _Specific only to the `enforce_order` flavor._
///
/// Skips checking Rust field names against names of the UDT fields. With this
/// annotation, the generated implementation will allow mismatch between Rust
/// struct field names and UDT field names, i.e. it's OK if i-th field has a
/// different name in Rust and in the UDT. Fields are still being type-checked.
///
/// # Field attributes
///
/// `#[scylla(rename = "name_in_the_udt")]`
///
/// Serializes the field to the UDT struct field with given name instead of
/// its Rust name.
pub use scylla_macros::SerializeCql;

/// Derive macro for the [`SerializeRow`](crate::types::serialize::row::SerializeRow) trait
Expand Down Expand Up @@ -123,7 +139,7 @@ pub use scylla_macros::SerializeCql;
/// }
/// ```
///
/// # Attributes
/// # Struct attributes
///
/// `#[scylla(flavor = "flavor_name")]`
///
Expand Down Expand Up @@ -163,6 +179,23 @@ pub use scylla_macros::SerializeCql;
/// It's not possible to automatically resolve those issues in the procedural
/// macro itself, so in those cases the user must provide an alternative path
/// to either the `scylla` or `scylla-cql` crate.
///
/// `#[scylla(skip_name_checks)]
///
/// _Specific only to the `enforce_order` flavor._
///
/// Skips checking Rust field names against names of the columns / bind markers.
/// With this annotation, the generated implementation will allow mismatch
/// between Rust struct field names and the column / bind markers, i.e. it's
/// OK if i-th Rust struct field has a different name than the column / bind
/// marker. The values are still being type-checked.
///
/// # Field attributes
///
/// `#[scylla(rename = "column_or_bind_marker_name")]`
///
/// Serializes the field to the column / bind marker with given name instead of
/// its Rust name.
pub use scylla_macros::SerializeRow;

// Reexports for derive(IntoUserType)
Expand Down
71 changes: 71 additions & 0 deletions scylla-cql/src/types/serialize/row.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1381,4 +1381,75 @@ mod tests {
.iter()
.all(|v| v == RawValue::Value(&[0, 0, 0, 0, 0x07, 0x5b, 0xcd, 0x15])))
}

#[derive(SerializeRow, Debug)]
#[scylla(crate = crate)]
struct TestRowWithColumnRename {
a: String,
#[scylla(rename = "x")]
b: i32,
}

#[derive(SerializeRow, Debug)]
#[scylla(crate = crate, flavor = "enforce_order")]
struct TestRowWithColumnRenameAndEnforceOrder {
a: String,
#[scylla(rename = "x")]
b: i32,
}

#[test]
fn test_row_serialization_with_column_rename() {
let spec = [col("x", ColumnType::Int), col("a", ColumnType::Text)];

let reference = do_serialize((42i32, "Ala ma kota"), &spec);
let row = do_serialize(
TestRowWithColumnRename {
a: "Ala ma kota".to_owned(),
b: 42,
},
&spec,
);

assert_eq!(reference, row);
}

#[test]
fn test_row_serialization_with_column_rename_and_enforce_order() {
let spec = [col("a", ColumnType::Text), col("x", ColumnType::Int)];

let reference = do_serialize(("Ala ma kota", 42i32), &spec);
let row = do_serialize(
TestRowWithColumnRenameAndEnforceOrder {
a: "Ala ma kota".to_owned(),
b: 42,
},
&spec,
);

assert_eq!(reference, row);
}

#[derive(SerializeRow, Debug)]
#[scylla(crate = crate, flavor = "enforce_order", skip_name_checks)]
struct TestRowWithSkippedNameChecks {
a: String,
b: i32,
}

#[test]
fn test_row_serialization_with_skipped_name_checks() {
let spec = [col("a", ColumnType::Text), col("x", ColumnType::Int)];

let reference = do_serialize(("Ala ma kota", 42i32), &spec);
let row = do_serialize(
TestRowWithSkippedNameChecks {
a: "Ala ma kota".to_owned(),
b: 42,
},
&spec,
);

assert_eq!(reference, row);
}
}
119 changes: 119 additions & 0 deletions scylla-cql/src/types/serialize/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2354,4 +2354,123 @@ mod tests {
)
));
}

#[derive(SerializeCql, Debug)]
#[scylla(crate = crate)]
struct TestUdtWithFieldRename {
a: String,
#[scylla(rename = "x")]
b: i32,
}

#[derive(SerializeCql, Debug)]
#[scylla(crate = crate, flavor = "enforce_order")]
struct TestUdtWithFieldRenameAndEnforceOrder {
a: String,
#[scylla(rename = "x")]
b: i32,
}

#[test]
fn test_udt_serialization_with_field_rename() {
let typ = ColumnType::UserDefinedType {
type_name: "typ".to_string(),
keyspace: "ks".to_string(),
field_types: vec![
("x".to_string(), ColumnType::Int),
("a".to_string(), ColumnType::Text),
],
};

let mut reference = Vec::new();
// Total length of the struct is 23
reference.extend_from_slice(&23i32.to_be_bytes());
// Field 'x'
reference.extend_from_slice(&4i32.to_be_bytes());
reference.extend_from_slice(&42i32.to_be_bytes());
// Field 'a'
reference.extend_from_slice(&("Ala ma kota".len() as i32).to_be_bytes());
reference.extend_from_slice("Ala ma kota".as_bytes());

let udt = do_serialize(
TestUdtWithFieldRename {
a: "Ala ma kota".to_owned(),
b: 42,
},
&typ,
);

assert_eq!(reference, udt);
}

#[test]
fn test_udt_serialization_with_field_rename_and_enforce_order() {
let typ = ColumnType::UserDefinedType {
type_name: "typ".to_string(),
keyspace: "ks".to_string(),
field_types: vec![
("a".to_string(), ColumnType::Text),
("x".to_string(), ColumnType::Int),
],
};

let mut reference = Vec::new();
// Total length of the struct is 23
reference.extend_from_slice(&23i32.to_be_bytes());
// Field 'a'
reference.extend_from_slice(&("Ala ma kota".len() as i32).to_be_bytes());
reference.extend_from_slice("Ala ma kota".as_bytes());
// Field 'x'
reference.extend_from_slice(&4i32.to_be_bytes());
reference.extend_from_slice(&42i32.to_be_bytes());

let udt = do_serialize(
TestUdtWithFieldRenameAndEnforceOrder {
a: "Ala ma kota".to_owned(),
b: 42,
},
&typ,
);

assert_eq!(reference, udt);
}

#[derive(SerializeCql, Debug)]
#[scylla(crate = crate, flavor = "enforce_order", skip_name_checks)]
struct TestUdtWithSkippedNameChecks {
a: String,
b: i32,
}

#[test]
fn test_udt_serialization_with_skipped_name_checks() {
let typ = ColumnType::UserDefinedType {
type_name: "typ".to_string(),
keyspace: "ks".to_string(),
field_types: vec![
("a".to_string(), ColumnType::Text),
("x".to_string(), ColumnType::Int),
],
};

let mut reference = Vec::new();
// Total length of the struct is 23
reference.extend_from_slice(&23i32.to_be_bytes());
// Field 'a'
reference.extend_from_slice(&("Ala ma kota".len() as i32).to_be_bytes());
reference.extend_from_slice("Ala ma kota".as_bytes());
// Field 'x'
reference.extend_from_slice(&4i32.to_be_bytes());
reference.extend_from_slice(&42i32.to_be_bytes());

let udt = do_serialize(
TestUdtWithFieldRenameAndEnforceOrder {
a: "Ala ma kota".to_owned(),
b: 42,
},
&typ,
);

assert_eq!(reference, udt);
}
}
Loading

0 comments on commit 8f360c5

Please sign in to comment.