use std::cell::RefCell;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::fmt;
use std::rc::Rc;
use serde::de;
use serde::de::DeserializeSeed;
use serde::de::MapAccess;
use serde::de::Visitor;
use super::super::strict_string_map::StrictStringMap;
use crate::MetadataError;
use crate::MetadataWireLimitKind;
use crate::internal::BoundedStringSeed;
pub(in crate::wire) struct StrictStringMapValueVisitor<S> {
max_entries: usize,
max_key_bytes: usize,
error_slot: Option<Rc<RefCell<Option<MetadataError>>>>,
value_seed: S,
}
impl<S> StrictStringMapValueVisitor<S> {
#[must_use]
#[inline]
pub(in crate::wire) fn new(
max_entries: usize,
max_key_bytes: usize,
error_slot: Option<Rc<RefCell<Option<MetadataError>>>>,
value_seed: S,
) -> Self {
Self {
max_entries,
max_key_bytes,
error_slot,
value_seed,
}
}
}
impl<'de, S> Visitor<'de> for StrictStringMapValueVisitor<S>
where
S: DeserializeSeed<'de> + Clone,
{
type Value = StrictStringMap<S::Value>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a map with unique string keys")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut values = BTreeMap::new();
while let Some(key) = map.next_key_seed(BoundedStringSeed::<_, String>::new(|key: &str| {
if key.len() > self.max_key_bytes {
if let Some(slot) = &self.error_slot {
*slot.borrow_mut() = Some(MetadataError::WireLimitExceeded {
kind: MetadataWireLimitKind::KeyBytes,
value: key.len(),
maximum: self.max_key_bytes,
});
}
return Err(format!(
"map key has {} bytes, exceeding the limit of {} bytes",
key.len(),
self.max_key_bytes,
));
}
Ok(())
}))? {
if values.len() >= self.max_entries {
if let Some(slot) = &self.error_slot {
*slot.borrow_mut() = Some(MetadataError::WireLimitExceeded {
kind: MetadataWireLimitKind::Entries,
value: values.len() + 1,
maximum: self.max_entries,
});
}
return Err(de::Error::custom(format!(
"map has more than the limit of {} entries",
self.max_entries,
)));
}
match values.entry(key) {
Entry::Occupied(entry) => {
return Err(de::Error::custom(format!("duplicate map key '{}'", entry.key())));
}
Entry::Vacant(entry) => {
entry.insert(map.next_value_seed(self.value_seed.clone())?);
}
}
}
Ok(StrictStringMap(values))
}
}