Working implementation and tests

This commit is contained in:
Wojciech Kozlowski 2023-03-29 14:43:58 +09:00
parent b6474e3850
commit e2332f79ec
6 changed files with 357 additions and 23 deletions

189
Cargo.lock generated
View File

@ -2,18 +2,105 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "errno"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0"
dependencies = [
"errno-dragonfly",
"libc",
"windows-sys",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "fastrand"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
dependencies = [
"instant",
]
[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "io-lifetimes"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb"
dependencies = [
"hermit-abi",
"libc",
"windows-sys",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.6" 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 = "libc"
version = "0.2.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
[[package]]
name = "linux-raw-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd550e73688e6d578f0ac2119e32b797a327631a42f9433e59d02e139c8df60d"
[[package]] [[package]]
name = "musichoard" name = "musichoard"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"tempfile",
"uuid", "uuid",
] ]
@ -35,6 +122,29 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags",
]
[[package]]
name = "rustix"
version = "0.37.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c348b5dc624ecee40108aa2922fed8bad89d7fcc2b9f8cb18f632898ac4a37f9"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.13" version = "1.0.13"
@ -83,6 +193,19 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "tempfile"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"
dependencies = [
"cfg-if",
"fastrand",
"redox_syscall",
"rustix",
"windows-sys",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.8" version = "1.0.8"
@ -97,3 +220,69 @@ checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79"
dependencies = [ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"

View File

@ -9,3 +9,6 @@ edition = "2021"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
uuid = { version = "1.3", features = ["serde"] } uuid = { version = "1.3", features = ["serde"] }
[dev-dependencies]
tempfile = "3.5"

View File

@ -1,6 +1,5 @@
use std::fs::File; use std::fs::File;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::path::Path;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize; use serde::Serialize;
@ -9,31 +8,177 @@ use crate::database::DatabaseRead;
use super::DatabaseWrite; use super::DatabaseWrite;
struct DatabaseReaderJson {} pub struct DatabaseJson {
database_file: File,
}
impl DatabaseRead for DatabaseReaderJson { impl DatabaseJson {
fn read<D>(path: &Path, collection: &mut D) -> Result<(), std::io::Error> pub fn new(file: File) -> Self {
Self {
database_file: file,
}
}
}
impl DatabaseRead for DatabaseJson {
fn read<D>(&mut self, collection: &mut D) -> Result<(), std::io::Error>
where where
D: DeserializeOwned, D: DeserializeOwned,
{ {
// 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
let mut serialized = String::new(); let mut serialized = String::new();
File::open(path)?.read_to_string(&mut serialized)?; self.database_file.read_to_string(&mut serialized)?;
*collection = serde_json::from_str(&serialized)?; *collection = serde_json::from_str(&serialized)?;
Ok(()) Ok(())
} }
} }
struct DatabaseWriterJson {} impl DatabaseWrite for DatabaseJson {
fn write<S>(&mut self, collection: &S) -> Result<(), std::io::Error>
impl DatabaseWrite for DatabaseWriterJson {
fn write<S>(path: &Path, collection: &S) -> Result<(), std::io::Error>
where where
S: Serialize, S: Serialize,
{ {
let serialized = serde_json::to_string(&collection)?; let serialized = serde_json::to_string(&collection)?;
File::open(path)?.write_all(serialized.as_bytes()) self.database_file.write_all(serialized.as_bytes())
}
}
#[cfg(test)]
mod tests {
use std::path::Path;
use tempfile::NamedTempFile;
use uuid::uuid;
use super::*;
use crate::{Artist, Release, ReleaseGroup, ReleaseGroupType, Track};
const TEST_FILENAME: &str = "tests/files/database_json_test.json";
fn test_data() -> Vec<ReleaseGroup> {
vec![
ReleaseGroup {
r#type: ReleaseGroupType::Album,
title: String::from("Release group A"),
artist: vec![Artist {
name: String::from("Artist A"),
mbid: Some(uuid!("f7769831-746b-4a12-8124-0123d7fe17c9")),
}],
year: 1998,
mbid: Some(uuid!("89efbf43-3395-4f6e-ac11-32c1ce514bb0")),
releases: vec![Release {
tracks: vec![
Track {
number: 1,
title: String::from("Track A.1"),
artist: vec![Artist {
name: String::from("Artist A.A"),
mbid: Some(uuid!("b7f7163d-61d5-4c96-b305-005df54fb999")),
}],
mbid: None,
},
Track {
number: 2,
title: String::from("Track A.2"),
artist: vec![Artist {
name: String::from("Artist A.A"),
mbid: Some(uuid!("b7f7163d-61d5-4c96-b305-005df54fb999")),
}],
mbid: None,
},
Track {
number: 3,
title: String::from("Track A.3"),
artist: vec![
Artist {
name: String::from("Artist A.A"),
mbid: Some(uuid!("b7f7163d-61d5-4c96-b305-005df54fb999")),
},
Artist {
name: String::from("Artist A.B"),
mbid: Some(uuid!("6f6b46f2-4bb5-47e7-a8c8-03ebde30164f")),
},
],
mbid: None,
},
],
mbid: None,
}],
},
ReleaseGroup {
r#type: ReleaseGroupType::Single,
title: String::from("Release group B"),
artist: vec![Artist {
name: String::from("Artist B"),
mbid: None,
}],
year: 2008,
mbid: None,
releases: vec![Release {
tracks: vec![Track {
number: 1,
title: String::from("Track B.1"),
artist: vec![Artist {
name: String::from("Artist B.A"),
mbid: Some(uuid!("d927e216-2e63-415c-acec-bf9f1abd3e3c")),
}],
mbid: Some(uuid!("dacc9ce4-118c-4c92-aed7-1ebe4c7543b5")),
}],
mbid: Some(uuid!("ac7b642d-8b71-4588-a694-e5ae43fac873")),
}],
},
]
}
#[test]
fn write() {
let data = test_data();
let temp_file = NamedTempFile::new().unwrap();
DatabaseJson::new(File::create(temp_file.path()).unwrap())
.write(&data)
.unwrap();
// Compare files.
let mut data_str = String::new();
File::open(temp_file.path())
.unwrap()
.read_to_string(&mut data_str)
.unwrap();
let mut test_str = String::new();
File::open(TEST_FILENAME)
.unwrap()
.read_to_string(&mut test_str)
.unwrap();
assert_eq!(data_str, test_str);
}
#[test]
fn read() {
let mut data: Vec<ReleaseGroup> = vec![];
DatabaseJson::new(File::open(Path::new(TEST_FILENAME)).unwrap())
.read(&mut data)
.unwrap();
assert_eq!(data, test_data());
}
#[test]
fn reverse() {
let write_data = test_data();
let mut read_data: Vec<ReleaseGroup> = vec![];
let temp_file = NamedTempFile::new().unwrap();
DatabaseJson::new(File::create(temp_file.path()).unwrap())
.write(&write_data)
.unwrap();
DatabaseJson::new(File::open(temp_file.path()).unwrap())
.read(&mut read_data)
.unwrap();
assert_eq!(write_data, read_data);
} }
} }

View File

@ -1,18 +1,16 @@
use std::path::Path;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize; use serde::Serialize;
mod json; mod json;
trait DatabaseRead { trait DatabaseRead {
fn read<D>(path: &Path, collection: &mut D) -> Result<(), std::io::Error> fn read<D>(&mut self, collection: &mut D) -> Result<(), std::io::Error>
where where
D: DeserializeOwned; D: DeserializeOwned;
} }
trait DatabaseWrite { trait DatabaseWrite {
fn write<S>(path: &Path, collection: &S) -> Result<(), std::io::Error> fn write<S>(&mut self, collection: &S) -> Result<(), std::io::Error>
where where
S: Serialize; S: Serialize;
} }

View File

@ -9,32 +9,30 @@ mod database;
pub type Mbid = uuid::Uuid; pub type Mbid = uuid::Uuid;
/// [Artist](https://musicbrainz.org/doc/Artist). /// [Artist](https://musicbrainz.org/doc/Artist).
#[derive(Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize, PartialEq)]
pub struct Artist { pub struct Artist {
pub name: String, pub name: String,
pub mbid: Option<Mbid>, pub mbid: Option<Mbid>,
} }
/// [Track](https://musicbrainz.org/doc/Track). /// [Track](https://musicbrainz.org/doc/Track).
#[derive(Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize, PartialEq)]
pub struct Track { pub struct Track {
pub number: u32, pub number: u32,
pub title: String, pub title: String,
pub artist: Artist, pub artist: Vec<Artist>,
pub mbid: Option<Mbid>, pub mbid: Option<Mbid>,
} }
/// [Release](https://musicbrainz.org/doc/Release). /// [Release](https://musicbrainz.org/doc/Release).
#[derive(Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize, PartialEq)]
pub struct Release { pub struct Release {
pub title: String,
pub artist: String,
pub tracks: Vec<Track>, pub tracks: Vec<Track>,
pub mbid: Option<Mbid>, pub mbid: Option<Mbid>,
} }
/// [Release group primary type](https://musicbrainz.org/doc/Release_Group/Type). /// [Release group primary type](https://musicbrainz.org/doc/Release_Group/Type).
#[derive(Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize, PartialEq)]
pub enum ReleaseGroupType { pub enum ReleaseGroupType {
Album, Album,
Ep, Ep,
@ -43,11 +41,11 @@ pub enum ReleaseGroupType {
} }
/// [Release group](https://musicbrainz.org/doc/Release_Group). /// [Release group](https://musicbrainz.org/doc/Release_Group).
#[derive(Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize, PartialEq)]
pub struct ReleaseGroup { pub struct ReleaseGroup {
pub r#type: ReleaseGroupType, pub r#type: ReleaseGroupType,
pub title: String, pub title: String,
pub artist: Artist, pub artist: Vec<Artist>,
pub year: u32, pub year: u32,
pub mbid: Option<Mbid>, pub mbid: Option<Mbid>,
pub releases: Vec<Release>, pub releases: Vec<Release>,

View File

@ -0,0 +1 @@
[{"type":"Album","title":"Release group A","artist":[{"name":"Artist A","mbid":"f7769831-746b-4a12-8124-0123d7fe17c9"}],"year":1998,"mbid":"89efbf43-3395-4f6e-ac11-32c1ce514bb0","releases":[{"tracks":[{"number":1,"title":"Track A.1","artist":[{"name":"Artist A.A","mbid":"b7f7163d-61d5-4c96-b305-005df54fb999"}],"mbid":null},{"number":2,"title":"Track A.2","artist":[{"name":"Artist A.A","mbid":"b7f7163d-61d5-4c96-b305-005df54fb999"}],"mbid":null},{"number":3,"title":"Track A.3","artist":[{"name":"Artist A.A","mbid":"b7f7163d-61d5-4c96-b305-005df54fb999"},{"name":"Artist A.B","mbid":"6f6b46f2-4bb5-47e7-a8c8-03ebde30164f"}],"mbid":null}],"mbid":null}]},{"type":"Single","title":"Release group B","artist":[{"name":"Artist B","mbid":null}],"year":2008,"mbid":null,"releases":[{"tracks":[{"number":1,"title":"Track B.1","artist":[{"name":"Artist B.A","mbid":"d927e216-2e63-415c-acec-bf9f1abd3e3c"}],"mbid":"dacc9ce4-118c-4c92-aed7-1ebe4c7543b5"}],"mbid":"ac7b642d-8b71-4588-a694-e5ae43fac873"}]}]