Improve API by making signatures more generic

This commit is contained in:
Wojciech Kozlowski 2023-04-14 12:17:54 +02:00
parent 3ea04d90a6
commit 3e91918e74
3 changed files with 22 additions and 23 deletions

View File

@ -1,7 +1,7 @@
//! Module for storing MusicHoard data in a JSON file database. //! Module for storing MusicHoard data in a JSON file database.
use std::fs; use std::fs;
use std::path::{Path, PathBuf}; use std::path::PathBuf;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize; use serde::Serialize;
@ -60,10 +60,8 @@ pub struct JsonDatabaseFileBackend {
impl JsonDatabaseFileBackend { impl JsonDatabaseFileBackend {
/// Create a [`JsonDatabaseFileBackend`] that will read/write to the provided path. /// Create a [`JsonDatabaseFileBackend`] that will read/write to the provided path.
pub fn new(path: &Path) -> Self { pub fn new<P: Into<PathBuf>>(path: P) -> Self {
JsonDatabaseFileBackend { JsonDatabaseFileBackend { path: path.into() }
path: path.to_path_buf(),
}
} }
} }
@ -144,7 +142,7 @@ mod tests {
) )
} }
fn artists_to_json(artists: &Vec<Artist>) -> String { fn artists_to_json(artists: &[Artist]) -> String {
let mut artists_strings: Vec<String> = vec![]; let mut artists_strings: Vec<String> = vec![];
for artist in artists.iter() { for artist in artists.iter() {
artists_strings.push(artist_to_json(artist)); artists_strings.push(artist_to_json(artist));

View File

@ -3,7 +3,8 @@
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
path::{Path, PathBuf}, ffi::OsString,
path::PathBuf,
process::Command, process::Command,
str, str,
}; };
@ -80,7 +81,7 @@ impl ToBeetsArgs for Query {
#[cfg_attr(test, automock)] #[cfg_attr(test, automock)]
pub trait BeetsLibraryExecutor { pub trait BeetsLibraryExecutor {
/// Invoke beets with the provided arguments. /// Invoke beets with the provided arguments.
fn exec(&mut self, arguments: &[String]) -> Result<Vec<String>, Error>; fn exec<S: AsRef<str> + 'static>(&mut self, arguments: &[S]) -> Result<Vec<String>, Error>;
} }
/// Beets library. /// Beets library.
@ -90,7 +91,7 @@ pub struct BeetsLibrary<BLE> {
trait LibraryPrivate { trait LibraryPrivate {
fn list_cmd_and_args(query: &Query) -> Vec<String>; fn list_cmd_and_args(query: &Query) -> Vec<String>;
fn list_to_artists(list_output: Vec<String>) -> Result<Vec<Artist>, Error>; fn list_to_artists<S: AsRef<str>>(list_output: &[S]) -> Result<Vec<Artist>, Error>;
} }
impl<BLE: BeetsLibraryExecutor> BeetsLibrary<BLE> { impl<BLE: BeetsLibraryExecutor> BeetsLibrary<BLE> {
@ -104,7 +105,7 @@ impl<BLE: BeetsLibraryExecutor> Library for BeetsLibrary<BLE> {
fn list(&mut self, query: &Query) -> Result<Vec<Artist>, Error> { fn list(&mut self, query: &Query) -> Result<Vec<Artist>, Error> {
let cmd = Self::list_cmd_and_args(query); let cmd = Self::list_cmd_and_args(query);
let output = self.executor.exec(&cmd)?; let output = self.executor.exec(&cmd)?;
Self::list_to_artists(output) Self::list_to_artists(&output)
} }
} }
@ -116,11 +117,11 @@ impl<BLE: BeetsLibraryExecutor> LibraryPrivate for BeetsLibrary<BLE> {
cmd cmd
} }
fn list_to_artists(list_output: Vec<String>) -> Result<Vec<Artist>, Error> { fn list_to_artists<S: AsRef<str>>(list_output: &[S]) -> Result<Vec<Artist>, Error> {
let mut artists: Vec<Artist> = vec![]; let mut artists: Vec<Artist> = vec![];
let mut album_ids = HashMap::<ArtistId, HashSet<AlbumId>>::new(); let mut album_ids = HashMap::<ArtistId, HashSet<AlbumId>>::new();
for line in list_output.iter() { for line in list_output.iter().map(|s| s.as_ref()) {
if line.is_empty() { if line.is_empty() {
continue; continue;
} }
@ -199,22 +200,22 @@ impl<BLE: BeetsLibraryExecutor> LibraryPrivate for BeetsLibrary<BLE> {
/// Beets library executor that executes beets commands in their own process. /// Beets library executor that executes beets commands in their own process.
pub struct BeetsLibraryCommandExecutor { pub struct BeetsLibraryCommandExecutor {
bin: String, bin: OsString,
config: Option<PathBuf>, config: Option<PathBuf>,
} }
impl BeetsLibraryCommandExecutor { impl BeetsLibraryCommandExecutor {
/// Create a new [`BeetsLibraryCommandExecutor`] that uses the provided beets executable. /// Create a new [`BeetsLibraryCommandExecutor`] that uses the provided beets executable.
pub fn new(bin: &str) -> Self { pub fn new<S: Into<OsString>>(bin: S) -> Self {
BeetsLibraryCommandExecutor { BeetsLibraryCommandExecutor {
bin: bin.to_string(), bin: bin.into(),
config: None, config: None,
} }
} }
/// Update the configuration file passed to the beets executable. /// Update the configuration file passed to the beets executable.
pub fn config(mut self, path: Option<&Path>) -> Self { pub fn config<P: Into<PathBuf>>(mut self, path: Option<P>) -> Self {
self.config = path.map(|p| p.to_path_buf()); self.config = path.map(|p| p.into());
self self
} }
} }
@ -227,13 +228,13 @@ impl Default for BeetsLibraryCommandExecutor {
} }
impl BeetsLibraryExecutor for BeetsLibraryCommandExecutor { impl BeetsLibraryExecutor for BeetsLibraryCommandExecutor {
fn exec(&mut self, arguments: &[String]) -> Result<Vec<String>, Error> { fn exec<S: AsRef<str> + 'static>(&mut self, arguments: &[S]) -> Result<Vec<String>, Error> {
let mut cmd = Command::new(&self.bin); let mut cmd = Command::new(&self.bin);
if let Some(ref path) = self.config { if let Some(ref path) = self.config {
cmd.arg("--config"); cmd.arg("--config");
cmd.arg(path); cmd.arg(path);
} }
let output = cmd.args(arguments).output()?; let output = cmd.args(arguments.iter().map(|s| s.as_ref())).output()?;
if !output.status.success() { if !output.status.success() {
return Err(Error::CmdExec( return Err(Error::CmdExec(
String::from_utf8_lossy(&output.stderr).to_string(), String::from_utf8_lossy(&output.stderr).to_string(),
@ -281,7 +282,7 @@ mod tests {
strings strings
} }
fn artists_to_beets_string(artists: &Vec<Artist>) -> Vec<String> { fn artists_to_beets_string(artists: &[Artist]) -> Vec<String> {
let mut strings = vec![]; let mut strings = vec![];
for artist in artists.iter() { for artist in artists.iter() {
strings.append(&mut artist_to_beets_string(artist)); strings.append(&mut artist_to_beets_string(artist));

View File

@ -14,7 +14,7 @@ struct TrackSelection {
} }
impl TrackSelection { impl TrackSelection {
fn initialise(tracks: &Vec<Track>) -> Option<TrackSelection> { fn initialise(tracks: &[Track]) -> Option<TrackSelection> {
if !tracks.is_empty() { if !tracks.is_empty() {
Some(TrackSelection { index: 0 }) Some(TrackSelection { index: 0 })
} else { } else {
@ -43,7 +43,7 @@ struct AlbumSelection {
} }
impl AlbumSelection { impl AlbumSelection {
fn initialise(albums: &Vec<Album>) -> Option<AlbumSelection> { fn initialise(albums: &[Album]) -> Option<AlbumSelection> {
if !albums.is_empty() { if !albums.is_empty() {
Some(AlbumSelection { Some(AlbumSelection {
index: 0, index: 0,
@ -77,7 +77,7 @@ struct ArtistSelection {
} }
impl ArtistSelection { impl ArtistSelection {
fn initialise(artists: &Vec<Artist>) -> Option<ArtistSelection> { fn initialise(artists: &[Artist]) -> Option<ArtistSelection> {
if !artists.is_empty() { if !artists.is_empty() {
Some(ArtistSelection { Some(ArtistSelection {
index: 0, index: 0,