use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::fmt::Display;
use std::hash::BuildHasher;
use std::net::IpAddr;
use std::ops::Deref as _;
use std::sync::Arc;
use bytes::Bytes;
use thiserror::Error;
use uuid::Uuid;
use crate::frame::response::result::{CollectionType, ColumnType, NativeType};
use crate::frame::types::{unsigned_vint_encode, vint_encode};
use crate::value::{
    Counter, CqlDate, CqlDecimal, CqlDecimalBorrowed, CqlDuration, CqlTime, CqlTimestamp,
    CqlTimeuuid, CqlValue, CqlVarint, CqlVarintBorrowed, MaybeUnset, Unset,
};
#[cfg(feature = "chrono-04")]
use crate::value::ValueOverflow;
use super::writers::WrittenCellProof;
use super::{CellValueBuilder, CellWriter, SerializationError};
pub trait SerializeValue {
                                                                fn serialize<'b>(
        &self,
        typ: &ColumnType,
        writer: CellWriter<'b>,
    ) -> Result<WrittenCellProof<'b>, SerializationError>;
}
macro_rules! exact_type_check {
    ($typ:ident, $($cql:tt),*) => {
        match $typ {
            $(ColumnType::Native(NativeType::$cql))|* => {},
            _ => return Err(mk_typck_err::<Self>(
                $typ,
                BuiltinTypeCheckErrorKind::MismatchedType {
                    expected: &[$(ColumnType::Native(NativeType::$cql)),*],
                }
            ))
        }
    };
}
macro_rules! impl_serialize_via_writer {
    (|$me:ident, $writer:ident| $e:expr) => {
        impl_serialize_via_writer!(|$me, _typ, $writer| $e);
    };
    (|$me:ident, $typ:ident, $writer:ident| $e:expr) => {
        fn serialize<'b>(
            &self,
            typ: &ColumnType,
            writer: CellWriter<'b>,
        ) -> Result<WrittenCellProof<'b>, SerializationError> {
            let $writer = writer;
            let $typ = typ;
            let $me = self;
            let proof = $e;
            Ok(proof)
        }
    };
}
impl SerializeValue for i8 {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, TinyInt);
        writer.set_value(me.to_be_bytes().as_slice()).unwrap()
    });
}
impl SerializeValue for i16 {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, SmallInt);
        writer.set_value(me.to_be_bytes().as_slice()).unwrap()
    });
}
impl SerializeValue for i32 {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Int);
        writer.set_value(me.to_be_bytes().as_slice()).unwrap()
    });
}
impl SerializeValue for i64 {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, BigInt);
        writer.set_value(me.to_be_bytes().as_slice()).unwrap()
    });
}
impl SerializeValue for CqlDecimal {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Decimal);
        let mut builder = writer.into_value_builder();
        let (bytes, scale) = me.as_signed_be_bytes_slice_and_exponent();
        builder.append_bytes(&scale.to_be_bytes());
        builder.append_bytes(bytes);
        builder
            .finish()
            .map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
    });
}
impl SerializeValue for CqlDecimalBorrowed<'_> {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Decimal);
        let mut builder = writer.into_value_builder();
        let (bytes, scale) = me.as_signed_be_bytes_slice_and_exponent();
        builder.append_bytes(&scale.to_be_bytes());
        builder.append_bytes(bytes);
        builder
            .finish()
            .map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
    });
}
#[cfg(feature = "bigdecimal-04")]
impl SerializeValue for bigdecimal_04::BigDecimal {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Decimal);
        let mut builder = writer.into_value_builder();
        let (value, scale) = me.as_bigint_and_exponent();
        let scale: i32 = scale
            .try_into()
            .map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::ValueOverflow))?;
        builder.append_bytes(&scale.to_be_bytes());
        builder.append_bytes(&value.to_signed_bytes_be());
        builder
            .finish()
            .map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
    });
}
impl SerializeValue for CqlDate {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Date);
        writer.set_value(me.0.to_be_bytes().as_slice()).unwrap()
    });
}
impl SerializeValue for CqlTimestamp {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Timestamp);
        writer.set_value(me.0.to_be_bytes().as_slice()).unwrap()
    });
}
impl SerializeValue for CqlTime {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Time);
        writer.set_value(me.0.to_be_bytes().as_slice()).unwrap()
    });
}
#[cfg(feature = "chrono-04")]
impl SerializeValue for chrono_04::NaiveDate {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Date);
        <CqlDate as SerializeValue>::serialize(&(*me).into(), typ, writer)?
    });
}
#[cfg(feature = "chrono-04")]
impl SerializeValue for chrono_04::DateTime<chrono_04::Utc> {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Timestamp);
        <CqlTimestamp as SerializeValue>::serialize(&(*me).into(), typ, writer)?
    });
}
#[cfg(feature = "chrono-04")]
impl SerializeValue for chrono_04::NaiveTime {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Time);
        let cql_time = CqlTime::try_from(*me).map_err(|_: ValueOverflow| {
            mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::ValueOverflow)
        })?;
        <CqlTime as SerializeValue>::serialize(&cql_time, typ, writer)?
    });
}
#[cfg(feature = "time-03")]
impl SerializeValue for time_03::Date {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Date);
        <CqlDate as SerializeValue>::serialize(&(*me).into(), typ, writer)?
    });
}
#[cfg(feature = "time-03")]
impl SerializeValue for time_03::OffsetDateTime {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Timestamp);
        <CqlTimestamp as SerializeValue>::serialize(&(*me).into(), typ, writer)?
    });
}
#[cfg(feature = "time-03")]
impl SerializeValue for time_03::Time {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Time);
        <CqlTime as SerializeValue>::serialize(&(*me).into(), typ, writer)?
    });
}
#[cfg(feature = "secrecy-08")]
impl<V: SerializeValue + secrecy_08::Zeroize> SerializeValue for secrecy_08::Secret<V> {
    fn serialize<'b>(
        &self,
        typ: &ColumnType,
        writer: CellWriter<'b>,
    ) -> Result<WrittenCellProof<'b>, SerializationError> {
        use secrecy_08::ExposeSecret;
        V::serialize(self.expose_secret(), typ, writer).map_err(fix_rust_name_in_err::<Self>)
    }
}
impl SerializeValue for bool {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Boolean);
        writer.set_value(&[*me as u8]).unwrap()
    });
}
impl SerializeValue for f32 {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Float);
        writer.set_value(me.to_be_bytes().as_slice()).unwrap()
    });
}
impl SerializeValue for f64 {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Double);
        writer.set_value(me.to_be_bytes().as_slice()).unwrap()
    });
}
impl SerializeValue for Uuid {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Uuid);
        writer.set_value(me.as_bytes().as_ref()).unwrap()
    });
}
impl SerializeValue for CqlTimeuuid {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Timeuuid);
        writer.set_value(me.as_bytes().as_ref()).unwrap()
    });
}
impl SerializeValue for CqlVarint {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Varint);
        writer
            .set_value(me.as_signed_bytes_be_slice())
            .map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
    });
}
impl SerializeValue for CqlVarintBorrowed<'_> {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Varint);
        writer
            .set_value(me.as_signed_bytes_be_slice())
            .map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
    });
}
#[cfg(feature = "num-bigint-03")]
impl SerializeValue for num_bigint_03::BigInt {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Varint);
                                writer
            .set_value(me.to_signed_bytes_be().as_slice())
            .map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
    });
}
#[cfg(feature = "num-bigint-04")]
impl SerializeValue for num_bigint_04::BigInt {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Varint);
                writer
            .set_value(me.to_signed_bytes_be().as_slice())
            .map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
    });
}
impl SerializeValue for str {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Ascii, Text);
        writer
            .set_value(me.as_bytes())
            .map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
    });
}
impl SerializeValue for Vec<u8> {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Blob);
        writer
            .set_value(me.as_ref())
            .map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
    });
}
impl SerializeValue for &[u8] {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Blob);
        writer
            .set_value(me)
            .map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
    });
}
impl<const N: usize> SerializeValue for [u8; N] {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Blob);
        writer
            .set_value(me.as_ref())
            .map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
    });
}
impl SerializeValue for Bytes {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Blob);
        writer
            .set_value(me)
            .map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
    });
}
impl SerializeValue for IpAddr {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Inet);
        match me {
            IpAddr::V4(ip) => writer.set_value(&ip.octets()).unwrap(),
            IpAddr::V6(ip) => writer.set_value(&ip.octets()).unwrap(),
        }
    });
}
impl SerializeValue for String {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Ascii, Text);
        writer
            .set_value(me.as_bytes())
            .map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))?
    });
}
impl<T: SerializeValue> SerializeValue for Option<T> {
    fn serialize<'b>(
        &self,
        typ: &ColumnType,
        writer: CellWriter<'b>,
    ) -> Result<WrittenCellProof<'b>, SerializationError> {
        match self {
            Some(v) => v
                .serialize(typ, writer)
                .map_err(fix_rust_name_in_err::<Self>),
            None => Ok(writer.set_null()),
        }
    }
}
impl SerializeValue for Unset {
    impl_serialize_via_writer!(|_me, writer| writer.set_unset());
}
impl SerializeValue for Counter {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Counter);
        writer.set_value(me.0.to_be_bytes().as_slice()).unwrap()
    });
}
impl SerializeValue for CqlDuration {
    impl_serialize_via_writer!(|me, typ, writer| {
        exact_type_check!(typ, Duration);
                let mut buf = Vec::with_capacity(27);         vint_encode(me.months as i64, &mut buf);
        vint_encode(me.days as i64, &mut buf);
        vint_encode(me.nanoseconds, &mut buf);
        writer.set_value(buf.as_slice()).unwrap()
    });
}
impl<V: SerializeValue> SerializeValue for MaybeUnset<V> {
    fn serialize<'b>(
        &self,
        typ: &ColumnType,
        writer: CellWriter<'b>,
    ) -> Result<WrittenCellProof<'b>, SerializationError> {
        match self {
            MaybeUnset::Set(v) => v
                .serialize(typ, writer)
                .map_err(fix_rust_name_in_err::<Self>),
            MaybeUnset::Unset => Ok(writer.set_unset()),
        }
    }
}
impl<T: SerializeValue + ?Sized> SerializeValue for &T {
    fn serialize<'b>(
        &self,
        typ: &ColumnType,
        writer: CellWriter<'b>,
    ) -> Result<WrittenCellProof<'b>, SerializationError> {
        T::serialize(*self, typ, writer).map_err(fix_rust_name_in_err::<Self>)
    }
}
impl<T: SerializeValue + ?Sized> SerializeValue for Box<T> {
    fn serialize<'b>(
        &self,
        typ: &ColumnType,
        writer: CellWriter<'b>,
    ) -> Result<WrittenCellProof<'b>, SerializationError> {
        T::serialize(&**self, typ, writer).map_err(fix_rust_name_in_err::<Self>)
    }
}
impl<T: SerializeValue + ?Sized> SerializeValue for Arc<T> {
    fn serialize<'b>(
        &self,
        typ: &ColumnType,
        writer: CellWriter<'b>,
    ) -> Result<WrittenCellProof<'b>, SerializationError> {
        T::serialize(&**self, typ, writer).map_err(fix_rust_name_in_err::<Self>)
    }
}
impl<V: SerializeValue, S: BuildHasher + Default> SerializeValue for HashSet<V, S> {
    fn serialize<'b>(
        &self,
        typ: &ColumnType,
        writer: CellWriter<'b>,
    ) -> Result<WrittenCellProof<'b>, SerializationError> {
        serialize_sequence(
            std::any::type_name::<Self>(),
            self.len(),
            self.iter(),
            typ,
            writer,
        )
    }
}
impl<K: SerializeValue, V: SerializeValue, S: BuildHasher> SerializeValue for HashMap<K, V, S> {
    fn serialize<'b>(
        &self,
        typ: &ColumnType,
        writer: CellWriter<'b>,
    ) -> Result<WrittenCellProof<'b>, SerializationError> {
        serialize_mapping(
            std::any::type_name::<Self>(),
            self.len(),
            self.iter(),
            typ,
            writer,
        )
    }
}
impl<V: SerializeValue> SerializeValue for BTreeSet<V> {
    fn serialize<'b>(
        &self,
        typ: &ColumnType,
        writer: CellWriter<'b>,
    ) -> Result<WrittenCellProof<'b>, SerializationError> {
        serialize_sequence(
            std::any::type_name::<Self>(),
            self.len(),
            self.iter(),
            typ,
            writer,
        )
    }
}
impl<K: SerializeValue, V: SerializeValue> SerializeValue for BTreeMap<K, V> {
    fn serialize<'b>(
        &self,
        typ: &ColumnType,
        writer: CellWriter<'b>,
    ) -> Result<WrittenCellProof<'b>, SerializationError> {
        serialize_mapping(
            std::any::type_name::<Self>(),
            self.len(),
            self.iter(),
            typ,
            writer,
        )
    }
}
impl<T: SerializeValue> SerializeValue for Vec<T> {
    fn serialize<'b>(
        &self,
        typ: &ColumnType,
        writer: CellWriter<'b>,
    ) -> Result<WrittenCellProof<'b>, SerializationError> {
        match typ {
            ColumnType::Collection {
                typ: CollectionType::List(_) | CollectionType::Set(_),
                ..
            } => serialize_sequence(
                std::any::type_name::<Self>(),
                self.len(),
                self.iter(),
                typ,
                writer,
            ),
            ColumnType::Vector {
                typ: element_type,
                dimensions,
            } => serialize_vector(
                std::any::type_name::<Self>(),
                self.len(),
                self.iter(),
                element_type,
                *dimensions,
                typ,
                writer,
            ),
            _ => Err(mk_typck_err_named(
                std::any::type_name::<Self>(),
                typ,
                SetOrListTypeCheckErrorKind::NotSetOrList,
            )),
        }
    }
}
impl<'a, T: SerializeValue + 'a> SerializeValue for &'a [T] {
    fn serialize<'b>(
        &self,
        typ: &ColumnType,
        writer: CellWriter<'b>,
    ) -> Result<WrittenCellProof<'b>, SerializationError> {
        match typ {
            ColumnType::Collection {
                typ: CollectionType::List(_) | CollectionType::Set(_),
                ..
            } => serialize_sequence(
                std::any::type_name::<Self>(),
                self.len(),
                self.iter(),
                typ,
                writer,
            ),
            ColumnType::Vector {
                typ: element_type,
                dimensions,
            } => serialize_vector(
                std::any::type_name::<Self>(),
                self.len(),
                self.iter(),
                element_type,
                *dimensions,
                typ,
                writer,
            ),
            _ => Err(mk_typck_err_named(
                std::any::type_name::<Self>(),
                typ,
                SetOrListTypeCheckErrorKind::NotSetOrList,
            )),
        }
    }
}
impl SerializeValue for CqlValue {
    fn serialize<'b>(
        &self,
        typ: &ColumnType,
        writer: CellWriter<'b>,
    ) -> Result<WrittenCellProof<'b>, SerializationError> {
        serialize_cql_value(self, typ, writer).map_err(fix_rust_name_in_err::<Self>)
    }
}
fn serialize_cql_value<'b>(
    value: &CqlValue,
    typ: &ColumnType,
    writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
    match value {
        CqlValue::Ascii(a) => <_ as SerializeValue>::serialize(&a, typ, writer),
        CqlValue::Boolean(b) => <_ as SerializeValue>::serialize(&b, typ, writer),
        CqlValue::Blob(b) => <_ as SerializeValue>::serialize(&b, typ, writer),
        CqlValue::Counter(c) => <_ as SerializeValue>::serialize(&c, typ, writer),
        CqlValue::Decimal(d) => <_ as SerializeValue>::serialize(&d, typ, writer),
        CqlValue::Date(d) => <_ as SerializeValue>::serialize(&d, typ, writer),
        CqlValue::Double(d) => <_ as SerializeValue>::serialize(&d, typ, writer),
        CqlValue::Duration(d) => <_ as SerializeValue>::serialize(&d, typ, writer),
        CqlValue::Empty => {
            if !typ.supports_special_empty_value() {
                return Err(mk_typck_err::<CqlValue>(
                    typ,
                    BuiltinTypeCheckErrorKind::NotEmptyable,
                ));
            }
            Ok(writer.set_value(&[]).unwrap())
        }
        CqlValue::Float(f) => <_ as SerializeValue>::serialize(&f, typ, writer),
        CqlValue::Int(i) => <_ as SerializeValue>::serialize(&i, typ, writer),
        CqlValue::BigInt(b) => <_ as SerializeValue>::serialize(&b, typ, writer),
        CqlValue::Text(t) => <_ as SerializeValue>::serialize(&t, typ, writer),
        CqlValue::Timestamp(t) => <_ as SerializeValue>::serialize(&t, typ, writer),
        CqlValue::Inet(i) => <_ as SerializeValue>::serialize(&i, typ, writer),
        CqlValue::List(l) => <_ as SerializeValue>::serialize(&l, typ, writer),
        CqlValue::Map(m) => serialize_mapping(
            std::any::type_name::<CqlValue>(),
            m.len(),
            m.iter().map(|p| (&p.0, &p.1)),
            typ,
            writer,
        ),
        CqlValue::Set(s) => <_ as SerializeValue>::serialize(&s, typ, writer),
        CqlValue::UserDefinedType {
            keyspace,
            name: type_name,
            fields,
        } => serialize_udt(typ, keyspace, type_name, fields, writer),
        CqlValue::SmallInt(s) => <_ as SerializeValue>::serialize(&s, typ, writer),
        CqlValue::TinyInt(t) => <_ as SerializeValue>::serialize(&t, typ, writer),
        CqlValue::Time(t) => <_ as SerializeValue>::serialize(&t, typ, writer),
        CqlValue::Timeuuid(t) => <_ as SerializeValue>::serialize(&t, typ, writer),
        CqlValue::Tuple(t) => {
                                    let fields = match typ {
                ColumnType::Tuple(fields) => {
                    if fields.len() < t.len() {
                        return Err(mk_typck_err::<CqlValue>(
                            typ,
                            TupleTypeCheckErrorKind::WrongElementCount {
                                rust_type_el_count: t.len(),
                                cql_type_el_count: fields.len(),
                            },
                        ));
                    }
                    fields
                }
                _ => {
                    return Err(mk_typck_err::<CqlValue>(
                        typ,
                        TupleTypeCheckErrorKind::NotTuple,
                    ))
                }
            };
            serialize_tuple_like(typ, fields.iter(), t.iter(), writer)
        }
        CqlValue::Uuid(u) => <_ as SerializeValue>::serialize(&u, typ, writer),
        CqlValue::Varint(v) => <_ as SerializeValue>::serialize(&v, typ, writer),
        CqlValue::Vector(v) => <_ as SerializeValue>::serialize(&v, typ, writer),
    }
}
fn fix_rust_name_in_err<RustT>(mut err: SerializationError) -> SerializationError {
                
    let rust_name = std::any::type_name::<RustT>();
    match Arc::get_mut(&mut err.0) {
        Some(err_mut) => {
            if let Some(err) = err_mut.downcast_mut::<BuiltinTypeCheckError>() {
                err.rust_name = rust_name;
            } else if let Some(err) = err_mut.downcast_mut::<BuiltinSerializationError>() {
                err.rust_name = rust_name;
            }
        }
        None => {
                                                if let Some(err) = err.0.downcast_ref::<BuiltinTypeCheckError>() {
                if err.rust_name != rust_name {
                    return SerializationError::new(BuiltinTypeCheckError {
                        rust_name,
                        ..err.clone()
                    });
                }
            }
            if let Some(err) = err.0.downcast_ref::<BuiltinSerializationError>() {
                if err.rust_name != rust_name {
                    return SerializationError::new(BuiltinSerializationError {
                        rust_name,
                        ..err.clone()
                    });
                }
            }
        }
    };
    err
}
fn serialize_udt<'b>(
    typ: &ColumnType,
    keyspace: &str,
    type_name: &str,
    values: &[(String, Option<CqlValue>)],
    writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
    let (dst_type_name, dst_keyspace, field_types) = match typ {
        ColumnType::UserDefinedType {
            definition: udt, ..
        } => (&udt.name, &udt.keyspace, &udt.field_types),
        _ => return Err(mk_typck_err::<CqlValue>(typ, UdtTypeCheckErrorKind::NotUdt)),
    };
    if keyspace != dst_keyspace || type_name != dst_type_name {
        return Err(mk_typck_err::<CqlValue>(
            typ,
            UdtTypeCheckErrorKind::NameMismatch {
                keyspace: dst_keyspace.clone().into_owned(),
                type_name: dst_type_name.clone().into_owned(),
            },
        ));
    }
            let mut indexed_fields: HashMap<_, _> = values.iter().map(|(k, v)| (k.as_str(), v)).collect();
    let mut builder = writer.into_value_builder();
    for (fname, ftyp) in field_types {
                        let fvalue = indexed_fields
            .remove(fname.deref())
            .and_then(|x| x.as_ref());
        let writer = builder.make_sub_writer();
        match fvalue {
            None => writer.set_null(),
            Some(v) => serialize_cql_value(v, ftyp, writer).map_err(|err| {
                let err = fix_rust_name_in_err::<CqlValue>(err);
                mk_ser_err::<CqlValue>(
                    typ,
                    UdtSerializationErrorKind::FieldSerializationFailed {
                        field_name: fname.clone().into_owned(),
                        err,
                    },
                )
            })?,
        };
    }
        if !indexed_fields.is_empty() {
                        let fname = indexed_fields.keys().min().unwrap();
        return Err(mk_typck_err::<CqlValue>(
            typ,
            UdtTypeCheckErrorKind::NoSuchFieldInUdt {
                field_name: fname.to_string(),
            },
        ));
    }
    builder
        .finish()
        .map_err(|_| mk_ser_err::<CqlValue>(typ, BuiltinSerializationErrorKind::SizeOverflow))
}
fn serialize_tuple_like<'t, 'b>(
    typ: &ColumnType,
    field_types: impl Iterator<Item = &'t ColumnType<'t>>,
    field_values: impl Iterator<Item = &'t Option<CqlValue>>,
    writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
    let mut builder = writer.into_value_builder();
    for (index, (el, el_typ)) in field_values.zip(field_types).enumerate() {
        let sub = builder.make_sub_writer();
        match el {
            None => sub.set_null(),
            Some(el) => serialize_cql_value(el, el_typ, sub).map_err(|err| {
                let err = fix_rust_name_in_err::<CqlValue>(err);
                mk_ser_err::<CqlValue>(
                    typ,
                    TupleSerializationErrorKind::ElementSerializationFailed { index, err },
                )
            })?,
        };
    }
    builder
        .finish()
        .map_err(|_| mk_ser_err::<CqlValue>(typ, BuiltinSerializationErrorKind::SizeOverflow))
}
macro_rules! impl_tuple {
    (
        $($typs:ident),*;
        $($fidents:ident),*;
        $($tidents:ident),*;
        $length:expr
    ) => {
        impl<$($typs: SerializeValue),*> SerializeValue for ($($typs,)*) {
            fn serialize<'b>(
                &self,
                typ: &ColumnType,
                writer: CellWriter<'b>,
            ) -> Result<WrittenCellProof<'b>, SerializationError> {
                let ($($tidents,)*) = match typ {
                    ColumnType::Tuple(typs) => match typs.as_slice() {
                        [$($tidents),*] => ($($tidents,)*),
                        _ => return Err(mk_typck_err::<Self>(
                            typ,
                            TupleTypeCheckErrorKind::WrongElementCount {
                                rust_type_el_count: $length,
                                cql_type_el_count: typs.len(),
                            }
                        ))
                    }
                    _ => return Err(mk_typck_err::<Self>(
                        typ,
                        TupleTypeCheckErrorKind::NotTuple,
                    ))
                };
                let ($($fidents,)*) = self;
                let mut builder = writer.into_value_builder();
                let index = 0;
                $(
                    <$typs as SerializeValue>::serialize($fidents, $tidents, builder.make_sub_writer())
                        .map_err(|err| mk_ser_err::<Self>(
                            typ,
                            TupleSerializationErrorKind::ElementSerializationFailed {
                                index,
                                err,
                            }
                        ))?;
                    let index = index + 1;
                )*
                let _ = index;
                builder
                    .finish()
                    .map_err(|_| mk_ser_err::<Self>(typ, BuiltinSerializationErrorKind::SizeOverflow))
            }
        }
    };
}
macro_rules! impl_tuples {
    (;;;$length:expr) => {};
    (
        $typ:ident$(, $($typs:ident),*)?;
        $fident:ident$(, $($fidents:ident),*)?;
        $tident:ident$(, $($tidents:ident),*)?;
        $length:expr
    ) => {
        impl_tuples!(
            $($($typs),*)?;
            $($($fidents),*)?;
            $($($tidents),*)?;
            $length - 1
        );
        impl_tuple!(
            $typ$(, $($typs),*)?;
            $fident$(, $($fidents),*)?;
            $tident$(, $($tidents),*)?;
            $length
        );
    };
}
impl_tuples!(
    T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15;
    f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15;
    t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15;
    16
);
fn serialize_sequence<'t, 'b, T: SerializeValue + 't>(
    rust_name: &'static str,
    len: usize,
    iter: impl Iterator<Item = &'t T>,
    typ: &ColumnType,
    writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
    let elt = match typ {
        ColumnType::Collection {
            frozen: false,
            typ: CollectionType::List(elt),
        }
        | ColumnType::Collection {
            frozen: false,
            typ: CollectionType::Set(elt),
        } => elt,
        _ => {
            return Err(mk_typck_err_named(
                rust_name,
                typ,
                SetOrListTypeCheckErrorKind::NotSetOrList,
            ));
        }
    };
    let mut builder = writer.into_value_builder();
    let element_count: i32 = len.try_into().map_err(|_| {
        mk_ser_err_named(
            rust_name,
            typ,
            SetOrListSerializationErrorKind::TooManyElements,
        )
    })?;
    builder.append_bytes(&element_count.to_be_bytes());
    for el in iter {
        T::serialize(el, elt, builder.make_sub_writer()).map_err(|err| {
            mk_ser_err_named(
                rust_name,
                typ,
                SetOrListSerializationErrorKind::ElementSerializationFailed(err),
            )
        })?;
    }
    builder
        .finish()
        .map_err(|_| mk_ser_err_named(rust_name, typ, BuiltinSerializationErrorKind::SizeOverflow))
}
fn serialize_next_constant_length_elem<'t, T: SerializeValue + 't>(
    rust_name: &'static str,
    element_type: &ColumnType,
    typ: &ColumnType,
    builder: &mut CellValueBuilder,
    element: &'t T,
) -> Result<(), SerializationError> {
    T::serialize(
        element,
        element_type,
        builder.make_sub_writer_without_size(),
    )
    .map_err(|err| {
        mk_ser_err_named(
            rust_name,
            typ,
            VectorSerializationErrorKind::ElementSerializationFailed(err),
        )
    })?;
    Ok(())
}
fn serialize_next_variable_length_elem<'t, T: SerializeValue + 't>(
    rust_name: &'static str,
    element_type: &ColumnType,
    typ: &ColumnType,
    builder: &mut CellValueBuilder,
    element: &'t T,
) -> Result<(), SerializationError> {
    let mut element_buffer = Vec::new();
    let inner_writer = CellWriter::new_without_size(&mut element_buffer);
    T::serialize(element, element_type, inner_writer).map_err(|err| {
        mk_ser_err_named(
            rust_name,
            typ,
            VectorSerializationErrorKind::ElementSerializationFailed(err),
        )
    })?;
    let mut element_length_buffer = Vec::new();
    unsigned_vint_encode(
        element_buffer.len().try_into().unwrap(),
        &mut element_length_buffer,
    );
    builder.append_bytes(element_length_buffer.as_slice());
    builder.append_bytes(element_buffer.as_slice());
    Ok(())
}
fn serialize_vector<'t, 'b, T: SerializeValue + 't>(
    rust_name: &'static str,
    len: usize,
    iter: impl Iterator<Item = &'t T>,
    element_type: &ColumnType,
    dimensions: u16,
    typ: &ColumnType,
    writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
    if len != dimensions as usize {
        return Err(mk_ser_err_named(
            rust_name,
            typ,
            VectorSerializationErrorKind::InvalidNumberOfElements(len, dimensions),
        ));
    }
    let mut builder = writer.into_value_builder();
    match element_type.type_size() {
        Some(_) => {
            for element in iter {
                serialize_next_constant_length_elem(
                    rust_name,
                    element_type,
                    typ,
                    &mut builder,
                    element,
                )?;
            }
        }
        None => {
            for element in iter {
                serialize_next_variable_length_elem(
                    rust_name,
                    element_type,
                    typ,
                    &mut builder,
                    element,
                )?;
            }
        }
    }
    builder
        .finish()
        .map_err(|_| mk_ser_err_named(rust_name, typ, BuiltinSerializationErrorKind::SizeOverflow))
}
fn serialize_mapping<'t, 'b, K: SerializeValue + 't, V: SerializeValue + 't>(
    rust_name: &'static str,
    len: usize,
    iter: impl Iterator<Item = (&'t K, &'t V)>,
    typ: &ColumnType,
    writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
    let (ktyp, vtyp) = match typ {
        ColumnType::Collection {
            frozen: false,
            typ: CollectionType::Map(k, v),
        } => (k, v),
        _ => {
            return Err(mk_typck_err_named(
                rust_name,
                typ,
                MapTypeCheckErrorKind::NotMap,
            ));
        }
    };
    let mut builder = writer.into_value_builder();
    let element_count: i32 = len.try_into().map_err(|_| {
        mk_ser_err_named(rust_name, typ, MapSerializationErrorKind::TooManyElements)
    })?;
    builder.append_bytes(&element_count.to_be_bytes());
    for (k, v) in iter {
        K::serialize(k, ktyp, builder.make_sub_writer()).map_err(|err| {
            mk_ser_err_named(
                rust_name,
                typ,
                MapSerializationErrorKind::KeySerializationFailed(err),
            )
        })?;
        V::serialize(v, vtyp, builder.make_sub_writer()).map_err(|err| {
            mk_ser_err_named(
                rust_name,
                typ,
                MapSerializationErrorKind::ValueSerializationFailed(err),
            )
        })?;
    }
    builder
        .finish()
        .map_err(|_| mk_ser_err_named(rust_name, typ, BuiltinSerializationErrorKind::SizeOverflow))
}
#[derive(Debug, Error, Clone)]
#[error("Failed to type check Rust type {rust_name} against CQL type {got:?}: {kind}")]
pub struct BuiltinTypeCheckError {
        pub rust_name: &'static str,
        pub got: ColumnType<'static>,
        pub kind: BuiltinTypeCheckErrorKind,
}
fn mk_typck_err<T: ?Sized>(
    got: &ColumnType,
    kind: impl Into<BuiltinTypeCheckErrorKind>,
) -> SerializationError {
    mk_typck_err_named(std::any::type_name::<T>(), got, kind)
}
fn mk_typck_err_named(
    name: &'static str,
    got: &ColumnType,
    kind: impl Into<BuiltinTypeCheckErrorKind>,
) -> SerializationError {
    SerializationError::new(BuiltinTypeCheckError {
        rust_name: name,
        got: got.clone().into_owned(),
        kind: kind.into(),
    })
}
#[derive(Debug, Error, Clone)]
#[error("Failed to serialize Rust type {rust_name} into CQL type {got:?}: {kind}")]
pub struct BuiltinSerializationError {
        pub rust_name: &'static str,
        pub got: ColumnType<'static>,
        pub kind: BuiltinSerializationErrorKind,
}
pub(crate) fn mk_ser_err<T: ?Sized>(
    got: &ColumnType,
    kind: impl Into<BuiltinSerializationErrorKind>,
) -> SerializationError {
    mk_ser_err_named(std::any::type_name::<T>(), got, kind)
}
fn mk_ser_err_named(
    name: &'static str,
    got: &ColumnType,
    kind: impl Into<BuiltinSerializationErrorKind>,
) -> SerializationError {
    SerializationError::new(BuiltinSerializationError {
        rust_name: name,
        got: got.clone().into_owned(),
        kind: kind.into(),
    })
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum BuiltinTypeCheckErrorKind {
        MismatchedType {
                expected: &'static [ColumnType<'static>],
    },
        NotEmptyable,
        SetOrListError(SetOrListTypeCheckErrorKind),
        MapError(MapTypeCheckErrorKind),
        TupleError(TupleTypeCheckErrorKind),
        UdtError(UdtTypeCheckErrorKind),
}
impl From<SetOrListTypeCheckErrorKind> for BuiltinTypeCheckErrorKind {
    fn from(value: SetOrListTypeCheckErrorKind) -> Self {
        BuiltinTypeCheckErrorKind::SetOrListError(value)
    }
}
impl From<MapTypeCheckErrorKind> for BuiltinTypeCheckErrorKind {
    fn from(value: MapTypeCheckErrorKind) -> Self {
        BuiltinTypeCheckErrorKind::MapError(value)
    }
}
impl From<TupleTypeCheckErrorKind> for BuiltinTypeCheckErrorKind {
    fn from(value: TupleTypeCheckErrorKind) -> Self {
        BuiltinTypeCheckErrorKind::TupleError(value)
    }
}
impl From<UdtTypeCheckErrorKind> for BuiltinTypeCheckErrorKind {
    fn from(value: UdtTypeCheckErrorKind) -> Self {
        BuiltinTypeCheckErrorKind::UdtError(value)
    }
}
impl Display for BuiltinTypeCheckErrorKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            BuiltinTypeCheckErrorKind::MismatchedType { expected } => {
                write!(f, "expected one of the CQL types: {expected:?}")
            }
            BuiltinTypeCheckErrorKind::NotEmptyable => {
                f.write_str("the separate empty representation is not valid for this type")
            }
            BuiltinTypeCheckErrorKind::SetOrListError(err) => err.fmt(f),
            BuiltinTypeCheckErrorKind::MapError(err) => err.fmt(f),
            BuiltinTypeCheckErrorKind::TupleError(err) => err.fmt(f),
            BuiltinTypeCheckErrorKind::UdtError(err) => err.fmt(f),
        }
    }
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum BuiltinSerializationErrorKind {
            SizeOverflow,
        ValueOverflow,
        SetOrListError(SetOrListSerializationErrorKind),
        VectorError(VectorSerializationErrorKind),
        MapError(MapSerializationErrorKind),
        TupleError(TupleSerializationErrorKind),
        UdtError(UdtSerializationErrorKind),
}
impl From<SetOrListSerializationErrorKind> for BuiltinSerializationErrorKind {
    fn from(value: SetOrListSerializationErrorKind) -> Self {
        BuiltinSerializationErrorKind::SetOrListError(value)
    }
}
impl From<VectorSerializationErrorKind> for BuiltinSerializationErrorKind {
    fn from(value: VectorSerializationErrorKind) -> Self {
        BuiltinSerializationErrorKind::VectorError(value)
    }
}
impl From<MapSerializationErrorKind> for BuiltinSerializationErrorKind {
    fn from(value: MapSerializationErrorKind) -> Self {
        BuiltinSerializationErrorKind::MapError(value)
    }
}
impl From<TupleSerializationErrorKind> for BuiltinSerializationErrorKind {
    fn from(value: TupleSerializationErrorKind) -> Self {
        BuiltinSerializationErrorKind::TupleError(value)
    }
}
impl From<UdtSerializationErrorKind> for BuiltinSerializationErrorKind {
    fn from(value: UdtSerializationErrorKind) -> Self {
        BuiltinSerializationErrorKind::UdtError(value)
    }
}
impl Display for BuiltinSerializationErrorKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            BuiltinSerializationErrorKind::SizeOverflow => {
                f.write_str("the Rust value is too big to be serialized in the CQL protocol format")
            }
            BuiltinSerializationErrorKind::ValueOverflow => {
                f.write_str("the Rust value is out of range supported by the CQL type")
            }
            BuiltinSerializationErrorKind::SetOrListError(err) => err.fmt(f),
            BuiltinSerializationErrorKind::VectorError(err) => err.fmt(f),
            BuiltinSerializationErrorKind::MapError(err) => err.fmt(f),
            BuiltinSerializationErrorKind::TupleError(err) => err.fmt(f),
            BuiltinSerializationErrorKind::UdtError(err) => err.fmt(f),
        }
    }
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum MapTypeCheckErrorKind {
        NotMap,
}
impl Display for MapTypeCheckErrorKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            MapTypeCheckErrorKind::NotMap => f.write_str(
                "the CQL type the Rust type was attempted to be type checked against was not a map",
            ),
        }
    }
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum MapSerializationErrorKind {
        TooManyElements,
        KeySerializationFailed(SerializationError),
        ValueSerializationFailed(SerializationError),
}
impl Display for MapSerializationErrorKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            MapSerializationErrorKind::TooManyElements => {
                f.write_str("the map contains too many elements to fit in CQL representation")
            }
            MapSerializationErrorKind::KeySerializationFailed(err) => {
                write!(f, "failed to serialize one of the keys: {err}")
            }
            MapSerializationErrorKind::ValueSerializationFailed(err) => {
                write!(f, "failed to serialize one of the values: {err}")
            }
        }
    }
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum SetOrListTypeCheckErrorKind {
        NotSetOrList,
}
impl Display for SetOrListTypeCheckErrorKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            SetOrListTypeCheckErrorKind::NotSetOrList => {
                f.write_str("the CQL type the Rust type was attempted to be type checked against was neither a set, nor a list, nor a vector")
            }
        }
    }
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum SetOrListSerializationErrorKind {
        TooManyElements,
        ElementSerializationFailed(SerializationError),
}
impl Display for SetOrListSerializationErrorKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            SetOrListSerializationErrorKind::TooManyElements => f.write_str(
                "the collection contains too many elements to fit in CQL representation",
            ),
            SetOrListSerializationErrorKind::ElementSerializationFailed(err) => {
                write!(f, "failed to serialize one of the elements: {err}")
            }
        }
    }
}
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum VectorSerializationErrorKind {
            #[error(
        "number of vector elements ({0}) does not match the number of declared dimensions ({1})"
    )]
    InvalidNumberOfElements(usize, u16),
        #[error("failed to serialize one of the elements: {0}")]
    ElementSerializationFailed(SerializationError),
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum TupleTypeCheckErrorKind {
        NotTuple,
                        WrongElementCount {
                rust_type_el_count: usize,
                cql_type_el_count: usize,
    },
}
impl Display for TupleTypeCheckErrorKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            TupleTypeCheckErrorKind::NotTuple => f.write_str(
                "the CQL type the Rust type was attempted to be type checked against is not a tuple"
            ),
            TupleTypeCheckErrorKind::WrongElementCount { rust_type_el_count, cql_type_el_count } => write!(
                f,
                "wrong tuple element count: CQL type has {cql_type_el_count}, the Rust tuple has {rust_type_el_count}"
            ),
        }
    }
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum TupleSerializationErrorKind {
        ElementSerializationFailed {
                index: usize,
                err: SerializationError,
    },
}
impl Display for TupleSerializationErrorKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            TupleSerializationErrorKind::ElementSerializationFailed { index, err } => {
                write!(f, "element no. {index} failed to serialize: {err}")
            }
        }
    }
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum UdtTypeCheckErrorKind {
        NotUdt,
        NameMismatch {
                keyspace: String,
                type_name: String,
    },
        ValueMissingForUdtField {
                field_name: String,
    },
        NoSuchFieldInUdt {
                field_name: String,
    },
        FieldNameMismatch {
                rust_field_name: String,
                db_field_name: String,
    },
}
impl Display for UdtTypeCheckErrorKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            UdtTypeCheckErrorKind::NotUdt => f.write_str("the CQL type the Rust type was attempted to be type checked against is not a UDT"),
            UdtTypeCheckErrorKind::NameMismatch {
                keyspace,
                type_name,
            } => write!(
                f,
                "the Rust UDT name does not match the actual CQL UDT name ({keyspace}.{type_name})"
            ),
            UdtTypeCheckErrorKind::ValueMissingForUdtField { field_name } => {
                write!(f, "the field {field_name} is missing in the Rust data but is required by the CQL UDT type")
            }
            UdtTypeCheckErrorKind::NoSuchFieldInUdt { field_name } => write!(
                f,
                "the field {field_name} that is present in the Rust data is not present in the CQL type"
            ),
            UdtTypeCheckErrorKind::FieldNameMismatch { rust_field_name, db_field_name } => write!(
                f,
                "expected field with name {db_field_name} at given position, but the Rust field name is {rust_field_name}"
            ),
        }
    }
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum UdtSerializationErrorKind {
        FieldSerializationFailed {
                field_name: String,
                err: SerializationError,
    },
}
impl Display for UdtSerializationErrorKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            UdtSerializationErrorKind::FieldSerializationFailed { field_name, err } => {
                write!(f, "field {field_name} failed to serialize: {err}")
            }
        }
    }
}
mod doctests {
                            fn _test_udt_bad_attributes_skip_name_check_requires_enforce_order() {}
                                        fn _test_udt_bad_attributes_skip_name_check_conflicts_with_rename() {}
                                            fn _test_udt_bad_attributes_rename_collision_with_field() {}
                                                fn _test_udt_bad_attributes_rename_collision_with_another_rename() {}
                                                fn _test_udt_bad_attributes_name_skip_name_checks_limitations_on_allow_missing() {}
                                                    fn _test_udt_good_attributes_name_skip_name_checks_limitations_on_allow_missing() {}
                                            fn _test_udt_unordered_flavour_no_limitations_on_allow_missing() {}
                                            fn _test_udt_default_when_null_is_accepted() {}
}
#[cfg(test)]
#[path = "value_tests.rs"]
pub(crate) mod tests;