mirror of
https://github.com/block/buzz.git
synced 2026-10-30 22:11:36 +00:00
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
90 lines
3.2 KiB
Rust
90 lines
3.2 KiB
Rust
//! JSON parsing that rejects duplicate object members at every nesting depth.
|
|
|
|
use std::{collections::HashSet, fmt};
|
|
|
|
use serde::de::{DeserializeOwned, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Visitor};
|
|
use serde_json::Value;
|
|
|
|
struct StrictValue;
|
|
|
|
impl<'de> DeserializeSeed<'de> for StrictValue {
|
|
type Value = Value;
|
|
fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Value, D::Error> {
|
|
deserializer.deserialize_any(self)
|
|
}
|
|
}
|
|
|
|
impl<'de> Visitor<'de> for StrictValue {
|
|
type Value = Value;
|
|
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
formatter.write_str("JSON with unique object keys")
|
|
}
|
|
fn visit_bool<E>(self, value: bool) -> Result<Value, E> {
|
|
Ok(Value::Bool(value))
|
|
}
|
|
fn visit_i64<E>(self, value: i64) -> Result<Value, E> {
|
|
Ok(Value::Number(value.into()))
|
|
}
|
|
fn visit_u64<E>(self, value: u64) -> Result<Value, E> {
|
|
Ok(Value::Number(value.into()))
|
|
}
|
|
fn visit_f64<E: serde::de::Error>(self, value: f64) -> Result<Value, E> {
|
|
serde_json::Number::from_f64(value)
|
|
.map(Value::Number)
|
|
.ok_or_else(|| E::custom("non-finite number"))
|
|
}
|
|
fn visit_str<E>(self, value: &str) -> Result<Value, E> {
|
|
Ok(Value::String(value.to_owned()))
|
|
}
|
|
fn visit_string<E>(self, value: String) -> Result<Value, E> {
|
|
Ok(Value::String(value))
|
|
}
|
|
fn visit_unit<E>(self) -> Result<Value, E> {
|
|
Ok(Value::Null)
|
|
}
|
|
fn visit_none<E>(self) -> Result<Value, E> {
|
|
Ok(Value::Null)
|
|
}
|
|
fn visit_some<D: Deserializer<'de>>(self, deserializer: D) -> Result<Value, D::Error> {
|
|
deserializer.deserialize_any(self)
|
|
}
|
|
fn visit_seq<A: SeqAccess<'de>>(self, mut sequence: A) -> Result<Value, A::Error> {
|
|
let mut values = Vec::with_capacity(sequence.size_hint().unwrap_or(0));
|
|
while let Some(value) = sequence.next_element_seed(StrictValue)? {
|
|
values.push(value);
|
|
}
|
|
Ok(Value::Array(values))
|
|
}
|
|
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Value, A::Error> {
|
|
use serde::de::Error;
|
|
let mut seen = HashSet::new();
|
|
let mut values = serde_json::Map::new();
|
|
while let Some(key) = map.next_key::<String>()? {
|
|
if !seen.insert(key.clone()) {
|
|
return Err(A::Error::custom("duplicate object member"));
|
|
}
|
|
values.insert(key, map.next_value_seed(StrictValue)?);
|
|
}
|
|
Ok(Value::Object(values))
|
|
}
|
|
}
|
|
|
|
/// Parse one complete JSON document, rejecting duplicate keys and trailing data.
|
|
pub fn from_slice<T: DeserializeOwned>(bytes: &[u8]) -> Result<T, serde_json::Error> {
|
|
let mut deserializer = serde_json::Deserializer::from_slice(bytes);
|
|
let value = StrictValue.deserialize(&mut deserializer)?;
|
|
deserializer.end()?;
|
|
T::deserialize(value)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
#[test]
|
|
fn rejects_duplicate_keys_at_any_depth() {
|
|
assert!(from_slice::<Value>(br#"{"v":1,"v":1}"#).is_err());
|
|
assert!(from_slice::<Value>(br#"{"wake":{"v":1,"v":1}}"#).is_err());
|
|
assert!(from_slice::<Value>(br#"{"v":1} trailing"#).is_err());
|
|
}
|
|
}
|