From 77a97659d17d4acc4f2b908e6720e0b1b7050bfd Mon Sep 17 00:00:00 2001 From: Wojciech Kozlowski Date: Sun, 29 Sep 2024 21:06:29 +0200 Subject: [PATCH] Complete support for paging --- examples/musicbrainz_api/browse.rs | 13 +++--- src/external/musicbrainz/api/browse.rs | 42 +++++++++++++------ src/external/musicbrainz/api/mod.rs | 16 +++++++ src/external/musicbrainz/api/search/artist.rs | 16 ++++++- src/external/musicbrainz/api/search/mod.rs | 18 ++++++++ .../musicbrainz/api/search/release_group.rs | 16 ++++++- src/tui/lib/external/musicbrainz/api/mod.rs | 4 +- 7 files changed, 101 insertions(+), 24 deletions(-) diff --git a/examples/musicbrainz_api/browse.rs b/examples/musicbrainz_api/browse.rs index cff2548..af8d968 100644 --- a/examples/musicbrainz_api/browse.rs +++ b/examples/musicbrainz_api/browse.rs @@ -3,7 +3,7 @@ use std::{thread, time}; use musichoard::{ collection::musicbrainz::Mbid, external::musicbrainz::{ - api::{browse::BrowseReleaseGroupRequest, MusicBrainzClient, PageSettings}, + api::{browse::BrowseReleaseGroupRequest, MusicBrainzClient, NextPage, PageSettings}, http::MusicBrainzHttp, }, }; @@ -66,20 +66,19 @@ fn main() { println!("{rg:?}\n"); } - let offset = response.release_group_offset; + let offset = response.page.release_group_offset; let count = response.release_groups.len(); response_counts.push(count); - let total = response.release_group_count; + let total = response.page.release_group_count; println!("Release group offset : {offset}"); println!("Release groups in this response: {count}"); println!("Release groups in total : {total}"); - let next_offset = offset + count; - if next_offset == total { - break; + match response.page.next_page_offset(count) { + NextPage::Offset(next_offset) => paging.with_offset(next_offset), + NextPage::Complete => break, } - paging.with_offset(next_offset); thread::sleep(time::Duration::from_secs(1)); } diff --git a/src/external/musicbrainz/api/browse.rs b/src/external/musicbrainz/api/browse.rs index 0156629..e6299b0 100644 --- a/src/external/musicbrainz/api/browse.rs +++ b/src/external/musicbrainz/api/browse.rs @@ -6,14 +6,31 @@ use crate::{ collection::musicbrainz::Mbid, external::musicbrainz::{ api::{ - Error, MbReleaseGroupMeta, MusicBrainzClient, PageSettings, SerdeMbReleaseGroupMeta, - MB_BASE_URL, + ApiDisplay, Error, MbReleaseGroupMeta, MusicBrainzClient, NextPage, PageSettings, + SerdeMbReleaseGroupMeta, MB_BASE_URL, }, IMusicBrainzHttp, }, }; -use super::ApiDisplay; +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)] +#[serde(rename_all(deserialize = "kebab-case"))] +pub struct BrowseReleaseGroupPage { + pub release_group_offset: usize, + pub release_group_count: usize, +} + +impl BrowseReleaseGroupPage { + pub fn next_page_offset(&self, page_count: usize) -> NextPage { + NextPage::next_page_offset( + self.release_group_offset, + self.release_group_count, + page_count, + ) + } +} + +pub type SerdeBrowseReleaseGroupPage = BrowseReleaseGroupPage; impl MusicBrainzClient { pub fn browse_release_group( @@ -60,24 +77,22 @@ impl<'a> BrowseReleaseGroupRequest<'a> { #[derive(Debug, PartialEq, Eq)] pub struct BrowseReleaseGroupResponse { - pub release_group_offset: usize, - pub release_group_count: usize, pub release_groups: Vec, + pub page: BrowseReleaseGroupPage, } #[derive(Clone, Deserialize)] #[serde(rename_all(deserialize = "kebab-case"))] struct DeserializeBrowseReleaseGroupResponse { - release_group_offset: usize, - release_group_count: usize, release_groups: Option>, + #[serde(flatten)] + page: SerdeBrowseReleaseGroupPage, } impl From for BrowseReleaseGroupResponse { fn from(value: DeserializeBrowseReleaseGroupResponse) -> Self { BrowseReleaseGroupResponse { - release_group_offset: value.release_group_offset, - release_group_count: value.release_group_count, + page: value.page, release_groups: value .release_groups .map(|rgs| rgs.into_iter().map(Into::into).collect()) @@ -120,14 +135,15 @@ mod tests { )]), }; let de_response = DeserializeBrowseReleaseGroupResponse { - release_group_offset: de_release_group_offset, - release_group_count: de_release_group_count, + page: SerdeBrowseReleaseGroupPage { + release_group_offset: de_release_group_offset, + release_group_count: de_release_group_count, + }, release_groups: Some(vec![de_meta.clone()]), }; let response = BrowseReleaseGroupResponse { - release_group_offset: de_release_group_offset, - release_group_count: de_release_group_count, + page: de_response.page, release_groups: vec![de_meta.clone().into()], }; diff --git a/src/external/musicbrainz/api/mod.rs b/src/external/musicbrainz/api/mod.rs index 34ab3cd..7faf9de 100644 --- a/src/external/musicbrainz/api/mod.rs +++ b/src/external/musicbrainz/api/mod.rs @@ -84,6 +84,22 @@ impl PageSettings { } } +pub enum NextPage { + Offset(usize), + Complete, +} + +impl NextPage { + pub fn next_page_offset(offset: usize, total_count: usize, page_count: usize) -> NextPage { + let next_offset = offset + page_count; + if next_offset < total_count { + NextPage::Offset(next_offset) + } else { + NextPage::Complete + } + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct MbArtistMeta { pub id: Mbid, diff --git a/src/external/musicbrainz/api/search/artist.rs b/src/external/musicbrainz/api/search/artist.rs index 8eaa4ea..8399c4b 100644 --- a/src/external/musicbrainz/api/search/artist.rs +++ b/src/external/musicbrainz/api/search/artist.rs @@ -3,7 +3,10 @@ use std::fmt; use serde::Deserialize; use crate::external::musicbrainz::api::{ - search::query::{impl_term, EmptyQuery, EmptyQueryJoin, Query, QueryJoin}, + search::{ + query::{impl_term, EmptyQuery, EmptyQueryJoin, Query, QueryJoin}, + SearchPage, SerdeSearchPage, + }, MbArtistMeta, SerdeMbArtistMeta, }; @@ -26,18 +29,22 @@ impl_term!(string, SearchArtist<'a>, String, &'a str); #[derive(Debug, PartialEq, Eq)] pub struct SearchArtistResponse { pub artists: Vec, + pub page: SearchPage, } #[derive(Clone, Deserialize)] #[serde(rename_all(deserialize = "kebab-case"))] pub struct DeserializeSearchArtistResponse { artists: Vec, + #[serde(flatten)] + page: SerdeSearchPage, } impl From for SearchArtistResponse { fn from(value: DeserializeSearchArtistResponse) -> Self { SearchArtistResponse { artists: value.artists.into_iter().map(Into::into).collect(), + page: value.page, } } } @@ -77,6 +84,8 @@ mod tests { use super::*; fn de_response() -> DeserializeSearchArtistResponse { + let de_offset = 24; + let de_count = 124; let de_artist = DeserializeSearchArtistResponseArtist { score: 67, meta: SerdeMbArtistMeta { @@ -88,6 +97,10 @@ mod tests { }; DeserializeSearchArtistResponse { artists: vec![de_artist.clone()], + page: SerdeSearchPage { + offset: de_offset, + count: de_count, + }, } } @@ -101,6 +114,7 @@ mod tests { meta: a.meta.into(), }) .collect(), + page: de_response.page, } } diff --git a/src/external/musicbrainz/api/search/mod.rs b/src/external/musicbrainz/api/search/mod.rs index c4c1521..daefcba 100644 --- a/src/external/musicbrainz/api/search/mod.rs +++ b/src/external/musicbrainz/api/search/mod.rs @@ -9,6 +9,7 @@ pub use release_group::{ }; use paste::paste; +use serde::Deserialize; use url::form_urlencoded; use crate::external::musicbrainz::{ @@ -22,6 +23,23 @@ use crate::external::musicbrainz::{ IMusicBrainzHttp, }; +use super::NextPage; + +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)] +#[serde(rename_all(deserialize = "kebab-case"))] +pub struct SearchPage { + pub offset: usize, + pub count: usize, +} + +impl SearchPage { + pub fn next_page_offset(&self, page_count: usize) -> NextPage { + NextPage::next_page_offset(self.offset, self.count, page_count) + } +} + +pub type SerdeSearchPage = SearchPage; + macro_rules! impl_search_entity { ($name:ident, $entity:literal) => { paste! { diff --git a/src/external/musicbrainz/api/search/release_group.rs b/src/external/musicbrainz/api/search/release_group.rs index be48a7f..ae36fe2 100644 --- a/src/external/musicbrainz/api/search/release_group.rs +++ b/src/external/musicbrainz/api/search/release_group.rs @@ -5,7 +5,10 @@ use serde::Deserialize; use crate::{ collection::{album::AlbumDate, musicbrainz::Mbid}, external::musicbrainz::api::{ - search::query::{impl_term, EmptyQuery, EmptyQueryJoin, Query, QueryJoin}, + search::{ + query::{impl_term, EmptyQuery, EmptyQueryJoin, Query, QueryJoin}, + SearchPage, SerdeSearchPage, + }, ApiDisplay, MbReleaseGroupMeta, SerdeMbReleaseGroupMeta, }, }; @@ -50,18 +53,22 @@ impl_term!(rgid, SearchReleaseGroup<'a>, Rgid, &'a Mbid); #[derive(Debug, PartialEq, Eq)] pub struct SearchReleaseGroupResponse { pub release_groups: Vec, + pub page: SearchPage, } #[derive(Clone, Deserialize)] #[serde(rename_all(deserialize = "kebab-case"))] pub struct DeserializeSearchReleaseGroupResponse { release_groups: Vec, + #[serde(flatten)] + page: SerdeSearchPage, } impl From for SearchReleaseGroupResponse { fn from(value: DeserializeSearchReleaseGroupResponse) -> Self { SearchReleaseGroupResponse { release_groups: value.release_groups.into_iter().map(Into::into).collect(), + page: value.page, } } } @@ -109,6 +116,8 @@ mod tests { use super::*; fn de_response() -> DeserializeSearchReleaseGroupResponse { + let de_offset = 26; + let de_count = 126; let de_release_group = DeserializeSearchReleaseGroupResponseReleaseGroup { score: 67, meta: SerdeMbReleaseGroupMeta { @@ -121,6 +130,10 @@ mod tests { }; DeserializeSearchReleaseGroupResponse { release_groups: vec![de_release_group.clone()], + page: SerdeSearchPage { + offset: de_offset, + count: de_count, + }, } } @@ -134,6 +147,7 @@ mod tests { meta: rg.meta.into(), }) .collect(), + page: de_response.page, } } diff --git a/src/tui/lib/external/musicbrainz/api/mod.rs b/src/tui/lib/external/musicbrainz/api/mod.rs index 3e4c0cb..35156c5 100644 --- a/src/tui/lib/external/musicbrainz/api/mod.rs +++ b/src/tui/lib/external/musicbrainz/api/mod.rs @@ -57,7 +57,7 @@ impl IMusicBrainz for MusicBrainz { fn search_artist(&mut self, artist: &ArtistMeta) -> Result>, Error> { let query = SearchArtistRequest::new().string(&artist.id.name); - let paging = PageSettings::with_max_limit(); + let paging = PageSettings::default(); let mb_response = self.client.search_artist(&query, &paging)?; Ok(mb_response @@ -83,7 +83,7 @@ impl IMusicBrainz for MusicBrainz { .and() .release_group(&album.id.title); - let paging = PageSettings::with_max_limit(); + let paging = PageSettings::default(); let mb_response = self.client.search_release_group(&query, &paging)?; Ok(mb_response