use core::fmt::{self, Write as _};
use serde::{
Deserializer,
de::{self, DeserializeSeed, Visitor},
};
use super::error::excerpt;
#[derive(Debug)]
pub(crate) enum Expect {
Uint(u64),
Float(f64),
Bool(bool),
Str(&'static str),
Uints(&'static [u64]),
Strs(&'static [&'static str]),
Elements(&'static [(&'static str, Expect)]),
Object(&'static [(&'static str, &'static str, Expect)]),
Open(&'static [(&'static str, &'static str, Expect)]),
}
#[derive(Debug)]
pub(crate) struct Difference {
pub(crate) path: &'static str,
pub(crate) expected: String,
pub(crate) actual: String,
}
impl fmt::Display for Difference {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"at `{}`: expected {}, got {}",
self.path, self.expected, self.actual
)
}
}
#[derive(Clone, Copy)]
pub(crate) enum Actual<'a> {
Absent,
Bool(bool),
Uint(u64),
Float(f64),
Str(&'a str),
Uints(&'a [u64]),
Strs(&'a [String]),
Elements(&'a dyn Elements),
Object(&'a dyn Fields),
}
impl<'a> Actual<'a> {
pub(crate) fn of<T: AsActual>(field: &'a Option<T>) -> Self {
field.as_ref().map_or(Self::Absent, AsActual::as_actual)
}
}
pub(crate) trait AsActual {
fn as_actual(&self) -> Actual<'_>;
}
impl AsActual for bool {
fn as_actual(&self) -> Actual<'_> {
Actual::Bool(*self)
}
}
impl AsActual for u64 {
fn as_actual(&self) -> Actual<'_> {
Actual::Uint(*self)
}
}
impl AsActual for f64 {
fn as_actual(&self) -> Actual<'_> {
Actual::Float(*self)
}
}
impl AsActual for String {
fn as_actual(&self) -> Actual<'_> {
Actual::Str(self)
}
}
impl AsActual for Vec<u64> {
fn as_actual(&self) -> Actual<'_> {
Actual::Uints(self)
}
}
impl AsActual for Vec<String> {
fn as_actual(&self) -> Actual<'_> {
Actual::Strs(self)
}
}
pub(crate) trait Fields {
fn keys(&self) -> &'static [&'static str];
fn field(&self, key: &str) -> Actual<'_>;
}
pub(crate) trait Elements {
fn count(&self) -> usize;
fn element(&self, index: usize) -> Actual<'_>;
}
#[derive(Clone, Copy)]
pub(crate) struct KeyOf {
pub(crate) keys: &'static [&'static str],
pub(crate) closed: bool,
}
impl<'de> DeserializeSeed<'de> for KeyOf {
type Value = Option<&'static str>;
fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
deserializer.deserialize_identifier(self)
}
}
impl Visitor<'_> for KeyOf {
type Value = Option<&'static str>;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("an object's key")
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
match self.keys.iter().find(|key| **key == v) {
Some(key) => Ok(Some(key)),
None if self.closed => Err(E::unknown_field(v, self.keys)),
None => Ok(None),
}
}
}
macro_rules! typed_object {
(
$(#[$meta:meta])*
struct $name:ident { $( $key:literal => $field:ident: $ty:ty ),+ $(,)? }
) => {
$crate::embeddings::embeddinggemma2::contract::object!(
true, $(#[$meta])* $name { $( $key => $field: $ty ),+ }
);
};
}
macro_rules! open_object {
(
$(#[$meta:meta])*
struct $name:ident { $( $key:literal => $field:ident: $ty:ty ),+ $(,)? }
) => {
$crate::embeddings::embeddinggemma2::contract::object!(
false, $(#[$meta])* $name { $( $key => $field: $ty ),+ }
);
};
}
macro_rules! object {
(
$closed:literal,
$(#[$meta:meta])*
$name:ident { $( $key:literal => $field:ident: $ty:ty ),+ }
) => {
$(#[$meta])*
struct $name {
$( $field: Option<$ty>, )+
}
impl<'de> serde::Deserialize<'de> for $name {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> core::result::Result<Self, D::Error> {
struct Map;
impl<'de> serde::de::Visitor<'de> for Map {
type Value = $name;
fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("an object")
}
fn visit_map<A: serde::de::MapAccess<'de>>(self, mut map: A) -> core::result::Result<$name, A::Error> {
let mut object = $name { $( $field: None, )+ };
let keys = $crate::embeddings::embeddinggemma2::contract::KeyOf {
keys: &[$($key),+],
closed: $closed,
};
while let Some(key) = map.next_key_seed(keys)? {
match key {
$(
Some($key) => {
if object.$field.is_some() {
return Err(<A::Error as serde::de::Error>::duplicate_field($key));
}
object.$field = Some(map.next_value()?);
}
)+
_ => {
map.next_value::<serde::de::IgnoredAny>()?;
}
}
}
Ok(object)
}
}
deserializer.deserialize_map(Map)
}
}
impl $crate::embeddings::embeddinggemma2::contract::Fields for $name {
fn keys(&self) -> &'static [&'static str] {
&[$($key),+]
}
fn field(&self, key: &str) -> $crate::embeddings::embeddinggemma2::contract::Actual<'_> {
match key {
$( $key => $crate::embeddings::embeddinggemma2::contract::Actual::of(&self.$field), )+
_ => $crate::embeddings::embeddinggemma2::contract::Actual::Absent,
}
}
}
impl $crate::embeddings::embeddinggemma2::contract::AsActual for $name {
fn as_actual(&self) -> $crate::embeddings::embeddinggemma2::contract::Actual<'_> {
$crate::embeddings::embeddinggemma2::contract::Actual::Object(self)
}
}
};
}
pub(crate) use {object, open_object, typed_object};
pub(crate) fn compare(
expect: &Expect,
path: &'static str,
actual: Actual<'_>,
) -> Option<Difference> {
let differs = |actual: String| {
Some(Difference {
path,
expected: excerpt(Render(expect)),
actual,
})
};
match (expect, actual) {
(_, Actual::Absent) => differs("absent".to_owned()),
(Expect::Object(entries) | Expect::Open(entries), Actual::Object(object)) => entries
.iter()
.find_map(|(key, path, expect)| compare(expect, path, object.field(key)))
.or_else(|| {
if !matches!(expect, Expect::Object(_)) {
return None;
}
let extra = object.keys().iter().find(|key| {
!matches!(object.field(key), Actual::Absent)
&& !entries.iter().any(|(known, _, _)| known == *key)
})?;
differs(format!("also the key {}", JsonStr(extra)))
}),
(Expect::Elements(items), Actual::Elements(elements)) => {
if elements.count() != items.len() {
return differs(excerpt(Shown(actual)));
}
items
.iter()
.enumerate()
.find_map(|(index, (path, expect))| compare(expect, path, elements.element(index)))
}
(Expect::Uint(n), Actual::Uint(v)) if *n == v => None,
(Expect::Float(x), Actual::Float(v)) if *x == v => None,
(Expect::Bool(b), Actual::Bool(v)) if *b == v => None,
(Expect::Str(s), Actual::Str(v)) if *s == v => None,
(Expect::Uints(ns), Actual::Uints(vs)) if *ns == vs => None,
(Expect::Strs(ss), Actual::Strs(vs))
if ss.len() == vs.len() && ss.iter().zip(vs).all(|(s, v)| s == v) =>
{
None
}
(_, actual) => differs(excerpt(Shown(actual))),
}
}
pub(crate) enum Step<'a> {
Key(&'a str),
Index(usize),
}
pub(crate) fn field_at<'s>(
contract: &'static Expect,
steps: impl IntoIterator<Item = Step<'s>>,
) -> (&'static str, &'static Expect) {
let mut here: (&'static str, &'static Expect) = ("", contract);
for step in steps {
let next = match (here.1, step) {
(Expect::Object(entries) | Expect::Open(entries), Step::Key(key)) => entries
.iter()
.find(|(known, _, _)| *known == key)
.map(|(_, path, expect)| (*path, expect)),
(Expect::Elements(items), Step::Index(index)) => {
items.get(index).map(|(path, expect)| (*path, expect))
}
_ => None,
};
match next {
Some(next) => here = next,
None => break,
}
}
here
}
pub(crate) struct Wanted<'c>(pub(crate) &'c Expect);
impl fmt::Display for Wanted<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
Expect::Object(entries) => write!(f, "exactly the keys [{}]", Keys(entries)),
Expect::Open(entries) => write!(f, "at least the keys [{}]", Keys(entries)),
expect => write!(f, "{}", Render(expect)),
}
}
}
struct JsonStr<'a>(&'a str);
impl fmt::Display for JsonStr<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_char('"')?;
for c in self.0.chars() {
match c {
'"' => f.write_str("\\\"")?,
'\\' => f.write_str("\\\\")?,
'\n' => f.write_str("\\n")?,
'\r' => f.write_str("\\r")?,
'\t' => f.write_str("\\t")?,
c if u32::from(c) < 0x20 => write!(f, "\\u{:04x}", u32::from(c))?,
c => f.write_char(c)?,
}
}
f.write_char('"')
}
}
struct JsonFloat(f64);
impl fmt::Display for JsonFloat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match serde_json::Number::from_f64(self.0) {
Some(number) => write!(f, "{number}"),
None => f.write_str("null"),
}
}
}
struct Render<'c>(&'c Expect);
impl fmt::Display for Render<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
Expect::Uint(n) => write!(f, "{n}"),
Expect::Float(x) => write!(f, "{}", JsonFloat(*x)),
Expect::Bool(b) => write!(f, "{b}"),
Expect::Str(s) => write!(f, "{}", JsonStr(s)),
Expect::Uints(ns) => list(f, ns.iter(), |f, n| write!(f, "{n}")),
Expect::Strs(ss) => list(f, ss.iter(), |f, s| write!(f, "{}", JsonStr(s))),
Expect::Elements(items) => list(f, items.iter(), |f, (_, item)| {
write!(f, "{}", Render(item))
}),
Expect::Object(entries) | Expect::Open(entries) => {
f.write_char('{')?;
for (i, (key, _, value)) in entries.iter().enumerate() {
if i > 0 {
f.write_char(',')?;
}
write!(f, "{}:{}", JsonStr(key), Render(value))?;
}
f.write_char('}')
}
}
}
}
struct Shown<'a>(Actual<'a>);
impl fmt::Display for Shown<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
Actual::Absent => f.write_str("absent"),
Actual::Bool(b) => write!(f, "{b}"),
Actual::Uint(n) => write!(f, "{n}"),
Actual::Float(x) => write!(f, "{}", JsonFloat(x)),
Actual::Str(s) => write!(f, "{}", JsonStr(s)),
Actual::Uints(ns) => list(f, ns.iter(), |f, n| write!(f, "{n}")),
Actual::Strs(ss) => list(f, ss.iter(), |f, s| write!(f, "{}", JsonStr(s))),
Actual::Elements(elements) => write!(f, "an array of {} objects", elements.count()),
Actual::Object(_) => f.write_str("an object"),
}
}
}
fn list<T>(
f: &mut fmt::Formatter<'_>,
items: impl Iterator<Item = T>,
mut each: impl FnMut(&mut fmt::Formatter<'_>, T) -> fmt::Result,
) -> fmt::Result {
f.write_char('[')?;
for (i, item) in items.enumerate() {
if i > 0 {
f.write_char(',')?;
}
each(f, item)?;
}
f.write_char(']')
}
struct Keys<'c>(&'c [(&'static str, &'static str, Expect)]);
impl fmt::Display for Keys<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, (key, _, _)) in self.0.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
write!(f, "{key:?}")?;
}
Ok(())
}
}