Compare commits
4 Commits
e2c103b4d7
...
a8443c378c
Author | SHA1 | Date | |
---|---|---|---|
a8443c378c | |||
03c1b2b9ae | |||
66b273bef4 | |||
77a97659d1 |
@ -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));
|
||||
}
|
||||
|
56
src/external/musicbrainz/api/browse.rs
vendored
56
src/external/musicbrainz/api/browse.rs
vendored
@ -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<Http: IMusicBrainzHttp> MusicBrainzClient<Http> {
|
||||
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<MbReleaseGroupMeta>,
|
||||
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<Vec<SerdeMbReleaseGroupMeta>>,
|
||||
#[serde(flatten)]
|
||||
page: SerdeBrowseReleaseGroupPage,
|
||||
}
|
||||
|
||||
impl From<DeserializeBrowseReleaseGroupResponse> 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())
|
||||
@ -94,8 +109,8 @@ mod tests {
|
||||
collection::album::{AlbumPrimaryType, AlbumSecondaryType},
|
||||
external::musicbrainz::{
|
||||
api::{
|
||||
SerdeAlbumDate, SerdeAlbumPrimaryType, SerdeAlbumSecondaryType, SerdeMbid,
|
||||
MB_MAX_PAGE_LIMIT,
|
||||
tests::next_page_test, SerdeAlbumDate, SerdeAlbumPrimaryType,
|
||||
SerdeAlbumSecondaryType, SerdeMbid, MB_MAX_PAGE_LIMIT,
|
||||
},
|
||||
MockIMusicBrainzHttp,
|
||||
},
|
||||
@ -103,6 +118,16 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn browse_release_group_next_page() {
|
||||
let page = BrowseReleaseGroupPage {
|
||||
release_group_offset: 5,
|
||||
release_group_count: 45,
|
||||
};
|
||||
|
||||
next_page_test(|val| page.next_page_offset(val));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn browse_release_group() {
|
||||
let mbid = "00000000-0000-0000-0000-000000000000";
|
||||
@ -120,14 +145,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()],
|
||||
};
|
||||
|
||||
|
36
src/external/musicbrainz/api/mod.rs
vendored
36
src/external/musicbrainz/api/mod.rs
vendored
@ -84,6 +84,23 @@ impl PageSettings {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
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,
|
||||
@ -342,6 +359,25 @@ mod tests {
|
||||
assert!(!format!("{unk_err:?}").is_empty());
|
||||
}
|
||||
|
||||
pub fn next_page_test<Fn>(mut f: Fn)
|
||||
where
|
||||
Fn: FnMut(usize) -> NextPage,
|
||||
{
|
||||
let next = f(20);
|
||||
assert_eq!(next, NextPage::Offset(25));
|
||||
|
||||
let next = f(40);
|
||||
assert_eq!(next, NextPage::Complete);
|
||||
|
||||
let next = f(100);
|
||||
assert_eq!(next, NextPage::Complete);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn next_page() {
|
||||
next_page_test(|val| NextPage::next_page_offset(5, 45, val));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_page_settings() {
|
||||
let paging = PageSettings::default();
|
||||
|
16
src/external/musicbrainz/api/search/artist.rs
vendored
16
src/external/musicbrainz/api/search/artist.rs
vendored
@ -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<SearchArtistResponseArtist>,
|
||||
pub page: SearchPage,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
#[serde(rename_all(deserialize = "kebab-case"))]
|
||||
pub struct DeserializeSearchArtistResponse {
|
||||
artists: Vec<DeserializeSearchArtistResponseArtist>,
|
||||
#[serde(flatten)]
|
||||
page: SerdeSearchPage,
|
||||
}
|
||||
|
||||
impl From<DeserializeSearchArtistResponse> 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,
|
||||
}
|
||||
}
|
||||
|
||||
|
35
src/external/musicbrainz/api/search/mod.rs
vendored
35
src/external/musicbrainz/api/search/mod.rs
vendored
@ -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! {
|
||||
@ -46,3 +64,20 @@ impl<Http: IMusicBrainzHttp> MusicBrainzClient<Http> {
|
||||
impl_search_entity!(Artist, "artist");
|
||||
impl_search_entity!(ReleaseGroup, "release-group");
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::external::musicbrainz::api::tests::next_page_test;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn search_next_page() {
|
||||
let page = SearchPage {
|
||||
offset: 5,
|
||||
count: 45,
|
||||
};
|
||||
|
||||
next_page_test(|val| page.next_page_offset(val));
|
||||
}
|
||||
}
|
||||
|
@ -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<SearchReleaseGroupResponseReleaseGroup>,
|
||||
pub page: SearchPage,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
#[serde(rename_all(deserialize = "kebab-case"))]
|
||||
pub struct DeserializeSearchReleaseGroupResponse {
|
||||
release_groups: Vec<DeserializeSearchReleaseGroupResponseReleaseGroup>,
|
||||
#[serde(flatten)]
|
||||
page: SerdeSearchPage,
|
||||
}
|
||||
|
||||
impl From<DeserializeSearchReleaseGroupResponse> 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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::tui::app::{
|
||||
machine::{App, AppInner, AppMachine},
|
||||
selection::{Delta, ListSelection},
|
||||
AppPublicState, AppState, IAppInteractBrowse,
|
||||
selection::ListSelection,
|
||||
AppPublicState, AppState, Delta, IAppInteractBrowse,
|
||||
};
|
||||
|
||||
pub struct BrowseState;
|
||||
|
@ -9,8 +9,8 @@ use musichoard::collection::{
|
||||
use crate::tui::{
|
||||
app::{
|
||||
machine::{fetch_state::FetchState, input::Input, App, AppInner, AppMachine},
|
||||
AlbumMatches, AppPublicState, AppState, ArtistMatches, IAppInteractMatch, ListOption,
|
||||
MatchOption, MatchStateInfo, MatchStatePublic, WidgetState,
|
||||
AlbumMatches, AppPublicState, AppState, ArtistMatches, Delta, IAppInteractMatch,
|
||||
ListOption, MatchOption, MatchStateInfo, MatchStatePublic, WidgetState,
|
||||
},
|
||||
lib::interface::musicbrainz::api::{Lookup, Match},
|
||||
};
|
||||
@ -243,19 +243,19 @@ impl<'a> From<&'a mut MatchState> for AppPublicState<'a> {
|
||||
impl IAppInteractMatch for AppMachine<MatchState> {
|
||||
type APP = App;
|
||||
|
||||
fn prev_match(mut self) -> Self::APP {
|
||||
fn decrement_match(mut self, delta: Delta) -> Self::APP {
|
||||
if let Some(index) = self.state.state.list.selected() {
|
||||
let result = index.saturating_sub(1);
|
||||
let result = index.saturating_sub(delta.as_usize(&self.state.state));
|
||||
self.state.state.list.select(Some(result));
|
||||
}
|
||||
|
||||
self.into()
|
||||
}
|
||||
|
||||
fn next_match(mut self) -> Self::APP {
|
||||
fn increment_match(mut self, delta: Delta) -> Self::APP {
|
||||
let index = self.state.state.list.selected().unwrap();
|
||||
let to = cmp::min(
|
||||
index.saturating_add(1),
|
||||
index.saturating_add(delta.as_usize(&self.state.state)),
|
||||
self.state.current.len().saturating_sub(1),
|
||||
);
|
||||
self.state.state.list.select(Some(to));
|
||||
@ -473,38 +473,38 @@ mod tests {
|
||||
assert_eq!(matches.state.current, matches_info);
|
||||
assert_eq!(matches.state.state, widget_state);
|
||||
|
||||
let matches = matches.prev_match().unwrap_match();
|
||||
let matches = matches.decrement_match(Delta::Line).unwrap_match();
|
||||
|
||||
assert_eq!(matches.state.current, matches_info);
|
||||
assert_eq!(matches.state.state.list.selected(), Some(0));
|
||||
|
||||
let mut matches = matches;
|
||||
for ii in 1..len {
|
||||
matches = matches.next_match().unwrap_match();
|
||||
matches = matches.increment_match(Delta::Line).unwrap_match();
|
||||
|
||||
assert_eq!(matches.state.current, matches_info);
|
||||
assert_eq!(matches.state.state.list.selected(), Some(ii));
|
||||
}
|
||||
|
||||
// Next is CannotHaveMBID
|
||||
let matches = matches.next_match().unwrap_match();
|
||||
let matches = matches.increment_match(Delta::Line).unwrap_match();
|
||||
|
||||
assert_eq!(matches.state.current, matches_info);
|
||||
assert_eq!(matches.state.state.list.selected(), Some(len));
|
||||
|
||||
// Next is ManualInputMbid
|
||||
let matches = matches.next_match().unwrap_match();
|
||||
let matches = matches.increment_match(Delta::Line).unwrap_match();
|
||||
|
||||
assert_eq!(matches.state.current, matches_info);
|
||||
assert_eq!(matches.state.state.list.selected(), Some(len + 1));
|
||||
|
||||
let matches = matches.next_match().unwrap_match();
|
||||
let matches = matches.increment_match(Delta::Line).unwrap_match();
|
||||
|
||||
assert_eq!(matches.state.current, matches_info);
|
||||
assert_eq!(matches.state.state.list.selected(), Some(len + 1));
|
||||
|
||||
// Go prev_match first as selecting on manual input does not go back to fetch.
|
||||
let matches = matches.prev_match().unwrap_match();
|
||||
let matches = matches.decrement_match(Delta::Line).unwrap_match();
|
||||
matches.select().unwrap_fetch();
|
||||
}
|
||||
|
||||
@ -619,10 +619,10 @@ mod tests {
|
||||
AppMachine::match_state(inner(music_hoard(vec![])), match_state(album_match()));
|
||||
|
||||
// album_match has two matches which means that the fourth option should be manual input.
|
||||
let matches = matches.next_match().unwrap_match();
|
||||
let matches = matches.next_match().unwrap_match();
|
||||
let matches = matches.next_match().unwrap_match();
|
||||
let matches = matches.next_match().unwrap_match();
|
||||
let matches = matches.increment_match(Delta::Line).unwrap_match();
|
||||
let matches = matches.increment_match(Delta::Line).unwrap_match();
|
||||
let matches = matches.increment_match(Delta::Line).unwrap_match();
|
||||
let matches = matches.increment_match(Delta::Line).unwrap_match();
|
||||
|
||||
let app = matches.select();
|
||||
|
||||
@ -657,8 +657,8 @@ mod tests {
|
||||
);
|
||||
|
||||
// There are no matches which means that the second option should be manual input.
|
||||
let matches = matches.next_match().unwrap_match();
|
||||
let matches = matches.next_match().unwrap_match();
|
||||
let matches = matches.increment_match(Delta::Line).unwrap_match();
|
||||
let matches = matches.increment_match(Delta::Line).unwrap_match();
|
||||
|
||||
let mut app = matches.select();
|
||||
app = input_mbid(app);
|
||||
@ -691,8 +691,8 @@ mod tests {
|
||||
);
|
||||
|
||||
// There are no matches which means that the second option should be manual input.
|
||||
let matches = matches.next_match().unwrap_match();
|
||||
let matches = matches.next_match().unwrap_match();
|
||||
let matches = matches.increment_match(Delta::Line).unwrap_match();
|
||||
let matches = matches.increment_match(Delta::Line).unwrap_match();
|
||||
|
||||
let mut app = matches.select();
|
||||
app = input_mbid(app);
|
||||
|
@ -2,7 +2,8 @@ mod machine;
|
||||
mod selection;
|
||||
|
||||
pub use machine::App;
|
||||
pub use selection::{Category, Delta, Selection, WidgetState};
|
||||
use ratatui::widgets::ListState;
|
||||
pub use selection::{Category, Selection};
|
||||
|
||||
use musichoard::collection::{
|
||||
album::AlbumMeta,
|
||||
@ -124,8 +125,8 @@ pub trait IAppEventFetch {
|
||||
pub trait IAppInteractMatch {
|
||||
type APP: IApp;
|
||||
|
||||
fn prev_match(self) -> Self::APP;
|
||||
fn next_match(self) -> Self::APP;
|
||||
fn decrement_match(self, delta: Delta) -> Self::APP;
|
||||
fn increment_match(self, delta: Delta) -> Self::APP;
|
||||
fn select(self) -> Self::APP;
|
||||
|
||||
fn abort(self) -> Self::APP;
|
||||
@ -159,6 +160,40 @@ pub trait IAppInteractError {
|
||||
fn dismiss_error(self) -> Self::APP;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct WidgetState {
|
||||
pub list: ListState,
|
||||
pub height: usize,
|
||||
}
|
||||
|
||||
impl PartialEq for WidgetState {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.list.selected().eq(&other.list.selected()) && self.height.eq(&other.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetState {
|
||||
#[must_use]
|
||||
pub const fn with_selected(mut self, selected: Option<usize>) -> Self {
|
||||
self.list = self.list.with_selected(selected);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Delta {
|
||||
Line,
|
||||
Page,
|
||||
}
|
||||
|
||||
impl Delta {
|
||||
fn as_usize(&self, state: &WidgetState) -> usize {
|
||||
match self {
|
||||
Delta::Line => 1,
|
||||
Delta::Page => state.height.saturating_sub(1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// It would be preferable to have a getter for each field separately. However, the selection field
|
||||
// needs to be mutably accessible requiring a mutable borrow of the entire struct if behind a trait.
|
||||
// This in turn complicates simultaneous field access since only a single mutable borrow is allowed.
|
||||
|
@ -5,9 +5,12 @@ use musichoard::collection::{
|
||||
track::Track,
|
||||
};
|
||||
|
||||
use crate::tui::app::selection::{
|
||||
track::{KeySelectTrack, TrackSelection},
|
||||
Delta, SelectionState, WidgetState,
|
||||
use crate::tui::app::{
|
||||
selection::{
|
||||
track::{KeySelectTrack, TrackSelection},
|
||||
SelectionState,
|
||||
},
|
||||
Delta, WidgetState,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -6,9 +6,12 @@ use musichoard::collection::{
|
||||
track::Track,
|
||||
};
|
||||
|
||||
use crate::tui::app::selection::{
|
||||
album::{AlbumSelection, KeySelectAlbum},
|
||||
Delta, SelectionState, WidgetState,
|
||||
use crate::tui::app::{
|
||||
selection::{
|
||||
album::{AlbumSelection, KeySelectAlbum},
|
||||
SelectionState,
|
||||
},
|
||||
Delta, WidgetState,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -5,7 +5,10 @@ mod track;
|
||||
use musichoard::collection::{album::Album, artist::Artist, track::Track, Collection};
|
||||
use ratatui::widgets::ListState;
|
||||
|
||||
use artist::{ArtistSelection, KeySelectArtist};
|
||||
use crate::tui::app::{
|
||||
selection::artist::{ArtistSelection, KeySelectArtist},
|
||||
Delta, WidgetState,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Category {
|
||||
@ -24,40 +27,6 @@ pub struct SelectionState<'a, T> {
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct WidgetState {
|
||||
pub list: ListState,
|
||||
pub height: usize,
|
||||
}
|
||||
|
||||
impl PartialEq for WidgetState {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.list.selected().eq(&other.list.selected()) && self.height.eq(&other.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetState {
|
||||
#[must_use]
|
||||
pub const fn with_selected(mut self, selected: Option<usize>) -> Self {
|
||||
self.list = self.list.with_selected(selected);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Delta {
|
||||
Line,
|
||||
Page,
|
||||
}
|
||||
|
||||
impl Delta {
|
||||
fn as_usize(&self, state: &WidgetState) -> usize {
|
||||
match self {
|
||||
Delta::Line => 1,
|
||||
Delta::Page => state.height.saturating_sub(1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Selection {
|
||||
pub fn new(artists: &[Artist]) -> Self {
|
||||
Selection {
|
||||
|
@ -2,7 +2,7 @@ use std::cmp;
|
||||
|
||||
use musichoard::collection::track::{Track, TrackId, TrackNum};
|
||||
|
||||
use crate::tui::app::selection::{Delta, SelectionState, WidgetState};
|
||||
use crate::tui::app::{selection::SelectionState, Delta, WidgetState};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TrackSelection {
|
||||
|
@ -212,8 +212,10 @@ impl<APP: IApp> IEventHandlerPrivate<APP> for EventHandler {
|
||||
// Abort.
|
||||
KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('Q') => app.abort(),
|
||||
// Select.
|
||||
KeyCode::Up => app.prev_match(),
|
||||
KeyCode::Down => app.next_match(),
|
||||
KeyCode::Up => app.decrement_match(Delta::Line),
|
||||
KeyCode::Down => app.increment_match(Delta::Line),
|
||||
KeyCode::PageUp => app.decrement_match(Delta::Page),
|
||||
KeyCode::PageDown => app.increment_match(Delta::Page),
|
||||
KeyCode::Enter => app.select(),
|
||||
// Othey keys.
|
||||
_ => app.no_op(),
|
||||
|
4
src/tui/lib/external/musicbrainz/api/mod.rs
vendored
4
src/tui/lib/external/musicbrainz/api/mod.rs
vendored
@ -57,7 +57,7 @@ impl<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
|
||||
fn search_artist(&mut self, artist: &ArtistMeta) -> Result<Vec<Match<ArtistMeta>>, 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<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
|
||||
.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
|
||||
|
Loading…
Reference in New Issue
Block a user