diff --git a/Cargo.lock b/Cargo.lock index 31f9a84..d4320f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,6 +43,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + [[package]] name = "cassowary" version = "0.3.0" @@ -107,6 +113,26 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "downcast" version = "0.11.0" @@ -164,6 +190,17 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" +[[package]] +name = "getrandom" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "heck" version = "0.3.3" @@ -312,11 +349,13 @@ dependencies = [ "crossterm", "mockall", "once_cell", + "openssh", "ratatui", "serde", "serde_json", "structopt", "tempfile", + "tokio", "uuid", ] @@ -341,6 +380,39 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +[[package]] +name = "openssh" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca6c277973fb549b36dd8980941b5ea3ecebea026f5b1f0060acde74d893c22" +dependencies = [ + "dirs", + "libc", + "once_cell", + "openssh-mux-client", + "shell-escape", + "tempfile", + "thiserror", + "tokio", + "tokio-pipe", +] + +[[package]] +name = "openssh-mux-client" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88eac793af6170bcd6d4f39c3b7ba3f4227cab5680d7189ba30f9d174600b75f" +dependencies = [ + "once_cell", + "sendfd", + "serde", + "ssh_format", + "thiserror", + "tokio", + "tokio-io-utility", + "typed-builder", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -364,6 +436,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + [[package]] name = "predicates" version = "2.1.5" @@ -467,6 +545,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall 0.2.16", + "thiserror", +] + [[package]] name = "regex" version = "1.7.3" @@ -510,6 +599,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "sendfd" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604b71b8fc267e13bb3023a2c901126c8f349393666a6d98ac1ae5729b701798" +dependencies = [ + "libc", + "tokio", +] + [[package]] name = "serde" version = "1.0.159" @@ -541,6 +640,12 @@ dependencies = [ "serde", ] +[[package]] +name = "shell-escape" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" + [[package]] name = "signal-hook" version = "0.3.15" @@ -577,6 +682,25 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "ssh_format" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8701239872766d43b8a5f9a560ff7f002b48064fadea87f44a70507069fb482" +dependencies = [ + "serde", +] + [[package]] name = "strsim" version = "0.8.0" @@ -657,6 +781,84 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.11", +] + +[[package]] +name = "tokio" +version = "1.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-io-utility" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d672654d175710e52c7c41f6aec77c62b3c0954e2a7ebce9049d1e94ed7c263" +dependencies = [ + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.11", +] + +[[package]] +name = "tokio-pipe" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f213a84bffbd61b8fa0ba8a044b4bbe35d471d0b518867181e82bd5c15542784" +dependencies = [ + "libc", + "tokio", +] + +[[package]] +name = "typed-builder" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "unicode-ident" version = "1.0.8" diff --git a/Cargo.toml b/Cargo.toml index 485339f..4e1b299 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,10 +7,12 @@ edition = "2021" [dependencies] crossterm = "0.26.1" +openssh = { version = "0.9.9", features = ["native-mux"], default-features = false } serde = { version = "1.0.159", features = ["derive"] } serde_json = "1.0.95" structopt = "0.3.26" ratatui = "0.20.1" +tokio = { version = "1.27.0", features = ["rt"] } uuid = { version = "1.3.0", features = ["serde"] } [dev-dependencies] diff --git a/src/database/json/backend.rs b/src/database/json/backend.rs new file mode 100644 index 0000000..32026d1 --- /dev/null +++ b/src/database/json/backend.rs @@ -0,0 +1,30 @@ +//! Module for storing MusicHoard data in a JSON file database. + +use std::fs; +use std::path::PathBuf; + +use super::JsonDatabaseBackend; + +/// JSON database backend that uses a local file for persistent storage. +pub struct JsonDatabaseFileBackend { + path: PathBuf, +} + +impl JsonDatabaseFileBackend { + /// Create a [`JsonDatabaseFileBackend`] that will read/write to the provided path. + pub fn new>(path: P) -> Self { + JsonDatabaseFileBackend { path: path.into() } + } +} + +impl JsonDatabaseBackend for JsonDatabaseFileBackend { + fn read(&self) -> Result { + // Read entire file to memory as for now this is faster than a buffered read from disk: + // https://github.com/serde-rs/json/issues/160 + fs::read_to_string(&self.path) + } + + fn write(&mut self, json: &str) -> Result<(), std::io::Error> { + fs::write(&self.path, json) + } +} diff --git a/src/database/json.rs b/src/database/json/mod.rs similarity index 87% rename from src/database/json.rs rename to src/database/json/mod.rs index 08b8913..5caeefa 100644 --- a/src/database/json.rs +++ b/src/database/json/mod.rs @@ -1,8 +1,5 @@ //! Module for storing MusicHoard data in a JSON file database. -use std::fs; -use std::path::PathBuf; - use serde::de::DeserializeOwned; use serde::Serialize; @@ -11,6 +8,8 @@ use mockall::automock; use super::{Database, Error}; +pub mod backend; + impl From for Error { fn from(err: serde_json::Error) -> Error { Error::SerDeError(err.to_string()) @@ -53,30 +52,6 @@ impl Database for JsonDatabase { } } -/// JSON database backend that uses a local file for persistent storage. -pub struct JsonDatabaseFileBackend { - path: PathBuf, -} - -impl JsonDatabaseFileBackend { - /// Create a [`JsonDatabaseFileBackend`] that will read/write to the provided path. - pub fn new>(path: P) -> Self { - JsonDatabaseFileBackend { path: path.into() } - } -} - -impl JsonDatabaseBackend for JsonDatabaseFileBackend { - fn read(&self) -> Result { - // Read entire file to memory as for now this is faster than a buffered read from disk: - // https://github.com/serde-rs/json/issues/160 - fs::read_to_string(&self.path) - } - - fn write(&mut self, json: &str) -> Result<(), std::io::Error> { - fs::write(&self.path, json) - } -} - #[cfg(test)] mod tests { use std::collections::HashMap; diff --git a/src/library/beets/executor.rs b/src/library/beets/executor.rs new file mode 100644 index 0000000..2c45a6d --- /dev/null +++ b/src/library/beets/executor.rs @@ -0,0 +1,140 @@ +//! Module for interacting with the music library via +//! [beets](https://beets.readthedocs.io/en/stable/). + +use std::{ + ffi::OsString, + path::PathBuf, + process::{Command, Output}, + str, +}; + +use openssh::{KnownHosts, Session}; +use tokio::runtime::{self, Runtime}; + +use super::{BeetsLibraryExecutor, Error}; + +const BEET_DEFAULT: &str = "beet"; + +trait BeetsLibraryExecutorPrivate { + fn output(output: Output) -> Result, Error> { + if !output.status.success() { + return Err(Error::Executor( + String::from_utf8_lossy(&output.stderr).to_string(), + )); + } + let output = str::from_utf8(&output.stdout)?; + Ok(output.split('\n').map(|s| s.to_string()).collect()) + } +} + +/// Beets library executor that executes beets commands in their own process. +pub struct BeetsLibraryProcessExecutor { + bin: OsString, + config: Option, +} + +impl BeetsLibraryProcessExecutor { + /// Create a new [`BeetsLibraryProcessExecutor`] that uses the default beets executable. + pub fn new() -> Self { + Self::bin(BEET_DEFAULT) + } + + /// Create a new [`BeetsLibraryProcessExecutor`] that uses the provided beets executable. + pub fn bin>(bin: S) -> Self { + BeetsLibraryProcessExecutor { + bin: bin.into(), + config: None, + } + } + + /// Update the configuration file passed to the beets executable. + pub fn config>(mut self, path: Option

) -> Self { + self.config = path.map(|p| p.into()); + self + } +} + +impl Default for BeetsLibraryProcessExecutor { + /// Create a new [`BeetsLibraryProcessExecutor`] that uses the system's default beets + /// executable. + fn default() -> Self { + BeetsLibraryProcessExecutor::new() + } +} + +impl BeetsLibraryExecutor for BeetsLibraryProcessExecutor { + fn exec + 'static>(&mut self, arguments: &[S]) -> Result, Error> { + let mut cmd = Command::new(&self.bin); + if let Some(ref path) = self.config { + cmd.arg("--config"); + cmd.arg(path); + } + let output = cmd.args(arguments.iter().map(|s| s.as_ref())).output()?; + Self::output(output) + } +} + +impl BeetsLibraryExecutorPrivate for BeetsLibraryProcessExecutor {} + +// GRCOV_EXCL_START +/// Beets library executor that executes beets commands over SSH. +pub struct BeetsLibrarySshExecutor { + rt: Runtime, + session: Session, + bin: String, + config: Option, +} + +impl From for Error { + fn from(err: openssh::Error) -> Error { + Error::Executor(err.to_string()) + } +} + +impl BeetsLibrarySshExecutor { + /// Create a new [`BeetsLibrarySshExecutor`] that uses the default beets executable over an SSH + /// connection. This call will attempt to establish the connection and will fail if the + /// connection fails. + pub fn new>(host: H) -> Result { + Self::bin(host, BEET_DEFAULT) + } + + /// Create a new [`BeetsLibrarySshExecutor`] that uses the provided beets executable over an SSH + /// connection. This call will attempt to establish the connection and will fail if the + /// connection fails. + pub fn bin, S: Into>(host: H, bin: S) -> Result { + let rt = runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + let session = rt.block_on(Session::connect_mux(host, KnownHosts::Strict))?; + Ok(BeetsLibrarySshExecutor { + rt, + session, + bin: bin.into(), + config: None, + }) + } + + /// Update the configuration file passed to the beets executable. + pub fn config>(mut self, path: Option

) -> Self { + self.config = path.map(|p| p.into()); + self + } +} + +impl BeetsLibraryExecutor for BeetsLibrarySshExecutor { + fn exec + 'static>(&mut self, arguments: &[S]) -> Result, Error> { + let mut cmd = self.session.command(&self.bin); + if let Some(ref path) = self.config { + cmd.arg("--config"); + cmd.arg(path); + } + cmd.args(arguments.iter().map(|s| s.as_ref())); + let output = self.rt.block_on(cmd.output())?; + Self::output(output) + } +} + +impl BeetsLibraryExecutorPrivate for BeetsLibrarySshExecutor {} +// GRCOV_EXCL_STOP diff --git a/src/library/beets.rs b/src/library/beets/mod.rs similarity index 89% rename from src/library/beets.rs rename to src/library/beets/mod.rs index 072a9c7..7ce5214 100644 --- a/src/library/beets.rs +++ b/src/library/beets/mod.rs @@ -3,9 +3,6 @@ use std::{ collections::{HashMap, HashSet}, - ffi::OsString, - path::PathBuf, - process::Command, str, }; @@ -16,6 +13,8 @@ use crate::{Album, AlbumId, Artist, ArtistId, Track, TrackFormat}; use super::{Error, Field, Library, Query}; +pub mod executor; + macro_rules! list_format_separator { () => { " -*^- " @@ -95,7 +94,7 @@ trait LibraryPrivate { } impl BeetsLibrary { - /// Create a new beets library with the provided executor, e.g. [`BeetsLibraryCommandExecutor`]. + /// Create a new beets library with the provided executor, e.g. [`BeetsLibraryProcessExecutor`]. pub fn new(executor: BLE) -> Self { BeetsLibrary { executor } } @@ -198,53 +197,6 @@ impl LibraryPrivate for BeetsLibrary { } } -/// Beets library executor that executes beets commands in their own process. -pub struct BeetsLibraryCommandExecutor { - bin: OsString, - config: Option, -} - -impl BeetsLibraryCommandExecutor { - /// Create a new [`BeetsLibraryCommandExecutor`] that uses the provided beets executable. - pub fn new>(bin: S) -> Self { - BeetsLibraryCommandExecutor { - bin: bin.into(), - config: None, - } - } - - /// Update the configuration file passed to the beets executable. - pub fn config>(mut self, path: Option

) -> Self { - self.config = path.map(|p| p.into()); - self - } -} - -impl Default for BeetsLibraryCommandExecutor { - /// Create a new [`BeetsLibraryCommandExecutor`] that uses the system's default beets executable. - fn default() -> Self { - BeetsLibraryCommandExecutor::new("beet") - } -} - -impl BeetsLibraryExecutor for BeetsLibraryCommandExecutor { - fn exec + 'static>(&mut self, arguments: &[S]) -> Result, Error> { - let mut cmd = Command::new(&self.bin); - if let Some(ref path) = self.config { - cmd.arg("--config"); - cmd.arg(path); - } - let output = cmd.args(arguments.iter().map(|s| s.as_ref())).output()?; - if !output.status.success() { - return Err(Error::CmdExec( - String::from_utf8_lossy(&output.stderr).to_string(), - )); - } - let output = str::from_utf8(&output.stdout)?; - Ok(output.split('\n').map(|s| s.to_string()).collect()) - } -} - #[cfg(test)] mod tests { use mockall::predicate; diff --git a/src/library/mod.rs b/src/library/mod.rs index 855c637..07f9592 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -60,26 +60,26 @@ impl Query { /// Error type for library calls. #[derive(Debug, PartialEq, Eq)] pub enum Error { - /// The underlying library failed to execute a command. - CmdExec(String), - /// The underlying library returned invalid data. - Invalid(String), - /// The underlying library experienced an I/O error. + /// The library's executor failed. + Executor(String), + /// The library experienced an I/O error. Io(String), - /// The underlying library failed to parse an integer. + /// The library received invalid data. + Invalid(String), + /// The library failed to parse an integer. ParseInt(String), - /// The underlying library failed to parse a UTF-8 string. + /// The library failed to parse a UTF-8 string. Utf8(String), } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Self::CmdExec(ref s) => write!(f, "the library failed to execute a command: {s}"), - Self::Invalid(ref s) => write!(f, "the library received invalid data: {s}"), + Self::Executor(ref s) => write!(f, "the library's executor failed: {s}"), Self::Io(ref s) => write!(f, "the library experienced an I/O error: {s}"), - Self::ParseInt(ref s) => write!(f, "the library received an invalid integer: {s}"), - Self::Utf8(ref s) => write!(f, "the library received invalid UTF-8: {s}"), + Self::Invalid(ref s) => write!(f, "the library received invalid data: {s}"), + Self::ParseInt(ref s) => write!(f, "the library failed to parse an integer: {s}"), + Self::Utf8(ref s) => write!(f, "the library failed to parse a UTF-8 string: {s}"), } } } @@ -133,21 +133,21 @@ mod tests { #[test] fn errors() { - let cmd_err = Error::CmdExec(String::from("CmdExec")); - let inv_err = Error::Invalid(String::from("Invalid")); + let exe_err = Error::Executor(String::from("Executor")); let io_err: Error = io::Error::new(io::ErrorKind::Interrupted, "Interrupted").into(); + let inv_err = Error::Invalid(String::from("Invalid")); let int_err: Error = "five".parse::().unwrap_err().into(); let utf_err: Error = std::str::from_utf8(b"\xe2\x28\xa1").unwrap_err().into(); - assert!(!cmd_err.to_string().is_empty()); - assert!(!inv_err.to_string().is_empty()); + assert!(!exe_err.to_string().is_empty()); assert!(!io_err.to_string().is_empty()); + assert!(!inv_err.to_string().is_empty()); assert!(!int_err.to_string().is_empty()); assert!(!utf_err.to_string().is_empty()); - assert!(!format!("{:?}", cmd_err).is_empty()); - assert!(!format!("{:?}", inv_err).is_empty()); + assert!(!format!("{:?}", exe_err).is_empty()); assert!(!format!("{:?}", io_err).is_empty()); + assert!(!format!("{:?}", inv_err).is_empty()); assert!(!format!("{:?}", int_err).is_empty()); assert!(!format!("{:?}", utf_err).is_empty()); } diff --git a/src/main.rs b/src/main.rs index cc241d5..cc74a41 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,22 @@ -use ratatui::backend::CrosstermBackend; -use ratatui::Terminal; -use std::io; use std::path::PathBuf; +use std::{ffi::OsString, io}; +use ratatui::{backend::CrosstermBackend, Terminal}; use structopt::StructOpt; use musichoard::{ collection::MhCollectionManager, - database::json::{JsonDatabase, JsonDatabaseFileBackend}, - library::beets::{BeetsLibrary, BeetsLibraryCommandExecutor}, + database::{ + json::{backend::JsonDatabaseFileBackend, JsonDatabase}, + Database, + }, + library::{ + beets::{ + executor::{BeetsLibraryProcessExecutor, BeetsLibrarySshExecutor}, + BeetsLibrary, + }, + Library, + }, }; mod tui; @@ -19,35 +27,22 @@ use tui::{ #[derive(StructOpt)] struct Opt { - #[structopt( - short = "b", - long = "beets", - name = "beets config file path", - parse(from_os_str) - )] - beets_config_file_path: Option, + #[structopt(long = "ssh", name = "beets SSH URI")] + beets_ssh_uri: Option, + + #[structopt(long = "beets", name = "beets config file path")] + beets_config_file_path: Option, #[structopt( - short = "d", long = "database", name = "database file path", - default_value = "database.json", - parse(from_os_str) + default_value = "database.json" )] database_file_path: PathBuf, } -fn main() { - // Create the application. - let opt = Opt::from_args(); - - let beets = BeetsLibrary::new( - BeetsLibraryCommandExecutor::default().config(opt.beets_config_file_path.as_deref()), - ); - - let database = JsonDatabase::new(JsonDatabaseFileBackend::new(&opt.database_file_path)); - - let collection_manager = MhCollectionManager::new(beets, database); +fn with(lib: LIB, db: DB) { + let collection_manager = MhCollectionManager::new(lib, db); // Initialize the terminal user interface. let backend = CrosstermBackend::new(io::stdout()); @@ -65,6 +60,22 @@ fn main() { Tui::run(terminal, app, ui, handler, listener).expect("failed to run tui"); } +fn main() { + // Create the application. + let opt = Opt::from_args(); + + if let Some(uri) = opt.beets_ssh_uri { + let uri = uri.into_string().expect("invalid SSH URI"); + let lib_exec = BeetsLibrarySshExecutor::new(uri).expect("failed to initialise beets"); + let db_exec = JsonDatabaseFileBackend::new(&opt.database_file_path); + with(BeetsLibrary::new(lib_exec), JsonDatabase::new(db_exec)); + } else { + let lib_exec = BeetsLibraryProcessExecutor::default().config(opt.beets_config_file_path); + let db_exec = JsonDatabaseFileBackend::new(&opt.database_file_path); + with(BeetsLibrary::new(lib_exec), JsonDatabase::new(db_exec)); + } +} + #[cfg(test)] #[macro_use] mod testlib; diff --git a/tests/database/json.rs b/tests/database/json.rs index d39cb7e..55ea7a6 100644 --- a/tests/database/json.rs +++ b/tests/database/json.rs @@ -2,7 +2,7 @@ use std::{fs, path::PathBuf}; use musichoard::{ database::{ - json::{JsonDatabase, JsonDatabaseFileBackend}, + json::{backend::JsonDatabaseFileBackend, JsonDatabase}, Database, }, Artist, diff --git a/tests/library/beets.rs b/tests/library/beets.rs index ac4d358..3d9215c 100644 --- a/tests/library/beets.rs +++ b/tests/library/beets.rs @@ -8,7 +8,7 @@ use once_cell::sync::Lazy; use musichoard::{ library::{ - beets::{BeetsLibrary, BeetsLibraryCommandExecutor}, + beets::{executor::BeetsLibraryProcessExecutor, BeetsLibrary}, Field, Library, Query, }, Artist, @@ -16,17 +16,17 @@ use musichoard::{ use crate::COLLECTION; -static BEETS_EMPTY_CONFIG: Lazy>>> = +static BEETS_EMPTY_CONFIG: Lazy>>> = Lazy::new(|| { Arc::new(Mutex::new(BeetsLibrary::new( - BeetsLibraryCommandExecutor::default(), + BeetsLibraryProcessExecutor::default(), ))) }); -static BEETS_TEST_CONFIG: Lazy>>> = +static BEETS_TEST_CONFIG: Lazy>>> = Lazy::new(|| { Arc::new(Mutex::new(BeetsLibrary::new( - BeetsLibraryCommandExecutor::default().config(Some( + BeetsLibraryProcessExecutor::default().config(Some( &fs::canonicalize("./tests/files/library/config.yml").unwrap(), )), ))) @@ -45,7 +45,7 @@ fn test_no_config_list() { #[test] fn test_invalid_config() { - let mut beets = BeetsLibrary::new(BeetsLibraryCommandExecutor::default().config(Some( + let mut beets = BeetsLibrary::new(BeetsLibraryProcessExecutor::default().config(Some( &PathBuf::from("./tests/files/library/config-does-not-exist.yml"), )));