refactor: replace VecLogError with standard Deserialize impl
This commit is contained in:
parent
963ff14dc1
commit
a45eba4705
11 changed files with 75 additions and 124 deletions
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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>()?;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)]
|
||||||
|
|
|
||||||
|
|
@ -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()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>,
|
||||||
|
|
|
||||||
|
|
@ -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>>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
Reference in a new issue