Resolve "Local collection trait and beets implementation" #9
@ -1,3 +1,5 @@
|
||||
//! Module for storing MusicHoard data in a JSON file database.
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Module for storing MusicHoard data in a database.
|
||||
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
|
||||
@ -5,6 +7,7 @@ pub mod json;
|
||||
|
||||
/// Trait for database reads.
|
||||
pub trait DatabaseRead {
|
||||
/// Read collection from the database.
|
||||
fn read<D>(&mut self, collection: &mut D) -> Result<(), std::io::Error>
|
||||
where
|
||||
D: DeserializeOwned;
|
||||
@ -12,6 +15,7 @@ pub trait DatabaseRead {
|
||||
|
||||
/// Trait for database writes.
|
||||
pub trait DatabaseWrite {
|
||||
/// Write collection to the database.
|
||||
fn write<S>(&mut self, collection: &S) -> Result<(), std::io::Error>
|
||||
where
|
||||
S: Serialize;
|
||||
|
@ -1,3 +1,6 @@
|
||||
//! Module for interacting with the music library via
|
||||
//! [beets](https://beets.readthedocs.io/en/stable/).
|
||||
|
||||
use std::{collections::HashSet, fmt::Display, process::Command};
|
||||
|
||||
use crate::{Album, AlbumId, Track};
|
||||
@ -23,7 +26,7 @@ impl<T: SimpleOption + Display> QueryOptionArgBeets for QueryOption<T> {
|
||||
Self::Exclude(value) => ("^", value),
|
||||
Self::None => return None,
|
||||
};
|
||||
Some(format!("{}{}:{}", negate, option_name, value))
|
||||
Some(format!("{}{}{}", negate, option_name, value))
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,7 +37,7 @@ impl QueryOptionArgBeets for QueryOption<Vec<String>> {
|
||||
Self::Exclude(value) => ("^", value),
|
||||
Self::None => return None,
|
||||
};
|
||||
Some(format!("{}{}:{}", negate, option_name, vec.join("; ")))
|
||||
Some(format!("{}{}{}", negate, option_name, vec.join("; ")))
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,38 +45,45 @@ impl QueryArgsBeets for Query {
|
||||
fn to_args(&self) -> Vec<String> {
|
||||
let mut arguments: Vec<String> = vec![];
|
||||
|
||||
if let Some(album_artist) = self.album_artist.to_arg("albumartist") {
|
||||
if let Some(album_artist) = self.album_artist.to_arg("albumartist:") {
|
||||
arguments.push(album_artist);
|
||||
};
|
||||
|
||||
if let Some(album_year) = self.album_year.to_arg("year") {
|
||||
if let Some(album_year) = self.album_year.to_arg("year:") {
|
||||
arguments.push(album_year);
|
||||
};
|
||||
|
||||
if let Some(album_title) = self.album_title.to_arg("album") {
|
||||
if let Some(album_title) = self.album_title.to_arg("album:") {
|
||||
arguments.push(album_title);
|
||||
};
|
||||
|
||||
if let Some(track_number) = self.track_number.to_arg("track") {
|
||||
if let Some(track_number) = self.track_number.to_arg("track:") {
|
||||
arguments.push(track_number);
|
||||
};
|
||||
|
||||
if let Some(track_title) = self.track_title.to_arg("title") {
|
||||
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") {
|
||||
if let Some(track_artist) = self.track_artist.to_arg("artist:") {
|
||||
arguments.push(track_artist);
|
||||
};
|
||||
|
||||
if let Some(all) = self.all.to_arg("") {
|
||||
arguments.push(all);
|
||||
}
|
||||
|
||||
arguments
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for invoking beets commands.
|
||||
pub trait BeetsExecutor {
|
||||
/// Invoke beets with the provided arguments.
|
||||
fn exec(&mut self, arguments: Vec<String>) -> Result<Vec<String>, Error>;
|
||||
}
|
||||
|
||||
/// Struct for interacting with the music library via beets.
|
||||
pub struct Beets {
|
||||
executor: Box<dyn BeetsExecutor>,
|
||||
}
|
||||
@ -179,6 +189,7 @@ impl LibraryPrivate for Beets {
|
||||
}
|
||||
}
|
||||
|
||||
/// Executor for executing beets commands on the local system.
|
||||
pub struct SystemExecutor {
|
||||
bin: String,
|
||||
}
|
||||
@ -295,19 +306,21 @@ mod tests {
|
||||
#[test]
|
||||
fn test_query() {
|
||||
let query = Query::new()
|
||||
.album_title(QueryOption::exclude(String::from("some.album")))
|
||||
.track_number(QueryOption::include(5))
|
||||
.track_artist(QueryOption::include(vec![
|
||||
.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"),
|
||||
]));
|
||||
]))
|
||||
.all(QueryOption::Exclude(String::from("some.all")));
|
||||
|
||||
assert_eq!(
|
||||
query.to_args(),
|
||||
vec![
|
||||
String::from("^album:some.album"),
|
||||
String::from("track:5"),
|
||||
String::from("artist:some.artist.1; some.artist.2")
|
||||
String::from("artist:some.artist.1; some.artist.2"),
|
||||
String::from("^some.all"),
|
||||
]
|
||||
);
|
||||
}
|
||||
@ -396,9 +409,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_list_query() {
|
||||
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")]));
|
||||
.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![
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Module for interacting with the music library.
|
||||
|
||||
use std::{num::ParseIntError, str::Utf8Error};
|
||||
|
||||
use crate::Album;
|
||||
@ -15,23 +17,12 @@ pub enum QueryOption<T> {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/// Return `true` if [QueryOption] is not [QueryOption::None].
|
||||
pub fn is_some(&self) -> bool {
|
||||
!matches!(self, QueryOption::None)
|
||||
}
|
||||
|
||||
/// Return `true` if [QueryOption] is [QueryOption::None].
|
||||
pub fn is_none(&self) -> bool {
|
||||
matches!(self, QueryOption::None)
|
||||
}
|
||||
@ -39,7 +30,7 @@ impl<T> QueryOption<T> {
|
||||
|
||||
impl<T> Default for QueryOption<T> {
|
||||
fn default() -> Self {
|
||||
Self::none()
|
||||
Self::None
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,42 +43,56 @@ pub struct Query {
|
||||
track_number: QueryOption<u32>,
|
||||
track_title: QueryOption<String>,
|
||||
track_artist: QueryOption<Vec<String>>,
|
||||
all: QueryOption<String>,
|
||||
}
|
||||
|
||||
impl Query {
|
||||
/// Create an empty query.
|
||||
pub fn new() -> Self {
|
||||
Query::default()
|
||||
}
|
||||
|
||||
/// Refine the query to a specific album artist.
|
||||
pub fn album_artist(mut self, album_artist: QueryOption<String>) -> Self {
|
||||
self.album_artist = album_artist;
|
||||
self
|
||||
}
|
||||
|
||||
/// Refine the query to a specific album year.
|
||||
pub fn album_year(mut self, album_year: QueryOption<u32>) -> Self {
|
||||
self.album_year = album_year;
|
||||
self
|
||||
}
|
||||
|
||||
/// Refine the query to a specific album title.
|
||||
pub fn album_title(mut self, album_title: QueryOption<String>) -> Self {
|
||||
self.album_title = album_title;
|
||||
self
|
||||
}
|
||||
|
||||
/// Refine the query to a specific track number.
|
||||
pub fn track_number(mut self, track_number: QueryOption<u32>) -> Self {
|
||||
self.track_number = track_number;
|
||||
self
|
||||
}
|
||||
|
||||
/// Refine the query to a specific track title.
|
||||
pub fn track_title(mut self, track_title: QueryOption<String>) -> Self {
|
||||
self.track_title = track_title;
|
||||
self
|
||||
}
|
||||
|
||||
/// Refine the query to a specific set of track artists.
|
||||
pub fn track_artist(mut self, track_artist: QueryOption<Vec<String>>) -> Self {
|
||||
self.track_artist = track_artist;
|
||||
self
|
||||
}
|
||||
|
||||
/// Refine the query for all fields.
|
||||
pub fn all(mut self, all: QueryOption<String>) -> Self {
|
||||
self.all = all;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Error type for library calls.
|
||||
@ -123,5 +128,6 @@ impl From<Utf8Error> for Error {
|
||||
|
||||
/// Trait for interacting with the music library.
|
||||
pub trait Library {
|
||||
/// List lirbary items that match the a specific query.
|
||||
fn list(&mut self, query: &Query) -> Result<Vec<Album>, Error>;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user