Derive Macro scylla::DeserializeRow

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

Derive macro for the DeserializeRow trait that generates an implementation which deserializes a row with a similar layout to the Rust struct.

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

This macro properly supports structs with lifetimes, meaning that you can deserialize columns that borrow memory from the serialized response.

§Example

Having a table defined like this:

CREATE TABLE ks.my_table (a PRIMARY KEY, b text, c blob);

results of a query “SELECT * FROM ks.my_table” or “SELECT a, b, c FROM ks.my_table” can be deserialized using the following struct:

#[derive(DeserializeRow)]
struct MyRow<'a> {
    a: i32,
    b: Option<String>,
    c: &'a [u8],
}

In general, the struct must match the queried names and types, not the table itself. For example, the query “SELECT a AS b FROM ks.my_table” executed against the aforementioned table can be deserialized to the struct:

#[derive(DeserializeRow)]
struct MyRow {
    b: i32,
}

§Attributes

The macro supports a number of attributes that customize the generated implementation. Many of the attributes were inspired by procedural macros from serde and try to follow the same naming conventions.

§Struct attributes

#[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 DeserializeValue trait using the following path:

use ::scylla::_macro_internal::DeserializeValue;

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(flavor = "flavor_name")]

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 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 column into the first field, second column into the second field and so on. It will still still verify that the column types and field types match.

§Field attributes

#[scylla(skip)]

The field will be completely ignored during deserialization and will be initialized with Default::default().

#[scylla(rename = "field_name")]

By default, the generated implementation will try to match the Rust field to a column with the same name. This attribute allows to match to a column with provided name.