Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serialization refactor: macros for value/row serialization #851

Merged
merged 4 commits into from
Dec 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions Cargo.lock.msrv

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions scylla-cql/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,25 @@ pub mod _macro_internal {
SerializedResult, SerializedValues, Value, ValueList, ValueTooBig,
};
pub use crate::macros::*;

pub use crate::types::serialize::row::{
BuiltinSerializationError as BuiltinRowSerializationError,
BuiltinSerializationErrorKind as BuiltinRowSerializationErrorKind,
BuiltinTypeCheckError as BuiltinRowTypeCheckError,
BuiltinTypeCheckErrorKind as BuiltinRowTypeCheckErrorKind, RowSerializationContext,
SerializeRow,
};
pub use crate::types::serialize::value::{
BuiltinSerializationError as BuiltinTypeSerializationError,
BuiltinSerializationErrorKind as BuiltinTypeSerializationErrorKind,
BuiltinTypeCheckError as BuiltinTypeTypeCheckError,
BuiltinTypeCheckErrorKind as BuiltinTypeTypeCheckErrorKind, SerializeCql,
UdtSerializationErrorKind, UdtTypeCheckErrorKind,
};
pub use crate::types::serialize::writers::WrittenCellProof;
pub use crate::types::serialize::{
CellValueBuilder, CellWriter, RowWriter, SerializationError,
};

pub use crate::frame::response::result::ColumnType;
}
152 changes: 152 additions & 0 deletions scylla-cql/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,158 @@ pub use scylla_macros::IntoUserType;
/// #[derive(ValueList)] allows to pass struct as a list of values for a query
pub use scylla_macros::ValueList;

/// Derive macro for the [`SerializeCql`](crate::types::serialize::value::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 UDT that don't match
/// to any of the Rust struct fields, _or vice versa_.
///
/// In case of failure, either [`BuiltinTypeCheckError`](crate::types::serialize::value::BuiltinTypeCheckError)
/// or [`BuiltinSerializationError`](crate::types::serialize::value::BuiltinSerializationError)
/// will be returned.
///
/// # Example
///
/// A UDT defined like this:
///
/// ```notrust
/// CREATE TYPE ks.my_udt (a int, b text, c blob);
/// ```
///
/// ...can be serialized using the following struct:
///
/// ```rust
/// # use scylla_cql::macros::SerializeCql;
/// #[derive(SerializeCql)]
/// # #[scylla(crate = scylla_cql)]
/// struct MyUdt {
/// a: i32,
/// b: Option<String>,
/// c: Vec<u8>,
/// }
/// ```
///
/// # 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`](crate::types::serialize::value::SerializeCql) trait
/// using the following path:
///
/// ```rust,ignore
/// 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.
pub use scylla_macros::SerializeCql;

/// Derive macro for the [`SerializeRow`](crate::types::serialize::row::SerializeRow) trait
/// which serializes given Rust structure into bind markers for a CQL statement.
///
/// At the moment, only structs with named fields are supported.
///
/// Serialization will fail if there are some bind markers/columns in the statement
/// that don't match to any of the Rust struct fields, _or vice versa_.
///
/// In case of failure, either [`BuiltinTypeCheckError`](crate::types::serialize::row::BuiltinTypeCheckError)
/// or [`BuiltinSerializationError`](crate::types::serialize::row::BuiltinSerializationError)
/// will be returned.
///
/// # Example
///
/// A UDT defined like this:
/// Given a table and a query:
///
/// ```notrust
/// CREATE TABLE ks.my_t (a int PRIMARY KEY, b text, c blob);
/// INSERT INTO ks.my_t (a, b, c) VALUES (?, ?, ?);
/// ```
///
/// ...the values for the query can be serialized using the following struct:
///
/// ```rust
/// # use scylla_cql::macros::SerializeRow;
/// #[derive(SerializeRow)]
/// # #[scylla(crate = scylla_cql)]
/// struct MyValues {
/// a: i32,
/// b: Option<String>,
/// c: Vec<u8>,
/// }
/// ```
///
/// # 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
/// columns/bind markers. 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 columns/bind markers.
/// 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 [`SerializeRow`](crate::types::serialize::row::SerializeRow) trait
/// using the following path:
///
/// ```rust,ignore
/// use ::scylla::_macro_internal::SerializeRow;
/// ```
///
/// 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.
pub use scylla_macros::SerializeRow;

// Reexports for derive(IntoUserType)
pub use bytes::{BufMut, Bytes, BytesMut};

Expand Down
Loading