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

Add flatten attribute to derive SerializeRow #1144

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
219 changes: 219 additions & 0 deletions scylla-cql/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,223 @@ pub mod _macro_internal {
pub use crate::types::serialize::{
CellValueBuilder, CellWriter, RowWriter, SerializationError,
};

/// Represents a set of values that can be sent along a CQL statement when serializing by name
///
/// For now this trait is an implementation detail of `#[derive(SerializeRow)]` when
/// serializing by name
pub trait SerializeRowByName {
/// A type that can handle serialization of this struct column-by-column
type Partial<'d>: PartialSerializeRowByName
where
Self: 'd;

/// Returns a type that can serialize this row "column-by-column"
fn partial(&self) -> Self::Partial<'_>;
}

impl<T: SerializeRowByName + ?Sized> SerializeRowByName for &T {
type Partial<'d>
= T::Partial<'d>
where
Self: 'd;

fn partial(&self) -> Self::Partial<'_> {
<T as SerializeRowByName>::partial(self)
}
}

/// How to serialize a row column-by-column
///
/// For now this trait is an implementation detail of `#[derive(SerializeRow)]` when
/// serializing by name
pub trait PartialSerializeRowByName {
/// Serializes a single column in the row according to the information in the
/// given context
///
/// It returns whether the column finished the serialization of the struct, did
/// it partially, none of at all, or errored
fn serialize_field(
&mut self,
spec: &ColumnSpec,
writer: &mut RowWriter<'_>,
) -> Result<self::ser::row::FieldStatus, SerializationError>;

/// Checks if there are any missing columns to finish the serialization
fn check_missing(self) -> Result<(), SerializationError>;
}

pub trait SerializeRowInOrder {
fn serialize_in_order(
&self,
columns: &mut self::ser::row::ByColumn<'_, '_>,
writer: &mut RowWriter<'_>,
) -> Result<(), SerializationError>;
}

impl<T: SerializeRowInOrder + ?Sized> SerializeRowInOrder for &T {
fn serialize_in_order(
&self,
columns: &mut self::ser::row::ByColumn<'_, '_>,
writer: &mut RowWriter<'_>,
) -> Result<(), SerializationError> {
<T as SerializeRowInOrder>::serialize_in_order(self, columns, writer)
}
}

pub mod ser {
pub mod row {
use super::super::{
PartialSerializeRowByName, SerializeRowByName, SerializeRowInOrder,
};
use crate::{
frame::response::result::ColumnSpec,
types::serialize::{
row::{
BuiltinSerializationErrorKind, BuiltinTypeCheckErrorKind,
RowSerializationContext,
},
value::SerializeValue,
writers::WrittenCellProof,
RowWriter, SerializationError,
},
};

pub use crate::types::serialize::row::{mk_ser_err, mk_typck_err};

/// Whether a field used a column to finish its serialization or not
///
/// Used when serializing by name as a single column may not have finished a rust
/// field in the case of a flattened struct
///
/// For now this enum is an implementation detail of `#[derive(SerializeRow)]` when
/// serializing by name
#[derive(Debug)]
pub enum FieldStatus {
/// The column finished the serialization for this field
Done,
/// The column was used but there are other fields not yet serialized
NotDone,
/// The column did not belong to this field
NotUsed,
}

pub struct ByName<'t, T: SerializeRowByName>(pub &'t T);

impl<T: SerializeRowByName> ByName<'_, T> {
/// Serializes all the fields/columns by name
pub fn serialize(
self,
ctx: &RowSerializationContext,
writer: &mut RowWriter<'_>,
) -> Result<(), SerializationError> {
let mut partial = self.0.partial();

for spec in ctx.columns() {
let serialized = partial.serialize_field(spec, writer)?;

if matches!(serialized, FieldStatus::NotUsed) {
return Err(mk_typck_err::<Self>(
BuiltinTypeCheckErrorKind::NoColumnWithName {
name: spec.name().to_owned(),
},
));
}
}

partial.check_missing()
}
}

pub fn serialize_column<'b, T>(
value: &impl SerializeValue,
spec: &ColumnSpec,
writer: &'b mut RowWriter<'_>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
let sub_writer = writer.make_cell_writer();
value.serialize(spec.typ(), sub_writer).map_err(|err| {
super::row::mk_ser_err::<T>(
BuiltinSerializationErrorKind::ColumnSerializationFailed {
name: spec.name().to_owned(),
err,
},
)
})
}

pub struct ByColumn<'i, 'c> {
columns: std::slice::Iter<'i, ColumnSpec<'c>>,
}

impl ByColumn<'_, '_> {
pub fn next<'b, T>(
&mut self,
expected: &str,
value: &impl SerializeValue,
writer: &'b mut RowWriter<'_>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
let spec = self.columns.next().ok_or_else(|| {
mk_typck_err::<T>(BuiltinTypeCheckErrorKind::ValueMissingForColumn {
name: expected.to_owned(),
})
})?;

if spec.name() != expected {
return Err(mk_typck_err::<T>(
BuiltinTypeCheckErrorKind::ColumnNameMismatch {
rust_column_name: expected.to_owned(),
db_column_name: spec.name().to_owned(),
},
));
}

serialize_column::<T>(value, spec, writer)
}

pub fn next_skip_name<'b, T>(
&mut self,
expected: &str,
value: &impl SerializeValue,
writer: &'b mut RowWriter<'_>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
let spec = self.columns.next().ok_or_else(|| {
mk_typck_err::<T>(BuiltinTypeCheckErrorKind::ValueMissingForColumn {
name: expected.to_owned(),
})
})?;

serialize_column::<T>(value, spec, writer)
}

pub fn finish<T>(&mut self) -> Result<(), SerializationError> {
let Some(spec) = self.columns.next() else {
return Ok(());
};

Err(mk_typck_err::<T>(
BuiltinTypeCheckErrorKind::NoColumnWithName {
name: spec.name().to_owned(),
},
))
}
}

pub struct InOrder<'t, T: SerializeRowInOrder>(pub &'t T);

impl<T: SerializeRowInOrder> InOrder<'_, T> {
pub fn serialize(
self,
ctx: &RowSerializationContext,
writer: &mut RowWriter<'_>,
) -> Result<(), SerializationError> {
let mut next_serializer = ByColumn {
columns: ctx.columns().iter(),
};

self.0.serialize_in_order(&mut next_serializer, writer)?;
next_serializer.finish::<T>()
}
}
}
}
}
Loading
Loading