fix: use AsRef generics for pagination

This commit is contained in:
ThetaDev 2022-11-30 12:42:06 +01:00
parent 85cac83070
commit 94b55711cb
4 changed files with 68 additions and 60 deletions

View file

@ -208,7 +208,7 @@ async fn playlist_cont(testfiles: &Path) {
.unwrap(); .unwrap();
let rp = rp_testfile(&json_path); let rp = rp_testfile(&json_path);
playlist.videos.next(&rp.query()).await.unwrap().unwrap(); playlist.videos.next(rp.query()).await.unwrap().unwrap();
} }
async fn video_details(testfiles: &Path) { async fn video_details(testfiles: &Path) {
@ -246,7 +246,7 @@ async fn comments_top(testfiles: &Path) {
let rp = rp_testfile(&json_path); let rp = rp_testfile(&json_path);
details details
.top_comments .top_comments
.next(&rp.query()) .next(rp.query())
.await .await
.unwrap() .unwrap()
.unwrap(); .unwrap();
@ -266,7 +266,7 @@ async fn comments_latest(testfiles: &Path) {
let rp = rp_testfile(&json_path); let rp = rp_testfile(&json_path);
details details
.latest_comments .latest_comments
.next(&rp.query()) .next(rp.query())
.await .await
.unwrap() .unwrap()
.unwrap(); .unwrap();
@ -284,7 +284,7 @@ async fn recommendations(testfiles: &Path) {
let details = rp.query().video_details("ZeerrnuLi5E").await.unwrap(); let details = rp.query().video_details("ZeerrnuLi5E").await.unwrap();
let rp = rp_testfile(&json_path); let rp = rp_testfile(&json_path);
details.recommended.next(&rp.query()).await.unwrap(); details.recommended.next(rp.query()).await.unwrap();
} }
async fn channel_videos(testfiles: &Path) { async fn channel_videos(testfiles: &Path) {
@ -384,7 +384,7 @@ async fn channel_videos_cont(testfiles: &Path) {
.unwrap(); .unwrap();
let rp = rp_testfile(&json_path); let rp = rp_testfile(&json_path);
videos.content.next(&rp.query()).await.unwrap().unwrap(); videos.content.next(rp.query()).await.unwrap().unwrap();
} }
async fn channel_playlists_cont(testfiles: &Path) { async fn channel_playlists_cont(testfiles: &Path) {
@ -403,7 +403,7 @@ async fn channel_playlists_cont(testfiles: &Path) {
.unwrap(); .unwrap();
let rp = rp_testfile(&json_path); let rp = rp_testfile(&json_path);
playlists.content.next(&rp.query()).await.unwrap().unwrap(); playlists.content.next(rp.query()).await.unwrap().unwrap();
} }
async fn search(testfiles: &Path) { async fn search(testfiles: &Path) {
@ -430,7 +430,7 @@ async fn search_cont(testfiles: &Path) {
let search = rp.query().search("doobydoobap").await.unwrap(); let search = rp.query().search("doobydoobap").await.unwrap();
let rp = rp_testfile(&json_path); let rp = rp_testfile(&json_path);
search.items.next(&rp.query()).await.unwrap().unwrap(); search.items.next(rp.query()).await.unwrap().unwrap();
} }
async fn search_playlists(testfiles: &Path) { async fn search_playlists(testfiles: &Path) {
@ -492,7 +492,7 @@ async fn startpage_cont(testfiles: &Path) {
let startpage = rp.query().startpage().await.unwrap(); let startpage = rp.query().startpage().await.unwrap();
let rp = rp_testfile(&json_path); let rp = rp_testfile(&json_path);
startpage.next(&rp.query()).await.unwrap(); startpage.next(rp.query()).await.unwrap();
} }
async fn trending(testfiles: &Path) { async fn trending(testfiles: &Path) {
@ -541,7 +541,7 @@ async fn music_playlist_cont(testfiles: &Path) {
.unwrap(); .unwrap();
let rp = rp_testfile(&json_path); let rp = rp_testfile(&json_path);
playlist.tracks.next(&rp.query()).await.unwrap().unwrap(); playlist.tracks.next(rp.query()).await.unwrap().unwrap();
} }
async fn music_playlist_related(testfiles: &Path) { async fn music_playlist_related(testfiles: &Path) {
@ -562,7 +562,7 @@ async fn music_playlist_related(testfiles: &Path) {
let rp = rp_testfile(&json_path); let rp = rp_testfile(&json_path);
playlist playlist
.related_playlists .related_playlists
.next(&rp.query()) .next(rp.query())
.await .await
.unwrap() .unwrap()
.unwrap(); .unwrap();
@ -688,7 +688,7 @@ async fn music_search_cont(testfiles: &Path) {
let res = rp.query().music_search_tracks("black mamba").await.unwrap(); let res = rp.query().music_search_tracks("black mamba").await.unwrap();
let rp = rp_testfile(&json_path); let rp = rp_testfile(&json_path);
res.items.next(&rp.query()).await.unwrap().unwrap(); res.items.next(rp.query()).await.unwrap().unwrap();
} }
async fn music_search_suggestion(testfiles: &Path) { async fn music_search_suggestion(testfiles: &Path) {
@ -801,7 +801,7 @@ async fn music_radio_cont(testfiles: &Path) {
let res = rp.query().music_radio("RDAMVM7nigXQS1Xb0").await.unwrap(); let res = rp.query().music_radio("RDAMVM7nigXQS1Xb0").await.unwrap();
let rp = rp_testfile(&json_path); let rp = rp_testfile(&json_path);
res.next(&rp.query()).await.unwrap().unwrap(); res.next(rp.query()).await.unwrap().unwrap();
} }
async fn music_new_albums(testfiles: &Path) { async fn music_new_albums(testfiles: &Path) {

View file

@ -1164,6 +1164,12 @@ impl RustyPipeQuery {
} }
} }
impl AsRef<RustyPipeQuery> for RustyPipeQuery {
fn as_ref(&self) -> &RustyPipeQuery {
self
}
}
/// Implement this for YouTube API response structs that need to be mapped to /// Implement this for YouTube API response structs that need to be mapped to
/// RustyPipe models. /// RustyPipe models.
trait MapResponse<T> { trait MapResponse<T> {

View file

@ -176,10 +176,11 @@ impl MapResponse<Paginator<MusicItem>> for response::MusicContinuation {
} }
impl<T: FromYtItem> Paginator<T> { impl<T: FromYtItem> Paginator<T> {
pub async fn next(&self, query: &RustyPipeQuery) -> Result<Option<Self>, Error> { pub async fn next<Q: AsRef<RustyPipeQuery>>(&self, query: Q) -> Result<Option<Self>, Error> {
Ok(match &self.ctoken { Ok(match &self.ctoken {
Some(ctoken) => Some( Some(ctoken) => Some(
query query
.as_ref()
.continuation(ctoken, self.endpoint, self.visitor_data.as_deref()) .continuation(ctoken, self.endpoint, self.visitor_data.as_deref())
.await?, .await?,
), ),
@ -187,7 +188,7 @@ impl<T: FromYtItem> Paginator<T> {
}) })
} }
pub async fn extend(&mut self, query: &RustyPipeQuery) -> Result<bool, Error> { pub async fn extend<Q: AsRef<RustyPipeQuery>>(&mut self, query: Q) -> Result<bool, Error> {
match self.next(query).await { match self.next(query).await {
Ok(Some(paginator)) => { Ok(Some(paginator)) => {
let mut items = paginator.items; let mut items = paginator.items;
@ -200,11 +201,12 @@ impl<T: FromYtItem> Paginator<T> {
} }
} }
pub async fn extend_pages( pub async fn extend_pages<Q: AsRef<RustyPipeQuery>>(
&mut self, &mut self,
query: &RustyPipeQuery, query: Q,
n_pages: usize, n_pages: usize,
) -> Result<(), Error> { ) -> Result<(), Error> {
let query = query.as_ref();
for _ in 0..n_pages { for _ in 0..n_pages {
match self.extend(query).await { match self.extend(query).await {
Ok(false) => break, Ok(false) => break,
@ -215,11 +217,12 @@ impl<T: FromYtItem> Paginator<T> {
Ok(()) Ok(())
} }
pub async fn extend_limit( pub async fn extend_limit<Q: AsRef<RustyPipeQuery>>(
&mut self, &mut self,
query: &RustyPipeQuery, query: Q,
n_items: usize, n_items: usize,
) -> Result<(), Error> { ) -> Result<(), Error> {
let query = query.as_ref();
while self.items.len() < n_items { while self.items.len() < n_items {
match self.extend(query).await { match self.extend(query).await {
Ok(false) => break, Ok(false) => break,
@ -232,10 +235,11 @@ impl<T: FromYtItem> Paginator<T> {
} }
impl Paginator<Comment> { impl Paginator<Comment> {
pub async fn next(&self, query: &RustyPipeQuery) -> Result<Option<Self>, Error> { pub async fn next<Q: AsRef<RustyPipeQuery>>(&self, query: Q) -> Result<Option<Self>, Error> {
Ok(match &self.ctoken { Ok(match &self.ctoken {
Some(ctoken) => Some( Some(ctoken) => Some(
query query
.as_ref()
.video_comments(ctoken, self.visitor_data.as_deref()) .video_comments(ctoken, self.visitor_data.as_deref())
.await?, .await?,
), ),
@ -245,9 +249,9 @@ impl Paginator<Comment> {
} }
impl Paginator<PlaylistVideo> { impl Paginator<PlaylistVideo> {
pub async fn next(&self, query: &RustyPipeQuery) -> Result<Option<Self>, Error> { pub async fn next<Q: AsRef<RustyPipeQuery>>(&self, query: Q) -> Result<Option<Self>, Error> {
Ok(match &self.ctoken { Ok(match &self.ctoken {
Some(ctoken) => Some(query.playlist_continuation(ctoken).await?), Some(ctoken) => Some(query.as_ref().playlist_continuation(ctoken).await?),
None => None, None => None,
}) })
} }
@ -256,7 +260,10 @@ impl Paginator<PlaylistVideo> {
macro_rules! paginator { macro_rules! paginator {
($entity_type:ty) => { ($entity_type:ty) => {
impl Paginator<$entity_type> { impl Paginator<$entity_type> {
pub async fn extend(&mut self, query: &RustyPipeQuery) -> Result<bool, Error> { pub async fn extend<Q: AsRef<RustyPipeQuery>>(
&mut self,
query: Q,
) -> Result<bool, Error> {
match self.next(query).await { match self.next(query).await {
Ok(Some(paginator)) => { Ok(Some(paginator)) => {
let mut items = paginator.items; let mut items = paginator.items;
@ -269,11 +276,12 @@ macro_rules! paginator {
} }
} }
pub async fn extend_pages( pub async fn extend_pages<Q: AsRef<RustyPipeQuery>>(
&mut self, &mut self,
query: &RustyPipeQuery, query: Q,
n_pages: usize, n_pages: usize,
) -> Result<(), Error> { ) -> Result<(), Error> {
let query = query.as_ref();
for _ in 0..n_pages { for _ in 0..n_pages {
match self.extend(query).await { match self.extend(query).await {
Ok(false) => break, Ok(false) => break,
@ -284,11 +292,12 @@ macro_rules! paginator {
Ok(()) Ok(())
} }
pub async fn extend_limit( pub async fn extend_limit<Q: AsRef<RustyPipeQuery>>(
&mut self, &mut self,
query: &RustyPipeQuery, query: Q,
n_items: usize, n_items: usize,
) -> Result<(), Error> { ) -> Result<(), Error> {
let query = query.as_ref();
while self.items.len() < n_items { while self.items.len() < n_items {
match self.extend(query).await { match self.extend(query).await {
Ok(false) => break, Ok(false) => break,

View file

@ -380,7 +380,7 @@ async fn playlist_cont() {
playlist playlist
.videos .videos
.extend_pages(&rp.query(), usize::MAX) .extend_pages(rp.query(), usize::MAX)
.await .await
.unwrap(); .unwrap();
assert!(playlist.videos.items.len() > 100); assert!(playlist.videos.items.len() > 100);
@ -396,11 +396,7 @@ async fn playlist_cont2() {
.await .await
.unwrap(); .unwrap();
playlist playlist.videos.extend_limit(rp.query(), 101).await.unwrap();
.videos
.extend_limit(&rp.query(), 101)
.await
.unwrap();
assert!(playlist.videos.items.len() > 100); assert!(playlist.videos.items.len() > 100);
assert!(playlist.videos.count.unwrap() > 100); assert!(playlist.videos.count.unwrap() > 100);
} }
@ -461,7 +457,7 @@ async fn get_video_details() {
assert!(!details.is_ccommons); assert!(!details.is_ccommons);
assert!(details.recommended.visitor_data.is_some()); assert!(details.recommended.visitor_data.is_some());
assert_next(details.recommended, &rp.query(), 10, 2).await; assert_next(details.recommended, rp.query(), 10, 2).await;
assert_gte(details.top_comments.count.unwrap(), 700_000, "comments"); assert_gte(details.top_comments.count.unwrap(), 700_000, "comments");
assert!(!details.top_comments.is_exhausted()); assert!(!details.top_comments.is_exhausted());
@ -499,7 +495,7 @@ async fn get_video_details_music() {
assert!(!details.is_ccommons); assert!(!details.is_ccommons);
assert!(details.recommended.visitor_data.is_some()); assert!(details.recommended.visitor_data.is_some());
assert_next(details.recommended, &rp.query(), 10, 2).await; assert_next(details.recommended, rp.query(), 10, 2).await;
// Update(01.11.2022): comments are sometimes enabled // Update(01.11.2022): comments are sometimes enabled
/* /*
@ -548,7 +544,7 @@ async fn get_video_details_ccommons() {
assert!(details.is_ccommons); assert!(details.is_ccommons);
assert!(details.recommended.visitor_data.is_some()); assert!(details.recommended.visitor_data.is_some());
assert_next(details.recommended, &rp.query(), 10, 2).await; assert_next(details.recommended, rp.query(), 10, 2).await;
assert_gte(details.top_comments.count.unwrap(), 2199, "comments"); assert_gte(details.top_comments.count.unwrap(), 2199, "comments");
assert!(!details.top_comments.is_exhausted()); assert!(!details.top_comments.is_exhausted());
@ -673,7 +669,7 @@ async fn get_video_details_chapters() {
} }
assert!(details.recommended.visitor_data.is_some()); assert!(details.recommended.visitor_data.is_some());
assert_next(details.recommended, &rp.query(), 10, 2).await; assert_next(details.recommended, rp.query(), 10, 2).await;
assert_gte(details.top_comments.count.unwrap(), 3200, "comments"); assert_gte(details.top_comments.count.unwrap(), 3200, "comments");
assert!(!details.top_comments.is_exhausted()); assert!(!details.top_comments.is_exhausted());
@ -718,7 +714,7 @@ async fn get_video_details_live() {
assert!(!details.is_ccommons); assert!(!details.is_ccommons);
assert!(details.recommended.visitor_data.is_some()); assert!(details.recommended.visitor_data.is_some());
assert_next(details.recommended, &rp.query(), 10, 2).await; assert_next(details.recommended, rp.query(), 10, 2).await;
// No comments because livestream // No comments because livestream
assert_eq!(details.top_comments.count, Some(0)); assert_eq!(details.top_comments.count, Some(0));
@ -786,7 +782,7 @@ async fn get_video_comments() {
let top_comments = details let top_comments = details
.top_comments .top_comments
.next(&rp.query()) .next(rp.query())
.await .await
.unwrap() .unwrap()
.unwrap(); .unwrap();
@ -805,7 +801,7 @@ async fn get_video_comments() {
let latest_comments = details let latest_comments = details
.latest_comments .latest_comments
.next(&rp.query()) .next(rp.query())
.await .await
.unwrap() .unwrap()
.unwrap(); .unwrap();
@ -839,7 +835,7 @@ async fn channel_videos() {
assert!(age_days < 60, "latest video older than 60 days"); assert!(age_days < 60, "latest video older than 60 days");
assert_next(channel.content, &rp.query(), 15, 2).await; assert_next(channel.content, rp.query(), 15, 2).await;
} }
#[tokio::test] #[tokio::test]
@ -873,7 +869,7 @@ async fn channel_shorts() {
"got no shorts" "got no shorts"
); );
assert_next(channel.content, &rp.query(), 15, 1).await; assert_next(channel.content, rp.query(), 15, 1).await;
} }
#[tokio::test] #[tokio::test]
@ -893,7 +889,7 @@ async fn channel_livestreams() {
"got no streams" "got no streams"
); );
assert_next(channel.content, &rp.query(), 5, 1).await; assert_next(channel.content, rp.query(), 5, 1).await;
} }
#[tokio::test] #[tokio::test]
@ -912,7 +908,7 @@ async fn channel_playlists() {
"got no playlists" "got no playlists"
); );
assert_next(channel.content, &rp.query(), 15, 1).await; assert_next(channel.content, rp.query(), 15, 1).await;
} }
#[tokio::test] #[tokio::test]
@ -1099,7 +1095,7 @@ async fn search() {
); );
assert_eq!(result.corrected_query.unwrap(), "doobydobap"); assert_eq!(result.corrected_query.unwrap(), "doobydobap");
assert_next(result.items, &rp.query(), 10, 2).await; assert_next(result.items, rp.query(), 10, 2).await;
} }
#[rstest] #[rstest]
@ -1115,7 +1111,7 @@ async fn search_filter_entity(#[case] entity: search_filter::Entity) {
.await .await
.unwrap(); .unwrap();
result.items.extend(&rp.query()).await.unwrap(); result.items.extend(rp.query()).await.unwrap();
assert_gte(result.items.items.len(), 20, "items"); assert_gte(result.items.items.len(), 20, "items");
result.items.items.iter().for_each(|item| match item { result.items.items.iter().for_each(|item| match item {
@ -1243,7 +1239,7 @@ async fn startpage() {
// The startpage requires visitor data to fetch continuations // The startpage requires visitor data to fetch continuations
assert!(startpage.visitor_data.is_some()); assert!(startpage.visitor_data.is_some());
assert_next(startpage, &rp.query(), 20, 2).await; assert_next(startpage, rp.query(), 20, 2).await;
} }
#[tokio::test] #[tokio::test]
@ -1321,7 +1317,7 @@ async fn music_playlist_cont() {
playlist playlist
.tracks .tracks
.extend_pages(&rp.query(), usize::MAX) .extend_pages(rp.query(), usize::MAX)
.await .await
.unwrap(); .unwrap();
@ -1338,11 +1334,7 @@ async fn music_playlist_related() {
.await .await
.unwrap(); .unwrap();
playlist playlist.related_playlists.extend(rp.query()).await.unwrap();
.related_playlists
.extend(&rp.query())
.await
.unwrap();
assert_gte( assert_gte(
playlist.related_playlists.items.len(), playlist.related_playlists.items.len(),
@ -1571,7 +1563,7 @@ async fn music_search_tracks() {
assert_eq!(album.id, "MPREb_OpHWHwyNOuY"); assert_eq!(album.id, "MPREb_OpHWHwyNOuY");
assert_eq!(album.name, "Black Mamba"); assert_eq!(album.name, "Black Mamba");
assert_next(res.items, &rp.query(), 15, 2).await; assert_next(res.items, rp.query(), 15, 2).await;
} }
#[tokio::test] #[tokio::test]
@ -1605,7 +1597,7 @@ async fn music_search_videos() {
assert_eq!(track.album, None); assert_eq!(track.album, None);
assert_gte(track.view_count.unwrap(), 230_000_000, "views"); assert_gte(track.view_count.unwrap(), 230_000_000, "views");
assert_next(res.items, &rp.query(), 15, 2).await; assert_next(res.items, rp.query(), 15, 2).await;
} }
#[tokio::test] #[tokio::test]
@ -1692,7 +1684,7 @@ async fn music_search_albums(
assert_eq!(res.corrected_query, None); assert_eq!(res.corrected_query, None);
assert_next(res.items, &rp.query(), 15, n_pages).await; assert_next(res.items, rp.query(), 15, n_pages).await;
} }
#[tokio::test] #[tokio::test]
@ -1724,7 +1716,7 @@ async fn music_search_artists_cont() {
let res = rp.query().music_search_artists("band").await.unwrap(); let res = rp.query().music_search_artists("band").await.unwrap();
assert_eq!(res.corrected_query, None); assert_eq!(res.corrected_query, None);
assert_next(res.items, &rp.query(), 15, 2).await; assert_next(res.items, rp.query(), 15, 2).await;
} }
#[rstest] #[rstest]
@ -2000,7 +1992,7 @@ async fn music_details_not_found() {
async fn music_radio_track() { async fn music_radio_track() {
let rp = RustyPipe::builder().strict().build(); let rp = RustyPipe::builder().strict().build();
let tracks = rp.query().music_radio_track("ZeerrnuLi5E").await.unwrap(); let tracks = rp.query().music_radio_track("ZeerrnuLi5E").await.unwrap();
assert_next(tracks, &rp.query(), 20, 3).await; assert_next(tracks, rp.query(), 20, 3).await;
} }
#[tokio::test] #[tokio::test]
@ -2030,7 +2022,7 @@ async fn music_radio_playlist() {
.music_radio_playlist("PL5dDx681T4bR7ZF1IuWzOv1omlRbE7PiJ") .music_radio_playlist("PL5dDx681T4bR7ZF1IuWzOv1omlRbE7PiJ")
.await .await
.unwrap(); .unwrap();
assert_next(tracks, &rp.query(), 10, 1).await; assert_next(tracks, rp.query(), 10, 1).await;
} }
#[tokio::test] #[tokio::test]
@ -2121,13 +2113,14 @@ fn assert_gte<T: PartialOrd + Display>(a: T, b: T, msg: &str) {
assert!(a >= b, "expected {} {}, got {}", b, msg, a); assert!(a >= b, "expected {} {}, got {}", b, msg, a);
} }
async fn assert_next<T: FromYtItem>( async fn assert_next<T: FromYtItem, Q: AsRef<RustyPipeQuery>>(
paginator: Paginator<T>, paginator: Paginator<T>,
query: &RustyPipeQuery, query: Q,
min_items: usize, min_items: usize,
n_pages: usize, n_pages: usize,
) { ) {
let mut p = paginator; let mut p = paginator;
let query = query.as_ref();
assert_gte(p.items.len(), min_items, "items on page 0"); assert_gte(p.items.len(), min_items, "items on page 0");
for i in 0..n_pages { for i in 0..n_pages {