Derive Macro scylla::SerializeCql

source ·
#[derive(SerializeCql)]
{
    // Attributes available to this derive:
    #[scylla]
}
Expand description

Derive macro for the SerializeCql trait which serializes given Rust structure as a User Defined Type (UDT).

At the moment, only structs with named fields are supported.

Serialization will fail if there are some fields in the Rust struct that don’t match to any of the UDT fields.

If there are fields in UDT that are not present in Rust definition:

  • serialization will succeed in “match_by_name” flavor (default). Missing fields in the middle of UDT will be sent as NULLs, missing fields at the end will not be sent at all.
  • serialization will succed if suffix of UDT fields is missing. If there are missing fields in the middle it will fail. Note that if “skip_name_checks” is enabled, and the types happen to match, it is possible for serialization to succeed with unexpected result. This behavior is the default to support ALTERing UDTs by adding new fields. You can require exact match of fields using force_exact_match attribute.

In case of failure, either BuiltinTypeCheckError or BuiltinSerializationError will be returned.

§Example

A UDT defined like this:

CREATE TYPE ks.my_udt (a int, b text, c blob);

…can be serialized using the following struct:

#[derive(SerializeCql)]
struct MyUdt {
    a: i32,
    b: Option<String>,
    // No "c" field - it is not mandatory by default for all fields to be present
}

§Struct attributes

#[scylla(flavor = "flavor_name")]

Allows to choose one of the possible “flavors”, i.e. the way how the generated code will approach serialization. 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 fields in the UDT. During serialization, the implementation will take care to serialize the fields in the order which the database expects.
  • "enforce_order" - the generated implementation requires the fields in the Rust struct to be in the same order as the fields in the UDT. If the order is incorrect, type checking/serialization 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.

#[scylla(crate = crate_name)]

By default, the code generated by the derive macro will refer to the items defined by the driver (types, traits, etc.) via the ::scylla path. For example, it will refer to the SerializeCql trait using the following path:

use ::scylla::_macro_internal::SerializeCql;

Most users will simply add scylla to their dependencies, then use the derive macro and the path above will work. However, there are some niche cases where this path will not work:

  • The scylla crate is imported under a different name,
  • The scylla crate is not imported at all - the macro actually is defined in the scylla-macros crate and the generated code depends on items defined in scylla-cql.

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.

#[scylla(force_exact_match)]

Forces Rust struct to have all the fields present in UDT, otherwise serialization fails.

§Field attributes

#[scylla(rename = "name_in_the_udt")]

Serializes the field to the UDT struct field with given name instead of its Rust name.

#[scylla(skip)]

Don’t use the field during serialization.


Documentation for this macro can only be found in scylla crate - not in scylla-macros nor in scylla-cql. This is because of rustdocs limitations that are hard to explain here.