use std::{fmt, marker::PhantomData}; use serde::{ de::{IgnoredAny, SeqAccess, Visitor}, Deserialize, }; use super::MapResult; /// Deserializes a list of arbitrary items into a `MapResult`, /// creating warnings for items that could not be deserialized. /// /// This is similar to `VecSkipError`, but it does not silently ignore /// faulty items. impl<'de, T> Deserialize<'de> for MapResult> where T: Deserialize<'de>, { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { #[derive(serde::Deserialize)] #[serde(untagged)] enum GoodOrError { Good(T), Error(serde_json::Value), } struct SeqVisitor(PhantomData); impl<'de, T> Visitor<'de> for SeqVisitor where T: Deserialize<'de>, { type Value = MapResult>; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a sequence") } fn visit_seq(self, mut seq: A) -> Result where A: SeqAccess<'de>, { let mut values = Vec::with_capacity(seq.size_hint().unwrap_or_default()); let mut warnings = Vec::new(); while let Some(value) = seq.next_element()? { match value { GoodOrError::::Good(value) => { values.push(value); } GoodOrError::::Error(value) => { warnings.push(format!( "error deserializing item: {}", serde_json::to_string(&value).unwrap_or_default() )); } } } Ok(MapResult { c: values, warnings, }) } } deserializer.deserialize_seq(SeqVisitor(PhantomData::)) } } /// Reimplementation of VecSkipError using a wrapper type /// to allow use with generics pub struct VecSkipErrorWrap(pub Vec); impl<'de, T> Deserialize<'de> for VecSkipErrorWrap where T: Deserialize<'de>, { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { #[derive(serde::Deserialize)] #[serde(untagged)] enum GoodOrError { Good(T), Error(IgnoredAny), } struct SeqVisitor(PhantomData); impl<'de, T> Visitor<'de> for SeqVisitor where T: Deserialize<'de>, { type Value = VecSkipErrorWrap; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { formatter.write_str("a sequence") } fn visit_seq(self, mut seq: A) -> Result where A: SeqAccess<'de>, { let mut values = Vec::with_capacity(seq.size_hint().unwrap_or_default()); while let Some(value) = seq.next_element()? { match value { GoodOrError::::Good(value) => { values.push(value); } GoodOrError::::Error(_) => {} } } Ok(VecSkipErrorWrap(values)) } } deserializer.deserialize_seq(SeqVisitor(PhantomData::)) } } #[cfg(test)] mod tests { use serde::Deserialize; use crate::serializer::MapResult; use super::VecSkipErrorWrap; #[derive(Debug, Deserialize)] #[allow(dead_code)] struct SLog { items: MapResult>, } #[derive(Deserialize)] #[allow(dead_code)] struct SSkip { items: VecSkipErrorWrap, } #[derive(Debug, Deserialize)] #[allow(dead_code)] struct Item { name: String, } const JSON: &str = r#"{"items": [{"name": "i1"}, {"xyz": "i2"}, {"name": "i3"}, {"namra": "i4"}]}"#; #[test] fn skip_error() { let res = serde_json::from_str::(JSON).unwrap(); insta::assert_debug_snapshot!(res.items.0, @r###" [ Item { name: "i1", }, Item { name: "i3", }, ] "###); } #[test] fn log_error() { let res = serde_json::from_str::(JSON).unwrap(); insta::assert_debug_snapshot!(res, @r###" SLog { items: [ Item { name: "i1", }, Item { name: "i3", }, ], } "###); insta::assert_debug_snapshot!(res.items.warnings, @r###" [ "error deserializing item: {\"xyz\":\"i2\"}", "error deserializing item: {\"namra\":\"i4\"}", ] "###); } }