Clean up QueryOption

This commit is contained in:
Wojciech Kozlowski 2023-03-30 23:34:23 +09:00
parent 37f7a15dd0
commit d5572644ee
5 changed files with 142 additions and 94 deletions

View File

@ -59,21 +59,17 @@ impl DatabaseWrite for DatabaseJson {
mod tests {
use std::path::Path;
use tempfile::NamedTempFile;
use uuid::uuid;
use super::*;
use crate::{Album, AlbumArtist, Track};
use crate::{Album, Track};
const TEST_FILENAME: &str = "tests/files/database_json_test.json";
fn test_data() -> Vec<Album> {
vec![
Album {
artist: AlbumArtist {
name: String::from("Artist A"),
mbid: Some(uuid!("f7769831-746b-4a12-8124-0123d7fe17c9")),
},
artist: String::from("Artist A"),
year: 1998,
title: String::from("Release group A"),
tracks: vec![
@ -95,10 +91,7 @@ mod tests {
],
},
Album {
artist: AlbumArtist {
name: String::from("Artist B"),
mbid: None,
},
artist: String::from("Artist B"),
year: 2008,
title: String::from("Release group B"),
tracks: vec![Track {

View File

@ -9,15 +9,8 @@ pub mod library;
/// [MusicBrainz Identifier](https://musicbrainz.org/doc/MusicBrainz_Identifier) (MBID).
pub type Mbid = Uuid;
/// An album artist. Carries a MBID to facilitate discography access.
#[derive(Debug, Deserialize, Serialize, PartialEq)]
pub struct AlbumArtist {
pub name: String,
pub mbid: Option<Mbid>,
}
/// A single track on an album.
#[derive(Debug, Deserialize, Serialize, PartialEq)]
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Track {
pub number: u32,
pub title: String,
@ -25,9 +18,9 @@ pub struct Track {
}
/// An album is a collection of tracks that were released together.
#[derive(Debug, Deserialize, Serialize, PartialEq)]
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Album {
pub artist: AlbumArtist,
pub artist: String,
pub year: u32,
pub title: String,
pub tracks: Vec<Track>,

View File

@ -1,13 +1,32 @@
use std::{collections::HashSet, fmt::Display, process::Command};
use crate::{Album, AlbumArtist, Track};
use crate::{Album, Track};
use super::{Error, Library, Query, QueryValue};
use super::{Error, Library, Query, QueryOption};
impl<T: Display> QueryValue<T> {
fn to_string(&self, option_name: &str) -> String {
let negate = if self.negate { "^" } else { "" };
format!("{}{}:{}", negate, option_name, self.value)
pub trait SimpleOption {}
impl SimpleOption for String {}
impl SimpleOption for u32 {}
impl<T: SimpleOption + Display> QueryOption<T> {
fn to_arg(&self, option_name: &str) -> Option<String> {
let (negate, value) = match self {
Self::Include(value) => ("", value),
Self::Exclude(value) => ("^", value),
Self::None => return None,
};
Some(format!("{}{}:{}", negate, option_name, value))
}
}
impl QueryOption<Vec<String>> {
fn to_arg(&self, option_name: &str) -> Option<String> {
let (negate, vec) = match self {
Self::Include(value) => ("", value),
Self::Exclude(value) => ("^", value),
Self::None => return None,
};
Some(format!("{}{}:{}", negate, option_name, vec.join("; ")))
}
}
@ -15,24 +34,28 @@ impl Query {
fn to_args(&self) -> Vec<String> {
let mut arguments: Vec<String> = vec![];
if let Some(ref albumartist) = self.albumartist {
arguments.push(albumartist.to_string("albumartist"));
if let Some(album_artist) = self.album_artist.to_arg("albumartist") {
arguments.push(album_artist);
};
if let Some(ref album) = self.album {
arguments.push(album.to_string("album"));
if let Some(album_year) = self.album_year.to_arg("year") {
arguments.push(album_year);
};
if let Some(ref track) = self.track {
arguments.push(track.to_string("track"));
if let Some(album_title) = self.album_title.to_arg("album") {
arguments.push(album_title);
};
if let Some(ref title) = self.title {
arguments.push(title.to_string("title"));
if let Some(track_number) = self.track_number.to_arg("track") {
arguments.push(track_number);
};
if let Some(ref artist) = self.artist {
arguments.push(artist.to_string("artist"));
if let Some(track_title) = self.track_title.to_arg("title") {
arguments.push(track_title);
};
if let Some(track_artist) = self.track_artist.to_arg("artist") {
arguments.push(track_artist);
};
arguments
@ -82,9 +105,7 @@ struct AlbumId {
impl AlbumId {
fn matches(&self, album: &Album) -> bool {
(self.artist == album.artist.name)
&& (self.year == album.year)
&& (self.title == album.title)
(self.artist == album.artist) && (self.year == album.year) && (self.title == album.title)
}
}
@ -150,10 +171,7 @@ impl Beets {
let album = albums.iter_mut().rev().find(|a| aid.matches(a)).unwrap();
album.tracks.push(track);
} else {
let album_artist = AlbumArtist {
name: aid.artist.to_string(),
mbid: None,
};
let album_artist = aid.artist.to_string();
let album_title = aid.title.to_string();
album_ids.insert(aid);
albums.push(Album {
@ -201,10 +219,7 @@ mod tests {
fn test_data() -> Vec<Album> {
vec![
Album {
artist: AlbumArtist {
name: "album_artist.a".to_string(),
mbid: None,
},
artist: "album_artist.a".to_string(),
year: 1998,
title: "album_title.a".to_string(),
tracks: vec![
@ -226,10 +241,7 @@ mod tests {
],
},
Album {
artist: AlbumArtist {
name: "album_artist.b".to_string(),
mbid: None,
},
artist: "album_artist.b".to_string(),
year: 2003,
title: "album_title.b".to_string(),
tracks: vec![
@ -249,7 +261,7 @@ mod tests {
}
fn album_to_beets_string(album: &Album) -> Vec<String> {
let album_artist = &album.artist.name;
let album_artist = &album.artist;
let album_year = &album.year;
let album_title = &album.title;
@ -271,29 +283,20 @@ mod tests {
#[test]
fn test_query() {
let query = Query {
albumartist: None,
album: Some(QueryValue {
negate: true,
value: String::from("some.album"),
}),
track: Some(QueryValue {
negate: false,
value: 5,
}),
title: None,
artist: Some(QueryValue {
negate: false,
value: String::from("some.artist"),
}),
};
let query = Query::new()
.album_title(QueryOption::exclude(String::from("some.album")))
.track_number(QueryOption::include(5))
.track_artist(QueryOption::include(vec![
String::from("some.artist.1"),
String::from("some.artist.2"),
]));
assert_eq!(
query.to_args(),
vec![
String::from("^album:some.album"),
String::from("track:5"),
String::from("artist:some.artist")
String::from("artist:some.artist.1; some.artist.2")
]
);
}
@ -381,22 +384,10 @@ mod tests {
#[test]
fn test_list_query() {
let query = Query {
albumartist: None,
album: Some(QueryValue {
negate: true,
value: String::from("some.album"),
}),
track: Some(QueryValue {
negate: false,
value: 5,
}),
title: None,
artist: Some(QueryValue {
negate: false,
value: String::from("some.artist"),
}),
};
let query = Query::new()
.album_title(QueryOption::exclude(String::from("some.album")))
.track_number(QueryOption::include(5))
.track_artist(QueryOption::include(vec![String::from("some.artist")]));
let executor = TestExecutor {
arguments: Some(vec![

View File

@ -4,19 +4,90 @@ use crate::Album;
pub mod beets;
pub struct QueryValue<T> {
negate: bool,
value: T,
/// A single query option.
pub enum QueryOption<T> {
/// Inclusive query.
Include(T),
/// Exclusive query.
Exclude(T),
/// No query.
None,
}
impl<T> QueryOption<T> {
/// Create an inclusive query option.
pub fn include(value: T) -> Self {
QueryOption::Include(value)
}
pub fn exclude(value: T) -> Self {
QueryOption::Exclude(value)
}
pub fn none() -> Self {
QueryOption::None
}
pub fn is_some(&self) -> bool {
!matches!(self, QueryOption::None)
}
pub fn is_none(&self) -> bool {
matches!(self, QueryOption::None)
}
}
impl<T> Default for QueryOption<T> {
fn default() -> Self {
Self::none()
}
}
/// Options for refining library queries.
#[derive(Default)]
pub struct Query {
albumartist: Option<QueryValue<String>>,
album: Option<QueryValue<String>>,
track: Option<QueryValue<u32>>,
title: Option<QueryValue<String>>,
artist: Option<QueryValue<String>>,
album_artist: QueryOption<String>,
album_year: QueryOption<u32>,
album_title: QueryOption<String>,
track_number: QueryOption<u32>,
track_title: QueryOption<String>,
track_artist: QueryOption<Vec<String>>,
}
impl Query {
pub fn new() -> Self {
Query::default()
}
pub fn album_artist(mut self, album_artist: QueryOption<String>) -> Self {
self.album_artist = album_artist;
self
}
pub fn album_year(mut self, album_year: QueryOption<u32>) -> Self {
self.album_year = album_year;
self
}
pub fn album_title(mut self, album_title: QueryOption<String>) -> Self {
self.album_title = album_title;
self
}
pub fn track_number(mut self, track_number: QueryOption<u32>) -> Self {
self.track_number = track_number;
self
}
pub fn track_title(mut self, track_title: QueryOption<String>) -> Self {
self.track_title = track_title;
self
}
pub fn track_artist(mut self, track_artist: QueryOption<Vec<String>>) -> Self {
self.track_artist = track_artist;
self
}
}
/// Error type for library calls.

View File

@ -1 +1 @@
[{"artist":{"name":"Artist A","mbid":"f7769831-746b-4a12-8124-0123d7fe17c9"},"year":1998,"title":"Release group A","tracks":[{"number":1,"title":"Track A.1","artist":["Artist A.A"]},{"number":2,"title":"Track A.2","artist":["Artist A.A"]},{"number":3,"title":"Track A.3","artist":["Artist A.A","Artist A.B"]}]},{"artist":{"name":"Artist B","mbid":null},"year":2008,"title":"Release group B","tracks":[{"number":1,"title":"Track B.1","artist":["Artist B.A"]}]}]
[{"artist":"Artist A","year":1998,"title":"Release group A","tracks":[{"number":1,"title":"Track A.1","artist":["Artist A.A"]},{"number":2,"title":"Track A.2","artist":["Artist A.A"]},{"number":3,"title":"Track A.3","artist":["Artist A.A","Artist A.B"]}]},{"artist":"Artist B","year":2008,"title":"Release group B","tracks":[{"number":1,"title":"Track B.1","artist":["Artist B.A"]}]}]