refactor: replace VecLogError with standard Deserialize impl

This commit is contained in:
ThetaDev 2023-05-05 17:41:09 +02:00
parent 963ff14dc1
commit a45eba4705
11 changed files with 75 additions and 124 deletions

View file

@ -81,7 +81,7 @@ impl MapResponse<Vec<MusicGenreItem>> for response::MusicGenres {
let genres = content_iter let genres = content_iter
.enumerate() .enumerate()
.flat_map(|(i, grid)| { .flat_map(|(i, grid)| {
let mut grid = grid.grid_renderer.items; let mut grid = grid.grid_renderer.contents;
warnings.append(&mut grid.warnings); warnings.append(&mut grid.warnings);
grid.c.into_iter().filter_map(move |section| match section { grid.c.into_iter().filter_map(move |section| match section {
response::music_genres::NavigationButton::MusicNavigationButtonRenderer( response::music_genres::NavigationButton::MusicNavigationButtonRenderer(

View file

@ -94,7 +94,7 @@ impl MapResponse<Playlist> for response::Playlist {
let (thumbnails, last_update_txt) = match self.sidebar { let (thumbnails, last_update_txt) = match self.sidebar {
Some(sidebar) => { Some(sidebar) => {
let mut sidebar_items = sidebar.playlist_sidebar_renderer.items; let mut sidebar_items = sidebar.playlist_sidebar_renderer.contents;
let mut primary = let mut primary =
sidebar_items sidebar_items
.try_swap_remove(0) .try_swap_remove(0)

View file

@ -57,7 +57,7 @@ use serde::{
use serde_with::{json::JsonString, serde_as, VecSkipError}; use serde_with::{json::JsonString, serde_as, VecSkipError};
use crate::error::ExtractionError; use crate::error::ExtractionError;
use crate::serializer::{text::Text, MapResult, VecLogError, VecSkipErrorWrap}; use crate::serializer::{text::Text, MapResult, VecSkipErrorWrap};
use self::video_item::YouTubeListRenderer; use self::video_item::YouTubeListRenderer;
@ -72,6 +72,12 @@ pub(crate) struct ContentsRenderer<T> {
pub contents: Vec<T>, pub contents: Vec<T>,
} }
#[derive(Debug, Deserialize)]
pub(crate) struct ContentsRendererLogged<T> {
#[serde(alias = "items")]
pub contents: MapResult<Vec<T>>,
}
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct Tab<T> { pub(crate) struct Tab<T> {
@ -216,11 +222,9 @@ pub(crate) struct ContinuationActionWrap {
pub append_continuation_items_action: ContinuationAction, pub append_continuation_items_action: ContinuationAction,
} }
#[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct ContinuationAction { pub(crate) struct ContinuationAction {
#[serde_as(as = "VecLogError<_>")]
pub continuation_items: MapResult<Vec<YouTubeListItem>>, pub continuation_items: MapResult<Vec<YouTubeListItem>>,
} }
@ -286,9 +290,10 @@ where
let mut contents = None; let mut contents = None;
while let Some(k) = map.next_key::<Cow<'de, str>>()? { while let Some(k) = map.next_key::<Cow<'de, str>>()? {
if k == "contents" || k == "tabs" { if k == "contents" || k == "tabs" || k == "items" {
let x = map.next_value::<VecSkipErrorWrap<T>>()?; contents = Some(ContentsRenderer {
contents = Some(ContentsRenderer { contents: x.0 }); contents: map.next_value::<VecSkipErrorWrap<T>>()?.0,
});
} else { } else {
map.next_value::<IgnoredAny>()?; map.next_value::<IgnoredAny>()?;
} }

View file

@ -1,12 +1,12 @@
use serde::Deserialize; use serde::Deserialize;
use serde_with::{rust::deserialize_ignore_any, serde_as}; use serde_with::{rust::deserialize_ignore_any, serde_as};
use crate::serializer::{text::Text, MapResult, VecLogError}; use crate::serializer::text::Text;
use super::{ use super::{
music_item::{ItemSection, SimpleHeader, SingleColumnBrowseResult}, music_item::{ItemSection, SimpleHeader, SingleColumnBrowseResult},
url_endpoint::BrowseEndpointWrap, url_endpoint::BrowseEndpointWrap,
SectionList, Tab, ContentsRendererLogged, SectionList, Tab,
}; };
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -18,15 +18,7 @@ pub(crate) struct MusicGenres {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct Grid { pub(crate) struct Grid {
pub grid_renderer: GridRenderer, pub grid_renderer: ContentsRendererLogged<NavigationButton>,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct GridRenderer {
#[serde_as(as = "VecLogError<_>")]
pub items: MapResult<Vec<NavigationButton>>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]

View file

@ -9,7 +9,7 @@ use crate::{
param::Language, param::Language,
serializer::{ serializer::{
text::{Text, TextComponents}, text::{Text, TextComponents},
MapResult, VecLogError, MapResult,
}, },
util::{self, dictionary, TryRemove}, util::{self, dictionary, TryRemove},
}; };
@ -39,7 +39,6 @@ pub(crate) enum ItemSection {
pub(crate) struct MusicShelf { pub(crate) struct MusicShelf {
/// Playlist ID (only for playlists) /// Playlist ID (only for playlists)
pub playlist_id: Option<String>, pub playlist_id: Option<String>,
#[serde_as(as = "VecLogError<_>")]
pub contents: MapResult<Vec<MusicResponseItem>>, pub contents: MapResult<Vec<MusicResponseItem>>,
/// Continuation token for fetching more (>100) playlist items /// Continuation token for fetching more (>100) playlist items
#[serde(default)] #[serde(default)]
@ -53,12 +52,10 @@ pub(crate) struct MusicShelf {
/// MusicCarouselShelf represents a horizontal list of music items displayed with /// MusicCarouselShelf represents a horizontal list of music items displayed with
/// large covers. /// large covers.
#[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct MusicCarouselShelf { pub(crate) struct MusicCarouselShelf {
pub header: Option<MusicCarouselShelfHeader>, pub header: Option<MusicCarouselShelfHeader>,
#[serde_as(as = "VecLogError<_>")]
pub contents: MapResult<Vec<MusicResponseItem>>, pub contents: MapResult<Vec<MusicResponseItem>>,
} }
@ -76,7 +73,6 @@ pub(crate) struct MusicCardShelf {
#[serde(default)] #[serde(default)]
pub thumbnail: MusicThumbnailRenderer, pub thumbnail: MusicThumbnailRenderer,
#[serde(default)] #[serde(default)]
#[serde_as(as = "VecLogError<_>")]
pub contents: MapResult<Vec<MusicResponseItem>>, pub contents: MapResult<Vec<MusicResponseItem>>,
} }
@ -227,7 +223,6 @@ pub(crate) struct CoverMusicItem {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct PlaylistPanelRenderer { pub(crate) struct PlaylistPanelRenderer {
#[serde_as(as = "VecLogError<_>")]
pub contents: MapResult<Vec<PlaylistPanelVideo>>, pub contents: MapResult<Vec<PlaylistPanelVideo>>,
/// Continuation token for fetching more radio items /// Continuation token for fetching more radio items
#[serde(default)] #[serde(default)]
@ -362,15 +357,7 @@ pub(crate) struct ButtonRenderer {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct MusicItemMenu { pub(crate) struct MusicItemMenu {
pub menu_renderer: MusicItemMenuRenderer, pub menu_renderer: ContentsRenderer<MusicItemMenuEntry>,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct MusicItemMenuRenderer {
#[serde_as(as = "VecSkipError<_>")]
pub items: Vec<MusicItemMenuEntry>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -385,11 +372,9 @@ pub(crate) struct Grid {
pub grid_renderer: GridRenderer, pub grid_renderer: GridRenderer,
} }
#[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct GridRenderer { pub(crate) struct GridRenderer {
#[serde_as(as = "VecLogError<_>")]
pub items: MapResult<Vec<MusicResponseItem>>, pub items: MapResult<Vec<MusicResponseItem>>,
pub header: Option<GridHeader>, pub header: Option<GridHeader>,
} }
@ -1147,7 +1132,7 @@ fn map_artist_id_fallback(
menu: Option<MusicItemMenu>, menu: Option<MusicItemMenu>,
fallback_artist: Option<&ArtistId>, fallback_artist: Option<&ArtistId>,
) -> Option<String> { ) -> Option<String> {
menu.and_then(|m| map_artist_id(m.menu_renderer.items)) menu.and_then(|m| map_artist_id(m.menu_renderer.contents))
.or_else(|| fallback_artist.and_then(|a| a.id.to_owned())) .or_else(|| fallback_artist.and_then(|a| a.id.to_owned()))
} }

View file

@ -5,7 +5,7 @@ use serde_with::serde_as;
use serde_with::{json::JsonString, DefaultOnError}; use serde_with::{json::JsonString, DefaultOnError};
use super::{ResponseContext, Thumbnails}; use super::{ResponseContext, Thumbnails};
use crate::serializer::{text::Text, MapResult, VecLogError}; use crate::serializer::{text::Text, MapResult};
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -75,10 +75,8 @@ pub(crate) struct StreamingData {
#[serde_as(as = "JsonString")] #[serde_as(as = "JsonString")]
pub expires_in_seconds: u32, pub expires_in_seconds: u32,
#[serde(default)] #[serde(default)]
#[serde_as(as = "VecLogError<_>")]
pub formats: MapResult<Vec<Format>>, pub formats: MapResult<Vec<Format>>,
#[serde(default)] #[serde(default)]
#[serde_as(as = "VecLogError<_>")]
pub adaptive_formats: MapResult<Vec<Format>>, pub adaptive_formats: MapResult<Vec<Format>>,
/// Only on livestreams /// Only on livestreams
pub dash_manifest_url: Option<String>, pub dash_manifest_url: Option<String>,

View file

@ -3,8 +3,10 @@ use serde_with::{
json::JsonString, rust::deserialize_ignore_any, serde_as, DefaultOnError, VecSkipError, json::JsonString, rust::deserialize_ignore_any, serde_as, DefaultOnError, VecSkipError,
}; };
use crate::serializer::text::{Text, TextComponent}; use crate::serializer::{
use crate::serializer::{MapResult, VecLogError}; text::{Text, TextComponent},
MapResult,
};
use crate::util::MappingError; use crate::util::MappingError;
use super::{ use super::{
@ -45,11 +47,9 @@ pub(crate) struct PlaylistVideoListRenderer {
pub playlist_video_list_renderer: PlaylistVideoList, pub playlist_video_list_renderer: PlaylistVideoList,
} }
#[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct PlaylistVideoList { pub(crate) struct PlaylistVideoList {
#[serde_as(as = "VecLogError<_>")]
pub contents: MapResult<Vec<PlaylistItem>>, pub contents: MapResult<Vec<PlaylistItem>>,
} }
@ -102,15 +102,7 @@ pub(crate) struct BylineRenderer {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct Sidebar { pub(crate) struct Sidebar {
pub playlist_sidebar_renderer: SidebarRenderer, pub playlist_sidebar_renderer: ContentsRenderer<SidebarItemPrimary>,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SidebarRenderer {
#[serde_as(as = "VecSkipError<_>")]
pub items: Vec<SidebarItemPrimary>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -193,10 +185,8 @@ pub(crate) struct OnResponseReceivedAction {
pub append_continuation_items_action: AppendAction, pub append_continuation_items_action: AppendAction,
} }
#[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct AppendAction { pub(crate) struct AppendAction {
#[serde_as(as = "VecLogError<_>")]
pub continuation_items: MapResult<Vec<PlaylistItem>>, pub continuation_items: MapResult<Vec<PlaylistItem>>,
} }

View file

@ -6,21 +6,20 @@ use serde_with::{rust::deserialize_ignore_any, serde_as, DefaultOnError, VecSkip
use crate::serializer::text::TextComponent; use crate::serializer::text::TextComponent;
use crate::serializer::{ use crate::serializer::{
text::{AccessibilityText, AttributedText, Text, TextComponents}, text::{AccessibilityText, AttributedText, Text, TextComponents},
MapResult, VecLogError, MapResult,
}; };
use super::{ use super::{
url_endpoint::BrowseEndpointWrap, ContinuationEndpoint, ContinuationItemRenderer, Icon, url_endpoint::BrowseEndpointWrap, ContinuationEndpoint, ContinuationItemRenderer, Icon,
MusicContinuationData, Thumbnails, MusicContinuationData, Thumbnails,
}; };
use super::{ChannelBadge, ResponseContext, YouTubeListItem}; use super::{ChannelBadge, ContentsRendererLogged, ResponseContext, YouTubeListItem};
/* /*
#VIDEO DETAILS #VIDEO DETAILS
*/ */
/// Video details response /// Video details response
#[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct VideoDetails { pub(crate) struct VideoDetails {
@ -29,7 +28,6 @@ pub(crate) struct VideoDetails {
/// Video ID /// Video ID
pub current_video_endpoint: Option<CurrentVideoEndpoint>, pub current_video_endpoint: Option<CurrentVideoEndpoint>,
/// Video chapters + comment section /// Video chapters + comment section
#[serde_as(as = "VecLogError<_>")]
pub engagement_panels: MapResult<Vec<EngagementPanel>>, pub engagement_panels: MapResult<Vec<EngagementPanel>>,
pub response_context: ResponseContext, pub response_context: ResponseContext,
} }
@ -60,11 +58,9 @@ pub(crate) struct VideoResultsWrap {
} }
/// Video metadata items /// Video metadata items
#[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct VideoResults { pub(crate) struct VideoResults {
#[serde_as(as = "Option<VecLogError<_>>")]
pub contents: Option<MapResult<Vec<VideoResultsItem>>>, pub contents: Option<MapResult<Vec<VideoResultsItem>>>,
} }
@ -303,7 +299,6 @@ pub(crate) struct RecommendationResultsWrap {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct RecommendationResults { pub(crate) struct RecommendationResults {
/// Can be `None` for age-restricted videos /// Can be `None` for age-restricted videos
#[serde_as(as = "Option<VecLogError<_>>")]
pub results: Option<MapResult<Vec<YouTubeListItem>>>, pub results: Option<MapResult<Vec<YouTubeListItem>>>,
#[serde_as(as = "Option<VecSkipError<_>>")] #[serde_as(as = "Option<VecSkipError<_>>")]
pub continuations: Option<Vec<MusicContinuationData>>, pub continuations: Option<Vec<MusicContinuationData>>,
@ -341,16 +336,7 @@ pub(crate) enum EngagementPanelRenderer {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct ChapterMarkersContent { pub(crate) struct ChapterMarkersContent {
pub macro_markers_list_renderer: MacroMarkersListRenderer, pub macro_markers_list_renderer: ContentsRendererLogged<MacroMarkersListItem>,
}
/// Chapter markers
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct MacroMarkersListRenderer {
#[serde_as(as = "VecLogError<_>")]
pub contents: MapResult<Vec<MacroMarkersListItem>>,
} }
/// Chapter marker /// Chapter marker
@ -436,7 +422,6 @@ pub(crate) struct CommentItemSectionHeaderMenuItem {
*/ */
/// Video comments continuation response /// Video comments continuation response
#[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct VideoComments { pub(crate) struct VideoComments {
@ -450,7 +435,6 @@ pub(crate) struct VideoComments {
/// - Comment replies: appendContinuationItemsAction /// - Comment replies: appendContinuationItemsAction
/// - n*commentRenderer, continuationItemRenderer: /// - n*commentRenderer, continuationItemRenderer:
/// replies + continuation /// replies + continuation
#[serde_as(as = "VecLogError<_>")]
pub on_response_received_endpoints: MapResult<Vec<CommentsContItem>>, pub on_response_received_endpoints: MapResult<Vec<CommentsContItem>>,
} }
@ -463,11 +447,9 @@ pub(crate) struct CommentsContItem {
} }
/// Video comments continuation action /// Video comments continuation action
#[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct AppendComments { pub(crate) struct AppendComments {
#[serde_as(as = "VecLogError<_>")]
pub continuation_items: MapResult<Vec<CommentListItem>>, pub continuation_items: MapResult<Vec<CommentListItem>>,
} }

View file

@ -15,7 +15,7 @@ use crate::{
param::Language, param::Language,
serializer::{ serializer::{
text::{AccessibilityText, Text, TextComponent}, text::{AccessibilityText, Text, TextComponent},
MapResult, VecLogError, MapResult,
}, },
timeago, timeago,
util::{self, TryRemove}, util::{self, TryRemove},
@ -69,7 +69,6 @@ pub(crate) enum YouTubeListItem {
#[serde(alias = "expandedShelfContentsRenderer", alias = "gridRenderer")] #[serde(alias = "expandedShelfContentsRenderer", alias = "gridRenderer")]
ItemSectionRenderer { ItemSectionRenderer {
#[serde(alias = "items")] #[serde(alias = "items")]
#[serde_as(as = "VecLogError<_>")]
contents: MapResult<Vec<YouTubeListItem>>, contents: MapResult<Vec<YouTubeListItem>>,
}, },
@ -206,11 +205,9 @@ pub(crate) struct YouTubeListRendererWrap {
pub section_list_renderer: YouTubeListRenderer, pub section_list_renderer: YouTubeListRenderer,
} }
#[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct YouTubeListRenderer { pub(crate) struct YouTubeListRenderer {
#[serde_as(as = "VecLogError<_>")]
pub contents: MapResult<Vec<YouTubeListItem>>, pub contents: MapResult<Vec<YouTubeListItem>>,
} }

View file

@ -6,7 +6,7 @@ mod vec_log_err;
pub use date::DateYmd; pub use date::DateYmd;
pub use range::Range; pub use range::Range;
pub use vec_log_err::{VecLogError, VecSkipErrorWrap}; pub use vec_log_err::VecSkipErrorWrap;
use std::fmt::Debug; use std::fmt::Debug;

View file

@ -4,7 +4,6 @@ use serde::{
de::{IgnoredAny, SeqAccess, Visitor}, de::{IgnoredAny, SeqAccess, Visitor},
Deserialize, Deserialize,
}; };
use serde_with::{de::DeserializeAsWrap, DeserializeAs};
use super::MapResult; use super::MapResult;
@ -13,39 +12,26 @@ use super::MapResult;
/// ///
/// This is similar to `VecSkipError`, but it does not silently ignore /// This is similar to `VecSkipError`, but it does not silently ignore
/// faulty items. /// faulty items.
pub struct VecLogError<T>(PhantomData<T>); impl<'de, T> Deserialize<'de> for MapResult<Vec<T>>
impl<'de, T, U> DeserializeAs<'de, MapResult<Vec<T>>> for VecLogError<U>
where where
U: DeserializeAs<'de, T>, T: Deserialize<'de>,
{ {
fn deserialize_as<D>(deserializer: D) -> Result<MapResult<Vec<T>>, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where where
D: serde::Deserializer<'de>, D: serde::Deserializer<'de>,
{ {
#[derive(serde::Deserialize)] #[derive(serde::Deserialize)]
#[serde( #[serde(untagged)]
untagged, enum GoodOrError<T> {
bound(deserialize = "DeserializeAsWrap<T, TAs>: Deserialize<'de>") Good(T),
)] Error(serde_json::Value),
enum GoodOrError<'a, T, TAs>
where
TAs: DeserializeAs<'a, T>,
{
Good(DeserializeAsWrap<T, TAs>),
Error(serde_json::value::Value),
#[serde(skip)]
_JustAMarkerForTheLifetime(PhantomData<&'a u32>),
} }
struct SeqVisitor<T, U> { struct SeqVisitor<T>(PhantomData<T>);
marker: PhantomData<T>,
marker2: PhantomData<U>,
}
impl<'de, T, U> Visitor<'de> for SeqVisitor<T, U> impl<'de, T> Visitor<'de> for SeqVisitor<T>
where where
U: DeserializeAs<'de, T>, T: Deserialize<'de>,
{ {
type Value = MapResult<Vec<T>>; type Value = MapResult<Vec<T>>;
@ -62,16 +48,15 @@ where
while let Some(value) = seq.next_element()? { while let Some(value) = seq.next_element()? {
match value { match value {
GoodOrError::<T, U>::Good(value) => { GoodOrError::<T>::Good(value) => {
values.push(value.into_inner()); values.push(value);
} }
GoodOrError::<T, U>::Error(value) => { GoodOrError::<T>::Error(value) => {
warnings.push(format!( warnings.push(format!(
"error deserializing item: {}", "error deserializing item: {}",
serde_json::to_string(&value).unwrap_or_default() serde_json::to_string(&value).unwrap_or_default()
)); ));
} }
_ => {}
} }
} }
Ok(MapResult { Ok(MapResult {
@ -81,15 +66,11 @@ where
} }
} }
let visitor = SeqVisitor::<T, U> { deserializer.deserialize_seq(SeqVisitor(PhantomData::<T>))
marker: PhantomData,
marker2: PhantomData,
};
deserializer.deserialize_seq(visitor)
} }
} }
/// Reimplementation of VecSkipError using a type wrapper /// Reimplementation of VecSkipError using a wrapper type
/// to allow use with generics /// to allow use with generics
pub struct VecSkipErrorWrap<T>(pub Vec<T>); pub struct VecSkipErrorWrap<T>(pub Vec<T>);
@ -145,32 +126,53 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use serde::Deserialize; use serde::Deserialize;
use serde_with::serde_as;
use crate::serializer::MapResult; use crate::serializer::MapResult;
#[serde_as] use super::VecSkipErrorWrap;
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[allow(dead_code)] #[allow(dead_code)]
struct S { struct SLog {
#[serde_as(as = "crate::serializer::VecLogError<_>")]
items: MapResult<Vec<Item>>, items: MapResult<Vec<Item>>,
} }
#[derive(Deserialize)]
#[allow(dead_code)]
struct SSkip {
items: VecSkipErrorWrap<Item>,
}
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[allow(dead_code)] #[allow(dead_code)]
struct Item { struct Item {
name: String, name: String,
} }
#[test] const JSON: &str =
fn test() { r#"{"items": [{"name": "i1"}, {"xyz": "i2"}, {"name": "i3"}, {"namra": "i4"}]}"#;
let json = r#"{"items": [{"name": "i1"}, {"xyz": "i2"}, {"name": "i3"}, {"namra": "i4"}]}"#;
let res = serde_json::from_str::<S>(json).unwrap(); #[test]
fn skip_error() {
let res = serde_json::from_str::<SSkip>(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::<SLog>(JSON).unwrap();
insta::assert_debug_snapshot!(res, @r###" insta::assert_debug_snapshot!(res, @r###"
S { SLog {
items: [ items: [
Item { Item {
name: "i1", name: "i1",