7---create-the-main-binary (#15)
Closes #7 Reviewed-on: https://git.wojciechkozlowski.eu/wojtek/musichoard/pulls/15
This commit is contained in:
parent
5c7579ba23
commit
6f9d663a0b
191
Cargo.lock
generated
191
Cargo.lock
generated
@ -2,6 +2,26 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ansi_term"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi 0.1.19",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
@ -20,6 +40,21 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "2.34.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||||
|
dependencies = [
|
||||||
|
"ansi_term",
|
||||||
|
"atty",
|
||||||
|
"bitflags",
|
||||||
|
"strsim",
|
||||||
|
"textwrap",
|
||||||
|
"unicode-width",
|
||||||
|
"vec_map",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@ -50,6 +85,24 @@ dependencies = [
|
|||||||
"instant",
|
"instant",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@ -71,7 +124,7 @@ version = "1.0.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb"
|
checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"hermit-abi 0.3.1",
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
@ -82,6 +135,12 @@ version = "1.0.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.140"
|
version = "0.2.140"
|
||||||
@ -98,12 +157,44 @@ checksum = "cd550e73688e6d578f0ac2119e32b797a327631a42f9433e59d02e139c8df60d"
|
|||||||
name = "musichoard"
|
name = "musichoard"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"structopt",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.17.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.54"
|
version = "1.0.54"
|
||||||
@ -168,7 +259,7 @@ checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.11",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -182,6 +273,47 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "structopt"
|
||||||
|
version = "0.3.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"lazy_static",
|
||||||
|
"structopt-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "structopt-derive"
|
||||||
|
version = "0.4.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.11"
|
version = "2.0.11"
|
||||||
@ -206,12 +338,33 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.8"
|
version = "1.0.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@ -221,6 +374,40 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vec_map"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.45.0"
|
version = "0.45.0"
|
||||||
|
@ -8,7 +8,9 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
structopt = "0.3"
|
||||||
uuid = { version = "1.3", features = ["serde"] }
|
uuid = { version = "1.3", features = ["serde"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
once_cell = "1.17"
|
||||||
tempfile = "3.5"
|
tempfile = "3.5"
|
||||||
|
@ -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::{read_to_string, write};
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
@ -56,8 +56,10 @@ pub struct DatabaseJsonFile {
|
|||||||
|
|
||||||
impl DatabaseJsonFile {
|
impl DatabaseJsonFile {
|
||||||
/// Create a database instance that will read/write to the provided path.
|
/// Create a database instance that will read/write to the provided path.
|
||||||
pub fn new(path: PathBuf) -> Self {
|
pub fn new(path: &Path) -> Self {
|
||||||
DatabaseJsonFile { path }
|
DatabaseJsonFile {
|
||||||
|
path: path.to_path_buf(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,11 +67,11 @@ impl DatabaseJsonBackend for DatabaseJsonFile {
|
|||||||
fn read(&self) -> Result<String, std::io::Error> {
|
fn read(&self) -> Result<String, std::io::Error> {
|
||||||
// Read entire file to memory as for now this is faster than a buffered read from disk:
|
// 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
|
// https://github.com/serde-rs/json/issues/160
|
||||||
read_to_string(&self.path)
|
fs::read_to_string(&self.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self, json: &str) -> Result<(), std::io::Error> {
|
fn write(&mut self, json: &str) -> Result<(), std::io::Error> {
|
||||||
write(&self.path, json)
|
fs::write(&self.path, json)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +79,7 @@ impl DatabaseJsonBackend for DatabaseJsonFile {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::{tests::test_data, Artist};
|
use crate::{tests::COLLECTION, Artist};
|
||||||
|
|
||||||
struct DatabaseJsonTest {
|
struct DatabaseJsonTest {
|
||||||
json: String,
|
json: String,
|
||||||
@ -154,7 +156,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn write() {
|
fn write() {
|
||||||
let write_data = test_data();
|
let write_data = COLLECTION.to_owned();
|
||||||
let backend = DatabaseJsonTest {
|
let backend = DatabaseJsonTest {
|
||||||
json: artists_to_json(&write_data),
|
json: artists_to_json(&write_data),
|
||||||
};
|
};
|
||||||
@ -166,7 +168,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn read() {
|
fn read() {
|
||||||
let expected = test_data();
|
let expected = COLLECTION.to_owned();
|
||||||
let backend = DatabaseJsonTest {
|
let backend = DatabaseJsonTest {
|
||||||
json: artists_to_json(&expected),
|
json: artists_to_json(&expected),
|
||||||
};
|
};
|
||||||
@ -181,13 +183,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reverse() {
|
fn reverse() {
|
||||||
let expected = test_data();
|
let expected = COLLECTION.to_owned();
|
||||||
let backend = DatabaseJsonTest {
|
let backend = DatabaseJsonTest {
|
||||||
json: artists_to_json(&expected),
|
json: artists_to_json(&expected),
|
||||||
};
|
};
|
||||||
let mut database = DatabaseJson::new(Box::new(backend));
|
let mut database = DatabaseJson::new(Box::new(backend));
|
||||||
|
|
||||||
let write_data = test_data();
|
let write_data = COLLECTION.to_owned();
|
||||||
let mut read_data: Vec<Artist> = vec![];
|
let mut read_data: Vec<Artist> = vec![];
|
||||||
database.write(&write_data).unwrap();
|
database.write(&write_data).unwrap();
|
||||||
database.read(&mut read_data).unwrap();
|
database.read(&mut read_data).unwrap();
|
||||||
|
16
src/lib.rs
16
src/lib.rs
@ -10,7 +10,7 @@ pub mod library;
|
|||||||
pub type Mbid = Uuid;
|
pub type Mbid = Uuid;
|
||||||
|
|
||||||
/// A single track on an album.
|
/// A single track on an album.
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
pub struct Track {
|
pub struct Track {
|
||||||
pub number: u32,
|
pub number: u32,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
@ -18,27 +18,27 @@ pub struct Track {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The album identifier.
|
/// The album identifier.
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone, Hash)]
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)]
|
||||||
pub struct AlbumId {
|
pub struct AlbumId {
|
||||||
pub year: u32,
|
pub year: u32,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An album is a collection of tracks that were released together.
|
/// An album is a collection of tracks that were released together.
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
pub struct Album {
|
pub struct Album {
|
||||||
pub id: AlbumId,
|
pub id: AlbumId,
|
||||||
pub tracks: Vec<Track>,
|
pub tracks: Vec<Track>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The artist identifier.
|
/// The artist identifier.
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone, Hash)]
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)]
|
||||||
pub struct ArtistId {
|
pub struct ArtistId {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An artist.
|
/// An artist.
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
pub struct Artist {
|
pub struct Artist {
|
||||||
pub id: ArtistId,
|
pub id: ArtistId,
|
||||||
pub albums: Vec<Album>,
|
pub albums: Vec<Album>,
|
||||||
@ -48,7 +48,9 @@ pub struct Artist {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn test_data() -> Vec<Artist> {
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| {
|
||||||
vec![
|
vec![
|
||||||
Artist {
|
Artist {
|
||||||
id: ArtistId {
|
id: ArtistId {
|
||||||
@ -128,5 +130,5 @@ mod tests {
|
|||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,9 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
fmt::Display,
|
fmt::Display,
|
||||||
|
path::{Path, PathBuf},
|
||||||
process::Command,
|
process::Command,
|
||||||
|
str,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Album, AlbumId, Artist, ArtistId, Track};
|
use crate::{Album, AlbumId, Artist, ArtistId, Track};
|
||||||
@ -30,7 +32,7 @@ impl<T: SimpleOption + Display> QueryOptionArgBeets for QueryOption<T> {
|
|||||||
Self::Exclude(value) => ("^", value),
|
Self::Exclude(value) => ("^", value),
|
||||||
Self::None => return None,
|
Self::None => return None,
|
||||||
};
|
};
|
||||||
Some(format!("{}{}{}", negate, option_name, value))
|
Some(format!("{negate}{option_name}{value}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +43,7 @@ impl QueryOptionArgBeets for QueryOption<Vec<String>> {
|
|||||||
Self::Exclude(value) => ("^", value),
|
Self::Exclude(value) => ("^", value),
|
||||||
Self::None => return None,
|
Self::None => return None,
|
||||||
};
|
};
|
||||||
Some(format!("{}{}{}", negate, option_name, vec.join("; ")))
|
Some(format!("{negate}{option_name}{}", vec.join("; ")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,8 +153,11 @@ impl LibraryPrivate for Beets {
|
|||||||
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() {
|
||||||
let split: Vec<&str> = line.split(Self::LIST_FORMAT_SEPARATOR).collect();
|
if line.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let split: Vec<&str> = line.split(Self::LIST_FORMAT_SEPARATOR).collect();
|
||||||
if split.len() != 6 {
|
if split.len() != 6 {
|
||||||
return Err(Error::InvalidData(line.to_string()));
|
return Err(Error::InvalidData(line.to_string()));
|
||||||
}
|
}
|
||||||
@ -221,14 +226,21 @@ impl LibraryPrivate for Beets {
|
|||||||
/// Executor for executing beets commands on the local system.
|
/// Executor for executing beets commands on the local system.
|
||||||
pub struct SystemExecutor {
|
pub struct SystemExecutor {
|
||||||
bin: String,
|
bin: String,
|
||||||
|
config: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SystemExecutor {
|
impl SystemExecutor {
|
||||||
pub fn new(bin: &str) -> SystemExecutor {
|
pub fn new(bin: &str) -> Self {
|
||||||
SystemExecutor {
|
SystemExecutor {
|
||||||
bin: bin.to_string(),
|
bin: bin.to_string(),
|
||||||
|
config: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn config(mut self, path: Option<&Path>) -> Self {
|
||||||
|
self.config = path.map(|p| p.to_path_buf());
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SystemExecutor {
|
impl Default for SystemExecutor {
|
||||||
@ -239,8 +251,18 @@ impl Default for SystemExecutor {
|
|||||||
|
|
||||||
impl BeetsExecutor for SystemExecutor {
|
impl BeetsExecutor for SystemExecutor {
|
||||||
fn exec(&mut self, arguments: &[String]) -> Result<Vec<String>, Error> {
|
fn exec(&mut self, arguments: &[String]) -> Result<Vec<String>, Error> {
|
||||||
let output = Command::new(&self.bin).args(arguments).output()?;
|
let mut cmd = Command::new(&self.bin);
|
||||||
let output = std::str::from_utf8(&output.stdout)?;
|
if let Some(ref path) = self.config {
|
||||||
|
cmd.arg("--config");
|
||||||
|
cmd.arg(path);
|
||||||
|
}
|
||||||
|
let output = cmd.args(arguments).output()?;
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err(Error::CmdExecError(
|
||||||
|
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())
|
Ok(output.split('\n').map(|s| s.to_string()).collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -249,7 +271,7 @@ impl BeetsExecutor for SystemExecutor {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::tests::test_data;
|
use crate::tests::COLLECTION;
|
||||||
|
|
||||||
struct TestExecutor {
|
struct TestExecutor {
|
||||||
arguments: Option<Vec<String>>,
|
arguments: Option<Vec<String>>,
|
||||||
@ -333,7 +355,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_list_ordered() {
|
fn test_list_ordered() {
|
||||||
let expected = test_data();
|
let expected = COLLECTION.to_owned();
|
||||||
let output = artists_to_beets_string(&expected);
|
let output = artists_to_beets_string(&expected);
|
||||||
|
|
||||||
let executor = TestExecutor {
|
let executor = TestExecutor {
|
||||||
@ -348,7 +370,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_list_unordered() {
|
fn test_list_unordered() {
|
||||||
let mut expected = test_data();
|
let mut expected = COLLECTION.to_owned();
|
||||||
let mut output = artists_to_beets_string(&expected);
|
let mut output = artists_to_beets_string(&expected);
|
||||||
let last = output.len() - 1;
|
let last = output.len() - 1;
|
||||||
output.swap(0, last);
|
output.swap(0, last);
|
||||||
@ -377,7 +399,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_list_album_title_year_clash() {
|
fn test_list_album_title_year_clash() {
|
||||||
let mut expected = test_data();
|
let mut expected = COLLECTION.to_owned();
|
||||||
expected[0].albums[0].id.year = expected[1].albums[0].id.year;
|
expected[0].albums[0].id.year = expected[1].albums[0].id.year;
|
||||||
expected[0].albums[0].id.title = expected[1].albums[0].id.title.clone();
|
expected[0].albums[0].id.title = expected[1].albums[0].id.title.clone();
|
||||||
|
|
||||||
|
@ -98,6 +98,8 @@ impl Query {
|
|||||||
/// Error type for library calls.
|
/// Error type for library calls.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
/// The underlying library failed to execute a command.
|
||||||
|
CmdExecError(String),
|
||||||
/// The underlying library returned invalid data.
|
/// The underlying library returned invalid data.
|
||||||
InvalidData(String),
|
InvalidData(String),
|
||||||
/// The underlying library experienced an I/O error.
|
/// The underlying library experienced an I/O error.
|
||||||
|
52
src/main.rs
Normal file
52
src/main.rs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
use musichoard::{
|
||||||
|
database::{
|
||||||
|
json::{DatabaseJson, DatabaseJsonFile},
|
||||||
|
DatabaseWrite,
|
||||||
|
},
|
||||||
|
library::{
|
||||||
|
beets::{Beets, SystemExecutor},
|
||||||
|
Library, Query,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(StructOpt)]
|
||||||
|
struct Opt {
|
||||||
|
#[structopt(
|
||||||
|
short = "b",
|
||||||
|
long = "beets-config",
|
||||||
|
name = "beets config file path",
|
||||||
|
parse(from_os_str)
|
||||||
|
)]
|
||||||
|
beets_config_file_path: Option<PathBuf>,
|
||||||
|
|
||||||
|
#[structopt(
|
||||||
|
short = "d",
|
||||||
|
long = "database",
|
||||||
|
name = "database file path",
|
||||||
|
default_value = "database.json",
|
||||||
|
parse(from_os_str)
|
||||||
|
)]
|
||||||
|
database_file_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let opt = Opt::from_args();
|
||||||
|
|
||||||
|
let mut beets = Beets::new(Box::new(
|
||||||
|
SystemExecutor::default().config(opt.beets_config_file_path.as_deref()),
|
||||||
|
));
|
||||||
|
|
||||||
|
let collection = beets
|
||||||
|
.list(&Query::new())
|
||||||
|
.expect("failed to query the library");
|
||||||
|
|
||||||
|
let mut database = DatabaseJson::new(Box::new(DatabaseJsonFile::new(&opt.database_file_path)));
|
||||||
|
|
||||||
|
database
|
||||||
|
.write(&collection)
|
||||||
|
.expect("failed to write to the database");
|
||||||
|
}
|
59
tests/database/json.rs
Normal file
59
tests/database/json.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
use std::fs;
|
||||||
|
|
||||||
|
use musichoard::{
|
||||||
|
database::{
|
||||||
|
json::{DatabaseJson, DatabaseJsonFile},
|
||||||
|
DatabaseRead, DatabaseWrite,
|
||||||
|
},
|
||||||
|
Artist,
|
||||||
|
};
|
||||||
|
use tempfile::NamedTempFile;
|
||||||
|
|
||||||
|
use crate::COLLECTION;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write() {
|
||||||
|
let file = NamedTempFile::new().unwrap();
|
||||||
|
|
||||||
|
let backend = DatabaseJsonFile::new(file.path());
|
||||||
|
let mut database = DatabaseJson::new(Box::new(backend));
|
||||||
|
|
||||||
|
let write_data = COLLECTION.to_owned();
|
||||||
|
database.write(&write_data).unwrap();
|
||||||
|
|
||||||
|
let expected_path = fs::canonicalize("./tests/files/database/database.json").unwrap();
|
||||||
|
let expected = fs::read_to_string(expected_path).unwrap();
|
||||||
|
let actual = fs::read_to_string(file.path()).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn read() {
|
||||||
|
let file_path = fs::canonicalize("./tests/files/database/database.json").unwrap();
|
||||||
|
|
||||||
|
let backend = DatabaseJsonFile::new(&file_path);
|
||||||
|
let database = DatabaseJson::new(Box::new(backend));
|
||||||
|
|
||||||
|
let mut read_data: Vec<Artist> = vec![];
|
||||||
|
database.read(&mut read_data).unwrap();
|
||||||
|
|
||||||
|
let expected = COLLECTION.to_owned();
|
||||||
|
assert_eq!(read_data, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reverse() {
|
||||||
|
let file = NamedTempFile::new().unwrap();
|
||||||
|
|
||||||
|
let backend = DatabaseJsonFile::new(file.path());
|
||||||
|
let mut database = DatabaseJson::new(Box::new(backend));
|
||||||
|
|
||||||
|
let write_data = COLLECTION.to_owned();
|
||||||
|
database.write(&write_data).unwrap();
|
||||||
|
|
||||||
|
let mut read_data: Vec<Artist> = vec![];
|
||||||
|
database.read(&mut read_data).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(write_data, read_data);
|
||||||
|
}
|
1
tests/database/mod.rs
Normal file
1
tests/database/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
mod json;
|
1
tests/files/.gitignore
vendored
Normal file
1
tests/files/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
library
|
1
tests/files/database/database.json
Normal file
1
tests/files/database/database.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{"id":{"name":"Аркона"},"albums":[{"id":{"year":2011,"title":"Slovo"},"tracks":[{"number":1,"title":"Az’","artist":["Аркона"]},{"number":2,"title":"Arkaim","artist":["Аркона"]},{"number":3,"title":"Bol’no mne","artist":["Аркона"]},{"number":4,"title":"Leshiy","artist":["Аркона"]},{"number":5,"title":"Zakliatie","artist":["Аркона"]},{"number":6,"title":"Predok","artist":["Аркона"]},{"number":7,"title":"Nikogda","artist":["Аркона"]},{"number":8,"title":"Tam za tumanami","artist":["Аркона"]},{"number":9,"title":"Potomok","artist":["Аркона"]},{"number":10,"title":"Slovo","artist":["Аркона"]},{"number":11,"title":"Odna","artist":["Аркона"]},{"number":12,"title":"Vo moiom sadochke…","artist":["Аркона"]},{"number":13,"title":"Stenka na stenku","artist":["Аркона"]},{"number":14,"title":"Zimushka","artist":["Аркона"]}]}]},{"id":{"name":"Eluveitie"},"albums":[{"id":{"year":2008,"title":"Slania"},"tracks":[{"number":1,"title":"Samon","artist":["Eluveitie"]},{"number":2,"title":"Primordial Breath","artist":["Eluveitie"]},{"number":3,"title":"Inis Mona","artist":["Eluveitie"]},{"number":4,"title":"Gray Sublime Archon","artist":["Eluveitie"]},{"number":5,"title":"Anagantios","artist":["Eluveitie"]},{"number":6,"title":"Bloodstained Ground","artist":["Eluveitie"]},{"number":7,"title":"The Somber Lay","artist":["Eluveitie"]},{"number":8,"title":"Slanias Song","artist":["Eluveitie"]},{"number":9,"title":"Giamonios","artist":["Eluveitie"]},{"number":10,"title":"Tarvos","artist":["Eluveitie"]},{"number":11,"title":"Calling the Rain","artist":["Eluveitie"]},{"number":12,"title":"Elembivos","artist":["Eluveitie"]}]},{"id":{"year":2004,"title":"Vên [re‐recorded]"},"tracks":[{"number":1,"title":"Verja Urit an Bitus","artist":["Eluveitie"]},{"number":2,"title":"Uis Elveti","artist":["Eluveitie"]},{"number":3,"title":"Ôrô","artist":["Eluveitie"]},{"number":4,"title":"Lament","artist":["Eluveitie"]},{"number":5,"title":"Druid","artist":["Eluveitie"]},{"number":6,"title":"Jêzaïg","artist":["Eluveitie"]}]}]},{"id":{"name":"Frontside"},"albums":[{"id":{"year":2001,"title":"…nasze jest królestwo, potęga i chwała na wieki…"},"tracks":[{"number":1,"title":"Intro = Chaos","artist":["Frontside"]},{"number":2,"title":"Modlitwa","artist":["Frontside"]},{"number":3,"title":"Długa droga z piekła","artist":["Frontside"]},{"number":4,"title":"Synowie ognia","artist":["Frontside"]},{"number":5,"title":"1902","artist":["Frontside"]},{"number":6,"title":"Krew za krew","artist":["Frontside"]},{"number":7,"title":"Kulminacja","artist":["Frontside"]},{"number":8,"title":"Judasz","artist":["Frontside"]},{"number":9,"title":"Więzy","artist":["Frontside"]},{"number":10,"title":"Zagubione dusze","artist":["Frontside"]},{"number":11,"title":"Linia życia","artist":["Frontside"]}]}]},{"id":{"name":"Metallica"},"albums":[{"id":{"year":1984,"title":"Ride the Lightning"},"tracks":[{"number":1,"title":"Fight Fire with Fire","artist":["Metallica"]},{"number":2,"title":"Ride the Lightning","artist":["Metallica"]},{"number":3,"title":"For Whom the Bell Tolls","artist":["Metallica"]},{"number":4,"title":"Fade to Black","artist":["Metallica"]},{"number":5,"title":"Trapped under Ice","artist":["Metallica"]},{"number":6,"title":"Escape","artist":["Metallica"]},{"number":7,"title":"Creeping Death","artist":["Metallica"]},{"number":8,"title":"The Call of Ktulu","artist":["Metallica"]}]},{"id":{"year":1999,"title":"S&M"},"tracks":[{"number":1,"title":"The Ecstasy of Gold","artist":["Metallica"]},{"number":2,"title":"The Call of Ktulu","artist":["Metallica"]},{"number":3,"title":"Master of Puppets","artist":["Metallica"]},{"number":4,"title":"Of Wolf and Man","artist":["Metallica"]},{"number":5,"title":"The Thing That Should Not Be","artist":["Metallica"]},{"number":6,"title":"Fuel","artist":["Metallica"]},{"number":7,"title":"The Memory Remains","artist":["Metallica"]},{"number":8,"title":"No Leaf Clover","artist":["Metallica"]},{"number":9,"title":"Hero of the Day","artist":["Metallica"]},{"number":10,"title":"Devil’s Dance","artist":["Metallica"]},{"number":11,"title":"Bleeding Me","artist":["Metallica"]},{"number":12,"title":"Nothing Else Matters","artist":["Metallica"]},{"number":13,"title":"Until It Sleeps","artist":["Metallica"]},{"number":14,"title":"For Whom the Bell Tolls","artist":["Metallica"]},{"number":15,"title":"−Human","artist":["Metallica"]},{"number":16,"title":"Wherever I May Roam","artist":["Metallica"]},{"number":17,"title":"Outlaw Torn","artist":["Metallica"]},{"number":18,"title":"Sad but True","artist":["Metallica"]},{"number":19,"title":"One","artist":["Metallica"]},{"number":20,"title":"Enter Sandman","artist":["Metallica"]},{"number":21,"title":"Battery","artist":["Metallica"]}]}]}]
|
442
tests/lib.rs
Normal file
442
tests/lib.rs
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
mod database;
|
||||||
|
mod library;
|
||||||
|
|
||||||
|
use musichoard::{Album, AlbumId, Artist, ArtistId, Track};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
Artist {
|
||||||
|
id: ArtistId {
|
||||||
|
name: String::from("Аркона"),
|
||||||
|
},
|
||||||
|
albums: vec![Album {
|
||||||
|
id: AlbumId {
|
||||||
|
year: 2011,
|
||||||
|
title: String::from("Slovo"),
|
||||||
|
},
|
||||||
|
tracks: vec![
|
||||||
|
Track {
|
||||||
|
number: 01,
|
||||||
|
title: String::from("Az’"),
|
||||||
|
artist: vec![String::from("Аркона")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 02,
|
||||||
|
title: String::from("Arkaim"),
|
||||||
|
artist: vec![String::from("Аркона")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 03,
|
||||||
|
title: String::from("Bol’no mne"),
|
||||||
|
artist: vec![String::from("Аркона")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 04,
|
||||||
|
title: String::from("Leshiy"),
|
||||||
|
artist: vec![String::from("Аркона")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 05,
|
||||||
|
title: String::from("Zakliatie"),
|
||||||
|
artist: vec![String::from("Аркона")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 06,
|
||||||
|
title: String::from("Predok"),
|
||||||
|
artist: vec![String::from("Аркона")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 07,
|
||||||
|
title: String::from("Nikogda"),
|
||||||
|
artist: vec![String::from("Аркона")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 08,
|
||||||
|
title: String::from("Tam za tumanami"),
|
||||||
|
artist: vec![String::from("Аркона")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 09,
|
||||||
|
title: String::from("Potomok"),
|
||||||
|
artist: vec![String::from("Аркона")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 10,
|
||||||
|
title: String::from("Slovo"),
|
||||||
|
artist: vec![String::from("Аркона")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 11,
|
||||||
|
title: String::from("Odna"),
|
||||||
|
artist: vec![String::from("Аркона")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 12,
|
||||||
|
title: String::from("Vo moiom sadochke…"),
|
||||||
|
artist: vec![String::from("Аркона")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 13,
|
||||||
|
title: String::from("Stenka na stenku"),
|
||||||
|
artist: vec![String::from("Аркона")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 14,
|
||||||
|
title: String::from("Zimushka"),
|
||||||
|
artist: vec![String::from("Аркона")],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
Artist {
|
||||||
|
id: ArtistId {
|
||||||
|
name: String::from("Eluveitie"),
|
||||||
|
},
|
||||||
|
albums: vec![
|
||||||
|
Album {
|
||||||
|
id: AlbumId {
|
||||||
|
year: 2008,
|
||||||
|
title: String::from("Slania"),
|
||||||
|
},
|
||||||
|
tracks: vec![
|
||||||
|
Track {
|
||||||
|
number: 01,
|
||||||
|
title: String::from("Samon"),
|
||||||
|
artist: vec![String::from("Eluveitie")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 02,
|
||||||
|
title: String::from("Primordial Breath"),
|
||||||
|
artist: vec![String::from("Eluveitie")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 03,
|
||||||
|
title: String::from("Inis Mona"),
|
||||||
|
artist: vec![String::from("Eluveitie")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 04,
|
||||||
|
title: String::from("Gray Sublime Archon"),
|
||||||
|
artist: vec![String::from("Eluveitie")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 05,
|
||||||
|
title: String::from("Anagantios"),
|
||||||
|
artist: vec![String::from("Eluveitie")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 06,
|
||||||
|
title: String::from("Bloodstained Ground"),
|
||||||
|
artist: vec![String::from("Eluveitie")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 07,
|
||||||
|
title: String::from("The Somber Lay"),
|
||||||
|
artist: vec![String::from("Eluveitie")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 08,
|
||||||
|
title: String::from("Slanias Song"),
|
||||||
|
artist: vec![String::from("Eluveitie")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 09,
|
||||||
|
title: String::from("Giamonios"),
|
||||||
|
artist: vec![String::from("Eluveitie")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 10,
|
||||||
|
title: String::from("Tarvos"),
|
||||||
|
artist: vec![String::from("Eluveitie")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 11,
|
||||||
|
title: String::from("Calling the Rain"),
|
||||||
|
artist: vec![String::from("Eluveitie")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 12,
|
||||||
|
title: String::from("Elembivos"),
|
||||||
|
artist: vec![String::from("Eluveitie")],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
Album {
|
||||||
|
id: AlbumId {
|
||||||
|
year: 2004,
|
||||||
|
title: String::from("Vên [re‐recorded]"),
|
||||||
|
},
|
||||||
|
tracks: vec![
|
||||||
|
Track {
|
||||||
|
number: 01,
|
||||||
|
title: String::from("Verja Urit an Bitus"),
|
||||||
|
artist: vec![String::from("Eluveitie")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 02,
|
||||||
|
title: String::from("Uis Elveti"),
|
||||||
|
artist: vec![String::from("Eluveitie")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 03,
|
||||||
|
title: String::from("Ôrô"),
|
||||||
|
artist: vec![String::from("Eluveitie")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 04,
|
||||||
|
title: String::from("Lament"),
|
||||||
|
artist: vec![String::from("Eluveitie")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 05,
|
||||||
|
title: String::from("Druid"),
|
||||||
|
artist: vec![String::from("Eluveitie")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 06,
|
||||||
|
title: String::from("Jêzaïg"),
|
||||||
|
artist: vec![String::from("Eluveitie")],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
Artist {
|
||||||
|
id: ArtistId {
|
||||||
|
name: String::from("Frontside"),
|
||||||
|
},
|
||||||
|
albums: vec![Album {
|
||||||
|
id: AlbumId {
|
||||||
|
year: 2001,
|
||||||
|
title: String::from("…nasze jest królestwo, potęga i chwała na wieki…"),
|
||||||
|
},
|
||||||
|
tracks: vec![
|
||||||
|
Track {
|
||||||
|
number: 01,
|
||||||
|
title: String::from("Intro = Chaos"),
|
||||||
|
artist: vec![String::from("Frontside")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 02,
|
||||||
|
title: String::from("Modlitwa"),
|
||||||
|
artist: vec![String::from("Frontside")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 03,
|
||||||
|
title: String::from("Długa droga z piekła"),
|
||||||
|
artist: vec![String::from("Frontside")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 04,
|
||||||
|
title: String::from("Synowie ognia"),
|
||||||
|
artist: vec![String::from("Frontside")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 05,
|
||||||
|
title: String::from("1902"),
|
||||||
|
artist: vec![String::from("Frontside")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 06,
|
||||||
|
title: String::from("Krew za krew"),
|
||||||
|
artist: vec![String::from("Frontside")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 07,
|
||||||
|
title: String::from("Kulminacja"),
|
||||||
|
artist: vec![String::from("Frontside")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 08,
|
||||||
|
title: String::from("Judasz"),
|
||||||
|
artist: vec![String::from("Frontside")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 09,
|
||||||
|
title: String::from("Więzy"),
|
||||||
|
artist: vec![String::from("Frontside")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 10,
|
||||||
|
title: String::from("Zagubione dusze"),
|
||||||
|
artist: vec![String::from("Frontside")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 11,
|
||||||
|
title: String::from("Linia życia"),
|
||||||
|
artist: vec![String::from("Frontside")],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
Artist {
|
||||||
|
id: ArtistId {
|
||||||
|
name: String::from("Metallica"),
|
||||||
|
},
|
||||||
|
albums: vec![
|
||||||
|
Album {
|
||||||
|
id: AlbumId {
|
||||||
|
year: 1984,
|
||||||
|
title: String::from("Ride the Lightning"),
|
||||||
|
},
|
||||||
|
tracks: vec![
|
||||||
|
Track {
|
||||||
|
number: 01,
|
||||||
|
title: String::from("Fight Fire with Fire"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 02,
|
||||||
|
title: String::from("Ride the Lightning"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 03,
|
||||||
|
title: String::from("For Whom the Bell Tolls"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 04,
|
||||||
|
title: String::from("Fade to Black"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 05,
|
||||||
|
title: String::from("Trapped under Ice"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 06,
|
||||||
|
title: String::from("Escape"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 07,
|
||||||
|
title: String::from("Creeping Death"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 08,
|
||||||
|
title: String::from("The Call of Ktulu"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
Album {
|
||||||
|
id: AlbumId {
|
||||||
|
year: 1999,
|
||||||
|
title: String::from("S&M"),
|
||||||
|
},
|
||||||
|
tracks: vec![
|
||||||
|
Track {
|
||||||
|
number: 01,
|
||||||
|
title: String::from("The Ecstasy of Gold"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 02,
|
||||||
|
title: String::from("The Call of Ktulu"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 03,
|
||||||
|
title: String::from("Master of Puppets"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 04,
|
||||||
|
title: String::from("Of Wolf and Man"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 05,
|
||||||
|
title: String::from("The Thing That Should Not Be"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 06,
|
||||||
|
title: String::from("Fuel"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 07,
|
||||||
|
title: String::from("The Memory Remains"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 08,
|
||||||
|
title: String::from("No Leaf Clover"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 09,
|
||||||
|
title: String::from("Hero of the Day"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 10,
|
||||||
|
title: String::from("Devil’s Dance"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 11,
|
||||||
|
title: String::from("Bleeding Me"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 12,
|
||||||
|
title: String::from("Nothing Else Matters"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 13,
|
||||||
|
title: String::from("Until It Sleeps"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 14,
|
||||||
|
title: String::from("For Whom the Bell Tolls"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 15,
|
||||||
|
title: String::from("−Human"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 16,
|
||||||
|
title: String::from("Wherever I May Roam"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 17,
|
||||||
|
title: String::from("Outlaw Torn"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 18,
|
||||||
|
title: String::from("Sad but True"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 19,
|
||||||
|
title: String::from("One"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 20,
|
||||||
|
title: String::from("Enter Sandman"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
Track {
|
||||||
|
number: 21,
|
||||||
|
title: String::from("Battery"),
|
||||||
|
artist: vec![String::from("Metallica")],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
80
tests/library/beets.rs
Normal file
80
tests/library/beets.rs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
use std::fs;
|
||||||
|
|
||||||
|
use musichoard::{
|
||||||
|
library::{
|
||||||
|
beets::{Beets, SystemExecutor},
|
||||||
|
Library, Query, QueryOption,
|
||||||
|
},
|
||||||
|
Artist,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::COLLECTION;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_no_config_list() {
|
||||||
|
let executor = SystemExecutor::default();
|
||||||
|
|
||||||
|
let mut beets = Beets::new(Box::new(executor));
|
||||||
|
let output = beets.list(&Query::default()).unwrap();
|
||||||
|
|
||||||
|
let expected: Vec<Artist> = vec![];
|
||||||
|
assert_eq!(output, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_full_list() {
|
||||||
|
let executor = SystemExecutor::default().config(Some(
|
||||||
|
&fs::canonicalize("./tests/files/library/config.yml").unwrap(),
|
||||||
|
));
|
||||||
|
|
||||||
|
let mut beets = Beets::new(Box::new(executor));
|
||||||
|
let output = beets.list(&Query::default()).unwrap();
|
||||||
|
|
||||||
|
let expected: Vec<Artist> = COLLECTION.to_owned();
|
||||||
|
assert_eq!(output, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_album_artist_query() {
|
||||||
|
let executor = SystemExecutor::default().config(Some(
|
||||||
|
&fs::canonicalize("./tests/files/library/config.yml").unwrap(),
|
||||||
|
));
|
||||||
|
|
||||||
|
let mut beets = Beets::new(Box::new(executor));
|
||||||
|
let output = beets
|
||||||
|
.list(&Query::default().album_artist(QueryOption::Include(String::from("Аркона"))))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let expected: Vec<Artist> = COLLECTION[0..1].to_owned();
|
||||||
|
assert_eq!(output, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_album_title_query() {
|
||||||
|
let executor = SystemExecutor::default().config(Some(
|
||||||
|
&fs::canonicalize("./tests/files/library/config.yml").unwrap(),
|
||||||
|
));
|
||||||
|
|
||||||
|
let mut beets = Beets::new(Box::new(executor));
|
||||||
|
let output = beets
|
||||||
|
.list(&Query::default().album_title(QueryOption::Include(String::from("Slovo"))))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let expected: Vec<Artist> = COLLECTION[0..1].to_owned();
|
||||||
|
assert_eq!(output, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_exclude_query() {
|
||||||
|
let executor = SystemExecutor::default().config(Some(
|
||||||
|
&fs::canonicalize("./tests/files/library/config.yml").unwrap(),
|
||||||
|
));
|
||||||
|
|
||||||
|
let mut beets = Beets::new(Box::new(executor));
|
||||||
|
let output = beets
|
||||||
|
.list(&Query::default().album_artist(QueryOption::Exclude(String::from("Аркона"))))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let expected: Vec<Artist> = COLLECTION[1..].to_owned();
|
||||||
|
assert_eq!(output, expected);
|
||||||
|
}
|
1
tests/library/mod.rs
Normal file
1
tests/library/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
mod beets;
|
Loading…
Reference in New Issue
Block a user