#![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
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum OpenSubtypeError {
#[error("an open-subtype instance must carry a non-empty `_type` scheme name")]
EmptyTypeName,
#[error("`_type` is the subtype tag, not a member")]
TypeAmongMembers,
}
#[derive(Debug, Clone, PartialEq)]
pub struct OpenSubtype {
type_name: String,
members: serde_json::Map<String, serde_json::Value>,
}
impl OpenSubtype {
pub fn new(
type_name: impl Into<String>,
members: serde_json::Map<String, serde_json::Value>,
) -> Result<Self, OpenSubtypeError> {
let type_name = type_name.into();
if type_name.is_empty() {
return Err(OpenSubtypeError::EmptyTypeName);
}
if members.contains_key(TYPE_KEY) {
return Err(OpenSubtypeError::TypeAmongMembers);
}
Ok(Self { type_name, members })
}
#[must_use]
pub fn type_name(&self) -> &str {
&self.type_name
}
#[must_use]
pub fn members(&self) -> &serde_json::Map<String, serde_json::Value> {
&self.members
}
#[must_use]
pub fn member(&self, key: &str) -> Option<&serde_json::Value> {
self.members.get(key)
}
}
impl serde::Serialize for OpenSubtype {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeMap as _;
let mut map = serializer.serialize_map(Some(self.members.len() + 1))?;
map.serialize_entry(TYPE_KEY, &self.type_name)?;
for (key, value) in &self.members {
map.serialize_entry(key, value)?;
}
map.end()
}
}
impl<'de> serde::Deserialize<'de> for OpenSubtype {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct OpenVisitor;
impl<'de> Visitor<'de> for OpenVisitor {
type Value = OpenSubtype;
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("an object carrying a `_type` scheme name")
}
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
let mut type_name: Option<String> = None;
let mut members = serde_json::Map::new();
while let Some(key) = map.next_key::<String>()? {
if key == TYPE_KEY {
if type_name.is_some() {
return Err(A::Error::duplicate_field(TYPE_KEY));
}
type_name = Some(map.next_value::<String>()?);
continue;
}
if members.contains_key(&key) {
return Err(A::Error::custom(format_args!(
"duplicate member `{key}` on an open-subtype instance"
)));
}
members.insert(key, map.next_value::<serde_json::Value>()?);
}
let Some(type_name) = type_name else {
return Err(A::Error::custom(
"an open-subtype instance must carry a `_type` scheme name",
));
};
OpenSubtype::new(type_name, members).map_err(A::Error::custom)
}
}
deserializer.deserialize_map(OpenVisitor)
}
}
pub fn refuse_empty_list<T, E: serde::de::Error>(
list: Option<Vec<T>>,
attribute: &str,
) -> Result<Option<Vec<T>>, E> {
match list {
Some(items) if items.is_empty() => Err(E::invalid_length(
0,
&format!("`{attribute}` to hold at least one element (ITS-JSON minItems 1)").as_str(),
)),
list => Ok(list),
}
}
#[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}"
))
}