#![expect(
clippy::disallowed_types,
reason = "the canonical-JSON reader runtime buffers wire members as serde_json::Value before \
`_type` dispatch — the wire value IS the domain here (#1694 boundary class)"
)]
use serde::de::{DeserializeSeed, Deserializer, Error as _, MapAccess, Visitor};
pub const TYPE_KEY: &str = "_type";
#[derive(Debug, Clone, Copy)]
pub struct ExpectedType(
pub &'static str,
);
impl<'de> DeserializeSeed<'de> for ExpectedType {
type Value = ();
fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<(), D::Error> {
deserializer.deserialize_str(self)
}
}
impl Visitor<'_> for ExpectedType {
type Value = ();
fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "the `_type` discriminator \"{}\"", self.0)
}
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<(), E> {
if v == self.0 {
Ok(())
} else {
Err(E::custom(format_args!(
"expected _type \"{}\", found \"{v}\"",
self.0
)))
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TagMatch {
Known(&'static str),
Unknown(String),
}
#[derive(Debug, Clone, Copy)]
pub struct MatchTag(
pub &'static [&'static str],
);
impl<'de> DeserializeSeed<'de> for MatchTag {
type Value = TagMatch;
fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<TagMatch, D::Error> {
deserializer.deserialize_str(self)
}
}
impl Visitor<'_> for MatchTag {
type Value = TagMatch;
fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("a `_type` discriminator string")
}
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<TagMatch, E> {
Ok(self
.0
.iter()
.find(|t| **t == v)
.map_or_else(|| TagMatch::Unknown(v.to_owned()), |t| TagMatch::Known(t)))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SlotKey {
Type,
Other(String),
}
impl<'de> serde::Deserialize<'de> for SlotKey {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct KeyVisitor;
impl Visitor<'_> for KeyVisitor {
type Value = SlotKey;
fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("an object member name")
}
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<SlotKey, E> {
Ok(if v == TYPE_KEY {
SlotKey::Type
} else {
SlotKey::Other(v.to_owned())
})
}
}
deserializer.deserialize_identifier(KeyVisitor)
}
}
pub type SlotTag = (Option<TagMatch>, Vec<(String, serde_json::Value)>);
pub fn read_slot_tag<'de, A: MapAccess<'de>>(
map: &mut A,
tags: &'static [&'static str],
) -> Result<SlotTag, A::Error> {
let mut buffered: Vec<(String, serde_json::Value)> = Vec::new();
while let Some(key) = map.next_key::<SlotKey>()? {
match key {
SlotKey::Type => {
let tag = map.next_value_seed(MatchTag(tags))?;
return Ok((Some(tag), buffered));
}
SlotKey::Other(name) => {
let value = map.next_value::<serde_json::Value>()?;
if buffered.iter().any(|(k, _)| *k == name) {
return Err(A::Error::custom(format_args!("duplicate field `{name}`")));
}
buffered.push((name, value));
}
}
}
Ok((None, buffered))
}
#[derive(Debug)]
pub struct TaggedRest<A> {
tag: Option<&'static str>,
pending_tag: Option<&'static str>,
buffered: std::vec::IntoIter<(String, serde_json::Value)>,
pending_value: Option<serde_json::Value>,
map: A,
}
impl<A> TaggedRest<A> {
pub fn new(
tag: Option<&'static str>,
buffered: Vec<(String, serde_json::Value)>,
map: A,
) -> Self {
Self {
tag,
pending_tag: None,
buffered: buffered.into_iter(),
pending_value: None,
map,
}
}
}
#[derive(Debug)]
struct TagDeserializer<E> {
tag: &'static str,
error: std::marker::PhantomData<E>,
}
impl<'de, E: serde::de::Error> Deserializer<'de> for TagDeserializer<E> {
type Error = E;
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
visitor.visit_str(self.tag)
}
fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
visitor.visit_some(self)
}
serde::forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
bytes byte_buf unit unit_struct newtype_struct seq tuple
tuple_struct map struct enum identifier ignored_any
}
}
impl<'de, A: MapAccess<'de>> MapAccess<'de> for TaggedRest<A> {
type Error = A::Error;
fn next_key_seed<K: DeserializeSeed<'de>>(
&mut self,
seed: K,
) -> Result<Option<K::Value>, Self::Error> {
if let Some(tag) = self.tag.take() {
self.pending_tag = Some(tag);
return seed
.deserialize(serde::de::value::StrDeserializer::<Self::Error>::new(
TYPE_KEY,
))
.map(Some);
}
if let Some((key, value)) = self.buffered.next() {
self.pending_value = Some(value);
return seed
.deserialize(serde::de::value::StringDeserializer::<Self::Error>::new(
key,
))
.map(Some);
}
self.map.next_key_seed(seed)
}
fn next_value_seed<V: DeserializeSeed<'de>>(
&mut self,
seed: V,
) -> Result<V::Value, Self::Error> {
if let Some(tag) = self.pending_tag.take() {
return seed.deserialize(TagDeserializer::<Self::Error> {
tag,
error: std::marker::PhantomData,
});
}
if let Some(value) = self.pending_value.take() {
return seed
.deserialize(value)
.map_err(|e: serde_json::Error| Self::Error::custom(e));
}
self.map.next_value_seed(seed)
}
fn size_hint(&self) -> Option<usize> {
None
}
}
impl<'de, A: MapAccess<'de>> Deserializer<'de> for TaggedRest<A> {
type Error = A::Error;
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
visitor.visit_map(self)
}
serde::forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
bytes byte_buf option unit unit_struct newtype_struct seq tuple
tuple_struct map struct enum identifier ignored_any
}
}
#[must_use]
pub fn unexpected_type<E: serde::de::Error>(slot: &str, found: &str, expected: &str) -> E {
E::custom(format_args!(
"{slot}: unexpected `_type` {found:?} (expected one of: {expected})"
))
}
#[must_use]
pub fn missing_type<E: serde::de::Error>(slot: &str, expected: &str) -> E {
E::custom(format_args!(
"{slot}: missing required `_type` on polymorphic slot (expected one of: {expected})"
))
}
#[must_use]
pub fn unknown_field<E: serde::de::Error>(
field: &str,
class: &'static str,
expected: &'static [&'static str],
) -> E {
if expected.is_empty() {
return E::custom(format_args!(
"unknown field `{field}` on `{class}`, there are no fields"
));
}
let mut list = String::new();
for (i, name) in expected.iter().enumerate() {
if i > 0 {
list.push_str(", ");
}
list.push('`');
list.push_str(name);
list.push('`');
}
E::custom(format_args!(
"unknown field `{field}` on `{class}`, expected one of {list}"
))
}