use super::{canon, replay, NativeConvertError, NativeRecord, MAX_NATIVE_NESTING_DEPTH};
use cadmpeg_core::decode::{DecodeContext, DepthGuard};
use serde::{ser, Serialize};
use std::collections::BTreeSet;
type Error = canon::CanonError;
const OPERATION: &str = "compare native record";
const RAW_VALUE_STRUCT: &str = "$serde_json::private::RawValue";
fn renders<T: Serialize + ?Sized>(actual: Option<&str>, expected: &T) -> bool {
let Some(actual) = actual else {
return false;
};
let mut rest = SameText {
rest: actual.as_bytes(),
};
serde_json::to_writer(&mut rest, expected).is_ok() && rest.rest.is_empty()
}
fn parses_to(actual: Option<&str>, expected: &str) -> bool {
let Some(actual) = actual else {
return false;
};
replay::value(actual)
.is_ok_and(|actual| replay::value(expected).is_ok_and(|expected| actual == expected))
}
fn object_members(json: &str) -> Option<replay::MemberTexts<'_>> {
let mut members = Vec::new();
replay::members(json, &mut members).then_some(members)
}
struct SameText<'a> {
rest: &'a [u8],
}
impl std::io::Write for SameText<'_> {
fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
match self.rest.strip_prefix(bytes) {
Some(rest) => {
self.rest = rest;
Ok(bytes.len())
}
None => Err(std::io::Error::other("differs from the stored text")),
}
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
pub(super) fn matches<T: Serialize + ?Sized>(
ctx: &DecodeContext<'_>,
actual: &NativeRecord,
expected: &T,
) -> Result<bool, NativeConvertError> {
expected
.serialize(Matcher {
ctx,
actual: Some(&actual.json),
remaining: MAX_NATIVE_NESTING_DEPTH + 1,
})
.map_err(|error| error.into_native(ctx))
}
#[derive(Clone, Copy)]
struct Matcher<'a, 'arena> {
ctx: &'a DecodeContext<'arena>,
actual: Option<&'a str>,
remaining: usize,
}
impl<'a, 'arena> Matcher<'a, 'arena> {
fn container(self) -> Result<DepthGuard<'a>, Error> {
if self.remaining == 0 {
return Err(<Error as ser::Error>::custom(
super::nests_too_deep_message(),
));
}
Ok(self.ctx.enter_nested(OPERATION)?)
}
fn child(self, actual: Option<&'a str>) -> Result<Self, Error> {
Ok(Self {
actual,
remaining: self
.remaining
.checked_sub(1)
.ok_or_else(|| <Error as ser::Error>::custom(super::nests_too_deep_message()))?,
..self
})
}
fn scalar(self, value: impl Serialize) -> Result<bool, Error> {
let expected = canon::to_text(self.ctx, MAX_NATIVE_NESTING_DEPTH + 1, &value)?;
Ok(self.actual == Some(expected.as_str()))
}
fn sequence(
self,
outer: Option<DepthGuard<'a>>,
parent_matches: bool,
) -> Result<Sequence<'a, 'arena>, Error> {
let nested = self.container()?;
let actual = self.actual.and_then(replay::Elements::of);
Ok(Sequence {
matcher: self,
matched: parent_matches && actual.is_some(),
actual,
index: 0,
_nested: nested,
_outer: outer,
})
}
fn map(
self,
outer: Option<DepthGuard<'a>>,
parent_matches: bool,
) -> Result<Object<'a, 'arena>, Error> {
let nested = self.container()?;
let members = self.actual.and_then(object_members);
Ok(Object {
matcher: self,
matched: parent_matches && members.is_some(),
members,
seen: BTreeSet::new(),
key: None,
raw: false,
_nested: nested,
_outer: outer,
})
}
fn variant(self, variant: &'static str) -> Result<(Self, DepthGuard<'a>, bool), Error> {
let outer = self.container()?;
let members = self.actual.and_then(object_members).unwrap_or_default();
let payload = match members.as_slice() {
[(key, payload)] if key == variant => Some(*payload),
_ => None,
};
Ok((self.child(payload)?, outer, members.len() == 1))
}
}
macro_rules! scalars {
($($method:ident($ty:ty)),* $(,)?) => {
$(fn $method(self, value: $ty) -> Result<bool, Error> { self.scalar(value) })*
};
}
impl<'a, 'arena> ser::Serializer for Matcher<'a, 'arena> {
type Ok = bool;
type Error = Error;
type SerializeSeq = Sequence<'a, 'arena>;
type SerializeTuple = Sequence<'a, 'arena>;
type SerializeTupleStruct = Sequence<'a, 'arena>;
type SerializeTupleVariant = Sequence<'a, 'arena>;
type SerializeMap = Object<'a, 'arena>;
type SerializeStruct = Object<'a, 'arena>;
type SerializeStructVariant = Object<'a, 'arena>;
scalars!(
serialize_bool(bool),
serialize_i8(i8),
serialize_i16(i16),
serialize_i32(i32),
serialize_i64(i64),
serialize_i128(i128),
serialize_u8(u8),
serialize_u16(u16),
serialize_u32(u32),
serialize_u64(u64),
serialize_u128(u128),
serialize_f32(f32),
serialize_f64(f64)
);
fn serialize_char(self, value: char) -> Result<bool, Error> {
self.serialize_str(value.encode_utf8(&mut [0; 4]))
}
fn serialize_str(self, value: &str) -> Result<bool, Error> {
Ok(renders(self.actual, value))
}
fn serialize_bytes(self, value: &[u8]) -> Result<bool, Error> {
let mut sequence = self.sequence(None, true)?;
for byte in value {
ser::SerializeSeq::serialize_element(&mut sequence, byte)?;
}
ser::SerializeSeq::end(sequence)
}
fn serialize_none(self) -> Result<bool, Error> {
self.serialize_unit()
}
fn serialize_some<T: Serialize + ?Sized>(self, value: &T) -> Result<bool, Error> {
value.serialize(self)
}
fn serialize_unit(self) -> Result<bool, Error> {
self.scalar(())
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<bool, Error> {
self.serialize_unit()
}
fn serialize_unit_variant(
self,
_name: &'static str,
_index: u32,
variant: &'static str,
) -> Result<bool, Error> {
self.serialize_str(variant)
}
fn serialize_newtype_struct<T: Serialize + ?Sized>(
self,
_name: &'static str,
value: &T,
) -> Result<bool, Error> {
value.serialize(self)
}
fn serialize_newtype_variant<T: Serialize + ?Sized>(
self,
_name: &'static str,
_index: u32,
variant: &'static str,
value: &T,
) -> Result<bool, Error> {
let (inner, _outer, matched) = self.variant(variant)?;
Ok(value.serialize(inner)? && matched)
}
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Error> {
self.sequence(None, true)
}
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Error> {
self.sequence(None, true)
}
fn serialize_tuple_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct, Error> {
self.sequence(None, true)
}
fn serialize_tuple_variant(
self,
_name: &'static str,
_index: u32,
variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant, Error> {
let (inner, outer, matched) = self.variant(variant)?;
inner.sequence(Some(outer), matched)
}
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Error> {
self.map(None, true)
}
fn serialize_struct(
self,
name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Error> {
let mut object = self.map(None, true)?;
if name == RAW_VALUE_STRUCT {
object.raw = true;
object.matched = true;
}
Ok(object)
}
fn serialize_struct_variant(
self,
_name: &'static str,
_index: u32,
variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Error> {
let (inner, outer, matched) = self.variant(variant)?;
inner.map(Some(outer), matched)
}
fn collect_str<T: std::fmt::Display + ?Sized>(self, value: &T) -> Result<bool, Error> {
self.serialize_str(&canon::collect_display_text(value)?)
}
}
struct Sequence<'a, 'arena> {
matcher: Matcher<'a, 'arena>,
actual: Option<replay::Elements<'a>>,
index: usize,
matched: bool,
_nested: DepthGuard<'a>,
_outer: Option<DepthGuard<'a>>,
}
impl Sequence<'_, '_> {
fn element<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
let actual = self.actual.as_mut().and_then(Iterator::next);
self.matched &= value
.serialize(self.matcher.child(actual)?)
.map_err(|error| error.within(|| canon::Step::Index(self.index)))?;
self.index = self
.index
.checked_add(1)
.ok_or_else(|| <Error as ser::Error>::custom("native sequence length overflow"))?;
Ok(())
}
fn finish(self) -> bool {
self.matched && self.actual.is_some_and(|mut rest| rest.next().is_none())
}
}
macro_rules! sequences {
($($trait:ident::$method:ident),* $(,)?) => {
$(impl ser::$trait for Sequence<'_, '_> {
type Ok = bool;
type Error = Error;
fn $method<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> { self.element(value) }
fn end(self) -> Result<bool, Error> { Ok(self.finish()) }
})*
};
}
sequences!(
SerializeSeq::serialize_element,
SerializeTuple::serialize_element,
SerializeTupleStruct::serialize_field,
SerializeTupleVariant::serialize_field
);
struct Object<'a, 'arena> {
matcher: Matcher<'a, 'arena>,
members: Option<replay::MemberTexts<'a>>,
seen: BTreeSet<String>,
key: Option<String>,
matched: bool,
raw: bool,
_nested: DepthGuard<'a>,
_outer: Option<DepthGuard<'a>>,
}
impl Object<'_, '_> {
fn field<T: Serialize + ?Sized>(&mut self, key: String, value: &T) -> Result<(), Error> {
if self.raw {
if key != RAW_VALUE_STRUCT || !self.seen.is_empty() {
return Err(<Error as ser::Error>::custom(
"invalid raw native comparison field",
));
}
let expected =
canon::to_text(self.matcher.ctx, self.matcher.remaining, &RawField(value))?;
self.matched &= parses_to(self.matcher.actual, &expected);
self.seen.insert(key);
return Ok(());
}
let members = self.members.as_deref().unwrap_or_default();
let actual = members
.iter()
.find(|(member, _)| *member == key)
.map(|(_, value)| *value);
self.matched &= value
.serialize(self.matcher.child(actual)?)
.map_err(|error| error.within(|| canon::Step::Key(key.clone())))?;
if !self.seen.insert(key) {
return Err(<Error as ser::Error>::custom(
"duplicate native comparison key",
));
}
Ok(())
}
fn finish(self) -> Result<bool, Error> {
if self.key.is_some() {
return Err(<Error as ser::Error>::custom("native map key has no value"));
}
Ok(self.matched
&& if self.raw {
self.seen.len() == 1
} else {
self.members.as_ref().map(Vec::len) == Some(self.seen.len())
})
}
}
impl ser::SerializeMap for Object<'_, '_> {
type Ok = bool;
type Error = Error;
fn serialize_key<T: Serialize + ?Sized>(&mut self, key: &T) -> Result<(), Error> {
if self.key.is_some() {
return Err(<Error as ser::Error>::custom("native map key has no value"));
}
self.key = Some(key.serialize(canon::CanonKey {
ctx: self.matcher.ctx,
})?);
Ok(())
}
fn serialize_value<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
let key = self
.key
.take()
.ok_or_else(|| <Error as ser::Error>::custom("native map value has no key"))?;
self.field(key, value)
}
fn end(self) -> Result<bool, Error> {
self.finish()
}
}
macro_rules! objects {
($($trait:ident),* $(,)?) => {
$(impl ser::$trait for Object<'_, '_> {
type Ok = bool;
type Error = Error;
fn serialize_field<T: Serialize + ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), Error> {
self.field(key.to_owned(), value)
}
fn end(self) -> Result<bool, Error> { self.finish() }
})*
};
}
objects!(SerializeStruct, SerializeStructVariant);
struct RawField<'a, T: ?Sized>(&'a T);
impl<T: Serialize + ?Sized> Serialize for RawField<'_, T> {
fn serialize<S: ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut object = serializer.serialize_struct(RAW_VALUE_STRUCT, 1)?;
ser::SerializeStruct::serialize_field(&mut object, RAW_VALUE_STRUCT, self.0)?;
ser::SerializeStruct::end(object)
}
}
#[cfg(test)]
mod tests;