Emphasize the more fundamental nature of the db

This commit is contained in:
Wojciech Kozlowski 2024-03-09 21:04:49 +01:00
parent ed271cf2a2
commit f95afd5f9a
6 changed files with 64 additions and 55 deletions

View File

@ -8,7 +8,7 @@ use musichoard::{
MusicHoard, MusicHoardBuilder, NoLibrary, MusicHoard, MusicHoardBuilder, NoLibrary,
}; };
type MH = MusicHoard<NoLibrary, JsonDatabase<JsonDatabaseFileBackend>>; type MH = MusicHoard<JsonDatabase<JsonDatabaseFileBackend>, NoLibrary>;
#[derive(StructOpt, Debug)] #[derive(StructOpt, Debug)]
#[structopt(about = "musichoard-edit: edit the MusicHoard database", #[structopt(about = "musichoard-edit: edit the MusicHoard database",

View File

@ -18,13 +18,13 @@ use crate::core::{
/// The Music Hoard. It is responsible for pulling information from both the library and the /// The Music Hoard. It is responsible for pulling information from both the library and the
/// database, ensuring its consistent and writing back any changes. /// database, ensuring its consistent and writing back any changes.
#[derive(Debug)] #[derive(Debug)]
pub struct MusicHoard<LIB, DB> { pub struct MusicHoard<Database, Library> {
collection: Collection, collection: Collection,
pre_commit: Collection, pre_commit: Collection,
library: LIB, database: Database,
database: DB,
library_cache: HashMap<ArtistId, Artist>,
database_cache: Collection, database_cache: Collection,
library: Library,
library_cache: HashMap<ArtistId, Artist>,
} }
/// Phantom type for when a library implementation is not needed. /// Phantom type for when a library implementation is not needed.
@ -35,28 +35,28 @@ pub struct NoLibrary;
#[derive(Debug)] #[derive(Debug)]
pub struct NoDatabase; pub struct NoDatabase;
impl Default for MusicHoard<NoLibrary, NoDatabase> { impl Default for MusicHoard<NoDatabase, NoLibrary> {
/// Create a new [`MusicHoard`] without any library or database. /// Create a new [`MusicHoard`] without any library or database.
fn default() -> Self { fn default() -> Self {
MusicHoard::empty() MusicHoard::empty()
} }
} }
impl MusicHoard<NoLibrary, NoDatabase> { impl MusicHoard<NoDatabase, NoLibrary> {
/// Create a new [`MusicHoard`] without any library or database. /// Create a new [`MusicHoard`] without any library or database.
pub fn empty() -> Self { pub fn empty() -> Self {
MusicHoard { MusicHoard {
collection: vec![], collection: vec![],
pre_commit: vec![], pre_commit: vec![],
library: NoLibrary,
database: NoDatabase, database: NoDatabase,
library_cache: HashMap::new(),
database_cache: vec![], database_cache: vec![],
library: NoLibrary,
library_cache: HashMap::new(),
} }
} }
} }
impl<LIB, DB> MusicHoard<LIB, DB> { impl<Database, Library> MusicHoard<Database, Library> {
/// Retrieve the [`Collection`]. /// Retrieve the [`Collection`].
pub fn get_collection(&self) -> &Collection { pub fn get_collection(&self) -> &Collection {
&self.collection &self.collection
@ -191,16 +191,16 @@ impl<LIB, DB> MusicHoard<LIB, DB> {
} }
} }
impl<LIB: ILibrary> MusicHoard<LIB, NoDatabase> { impl<Library: ILibrary> MusicHoard<NoDatabase, Library> {
/// Create a new [`MusicHoard`] with the provided [`ILibrary`] and no database. /// Create a new [`MusicHoard`] with the provided [`ILibrary`] and no database.
pub fn library(library: LIB) -> Self { pub fn library(library: Library) -> Self {
MusicHoard { MusicHoard {
collection: vec![], collection: vec![],
pre_commit: vec![], pre_commit: vec![],
library,
database: NoDatabase, database: NoDatabase,
library_cache: HashMap::new(),
database_cache: vec![], database_cache: vec![],
library,
library_cache: HashMap::new(),
} }
} }
@ -211,7 +211,7 @@ impl<LIB: ILibrary> MusicHoard<LIB, NoDatabase> {
} }
} }
impl<LIB: ILibrary, DB> MusicHoard<LIB, DB> { impl<Database, Library: ILibrary> MusicHoard<Database, Library> {
fn rescan_library_inner(&mut self) -> Result<Collection, Error> { fn rescan_library_inner(&mut self) -> Result<Collection, Error> {
let items = self.library.list(&Query::new())?; let items = self.library.list(&Query::new())?;
self.library_cache = Self::items_to_artists(items)?; self.library_cache = Self::items_to_artists(items)?;
@ -221,30 +221,30 @@ impl<LIB: ILibrary, DB> MusicHoard<LIB, DB> {
} }
} }
impl<DB: IDatabase> MusicHoard<NoLibrary, DB> { impl<Database: IDatabase> MusicHoard<Database, NoLibrary> {
/// Create a new [`MusicHoard`] with the provided [`IDatabase`] and no library. /// Create a new [`MusicHoard`] with the provided [`IDatabase`] and no library.
pub fn database(database: DB) -> Result<Self, Error> { pub fn database(database: Database) -> Result<Self, Error> {
let mut mh = MusicHoard { let mut mh = MusicHoard {
collection: vec![], collection: vec![],
pre_commit: vec![], pre_commit: vec![],
library: NoLibrary,
database, database,
library_cache: HashMap::new(),
database_cache: vec![], database_cache: vec![],
library: NoLibrary,
library_cache: HashMap::new(),
}; };
mh.reload_database()?; mh.reload_database()?;
Ok(mh) Ok(mh)
} }
} }
impl<LIB> MusicHoard<LIB, NoDatabase> { impl<Library> MusicHoard<NoDatabase, Library> {
fn commit(&mut self) -> Result<(), Error> { fn commit(&mut self) -> Result<(), Error> {
self.collection = self.pre_commit.clone(); self.collection = self.pre_commit.clone();
Ok(()) Ok(())
} }
} }
impl<LIB, DB: IDatabase> MusicHoard<LIB, DB> { impl<Database: IDatabase, Library> MusicHoard<Database, Library> {
/// Load the database and merge with the in-memory collection. /// Load the database and merge with the in-memory collection.
pub fn reload_database(&mut self) -> Result<(), Error> { pub fn reload_database(&mut self) -> Result<(), Error> {
self.database_cache = self.database.load()?; self.database_cache = self.database.load()?;
@ -448,16 +448,16 @@ impl<LIB, DB: IDatabase> MusicHoard<LIB, DB> {
} }
} }
impl<LIB: ILibrary, DB: IDatabase> MusicHoard<LIB, DB> { impl<Database: IDatabase, Library: ILibrary> MusicHoard<Database, Library> {
/// Create a new [`MusicHoard`] with the provided [`ILibrary`] and [`IDatabase`]. /// Create a new [`MusicHoard`] with the provided [`ILibrary`] and [`IDatabase`].
pub fn new(library: LIB, database: DB) -> Result<Self, Error> { pub fn new(database: Database, library: Library) -> Result<Self, Error> {
let mut mh = MusicHoard { let mut mh = MusicHoard {
collection: vec![], collection: vec![],
pre_commit: vec![], pre_commit: vec![],
library,
database, database,
library_cache: HashMap::new(),
database_cache: vec![], database_cache: vec![],
library,
library_cache: HashMap::new(),
}; };
mh.reload_database()?; mh.reload_database()?;
Ok(mh) Ok(mh)
@ -542,7 +542,7 @@ mod tests {
database.expect_load().times(1).returning(|| Ok(vec![])); database.expect_load().times(1).returning(|| Ok(vec![]));
database.expect_save().times(4).returning(|_| Ok(())); database.expect_save().times(4).returning(|_| Ok(()));
type MH = MusicHoard<NoLibrary, MockIDatabase>; type MH = MusicHoard<MockIDatabase, NoLibrary>;
let mut music_hoard: MH = MusicHoard::database(database).unwrap(); let mut music_hoard: MH = MusicHoard::database(database).unwrap();
let artist_1_id = ArtistId::new("the artist"); let artist_1_id = ArtistId::new("the artist");
@ -935,7 +935,7 @@ mod tests {
.times(1) .times(1)
.return_once(|_| Ok(())); .return_once(|_| Ok(()));
let mut music_hoard = MusicHoard::new(library, database).unwrap(); let mut music_hoard = MusicHoard::new(database, library).unwrap();
music_hoard.rescan_library().unwrap(); music_hoard.rescan_library().unwrap();
assert_eq!(music_hoard.get_collection(), &*LIBRARY_COLLECTION); assert_eq!(music_hoard.get_collection(), &*LIBRARY_COLLECTION);
@ -1083,7 +1083,7 @@ mod tests {
.times(1) .times(1)
.return_once(|| Ok(FULL_COLLECTION.to_owned())); .return_once(|| Ok(FULL_COLLECTION.to_owned()));
let music_hoard = MusicHoard::new(library, database).unwrap(); let music_hoard = MusicHoard::new(database, library).unwrap();
assert_eq!(music_hoard.get_collection(), &*FULL_COLLECTION); assert_eq!(music_hoard.get_collection(), &*FULL_COLLECTION);
} }

View File

@ -8,71 +8,77 @@ use crate::{
/// Builder for [`MusicHoard`]. Its purpose is to make it easier to set various combinations of /// Builder for [`MusicHoard`]. Its purpose is to make it easier to set various combinations of
/// library/database or their absence. /// library/database or their absence.
pub struct MusicHoardBuilder<LIB, DB> { pub struct MusicHoardBuilder<Database, Library> {
library: LIB, database: Database,
database: DB, library: Library,
} }
impl Default for MusicHoardBuilder<NoLibrary, NoDatabase> { impl Default for MusicHoardBuilder<NoDatabase, NoLibrary> {
/// Create a [`MusicHoardBuilder`]. /// Create a [`MusicHoardBuilder`].
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }
} }
impl MusicHoardBuilder<NoLibrary, NoDatabase> { impl MusicHoardBuilder<NoDatabase, NoLibrary> {
/// Create a [`MusicHoardBuilder`]. /// Create a [`MusicHoardBuilder`].
pub fn new() -> Self { pub fn new() -> Self {
MusicHoardBuilder { MusicHoardBuilder {
library: NoLibrary,
database: NoDatabase, database: NoDatabase,
library: NoLibrary,
} }
} }
} }
impl<LIB, DB> MusicHoardBuilder<LIB, DB> { impl<Database, Library> MusicHoardBuilder<Database, Library> {
/// Set a library for [`MusicHoard`]. /// Set a library for [`MusicHoard`].
pub fn set_library<NEWLIB: ILibrary>(self, library: NEWLIB) -> MusicHoardBuilder<NEWLIB, DB> { pub fn set_library<NewLibrary: ILibrary>(
self,
library: NewLibrary,
) -> MusicHoardBuilder<Database, NewLibrary> {
MusicHoardBuilder { MusicHoardBuilder {
library,
database: self.database, database: self.database,
library,
} }
} }
/// Set a database for [`MusicHoard`]. /// Set a database for [`MusicHoard`].
pub fn set_database<NEWDB: IDatabase>(self, database: NEWDB) -> MusicHoardBuilder<LIB, NEWDB> { pub fn set_database<NewDatabase: IDatabase>(
self,
database: NewDatabase,
) -> MusicHoardBuilder<NewDatabase, Library> {
MusicHoardBuilder { MusicHoardBuilder {
library: self.library,
database, database,
library: self.library,
} }
} }
} }
impl MusicHoardBuilder<NoLibrary, NoDatabase> { impl MusicHoardBuilder<NoDatabase, NoLibrary> {
/// Build [`MusicHoard`] with the currently set library and database. /// Build [`MusicHoard`] with the currently set library and database.
pub fn build(self) -> MusicHoard<NoLibrary, NoDatabase> { pub fn build(self) -> MusicHoard<NoDatabase, NoLibrary> {
MusicHoard::empty() MusicHoard::empty()
} }
} }
impl<LIB: ILibrary> MusicHoardBuilder<LIB, NoDatabase> { impl<Library: ILibrary> MusicHoardBuilder<NoDatabase, Library> {
/// Build [`MusicHoard`] with the currently set library and database. /// Build [`MusicHoard`] with the currently set library and database.
pub fn build(self) -> MusicHoard<LIB, NoDatabase> { pub fn build(self) -> MusicHoard<NoDatabase, Library> {
MusicHoard::library(self.library) MusicHoard::library(self.library)
} }
} }
impl<DB: IDatabase> MusicHoardBuilder<NoLibrary, DB> { impl<Database: IDatabase> MusicHoardBuilder<Database, NoLibrary> {
/// Build [`MusicHoard`] with the currently set library and database. /// Build [`MusicHoard`] with the currently set library and database.
pub fn build(self) -> Result<MusicHoard<NoLibrary, DB>, Error> { pub fn build(self) -> Result<MusicHoard<Database, NoLibrary>, Error> {
MusicHoard::database(self.database) MusicHoard::database(self.database)
} }
} }
impl<LIB: ILibrary, DB: IDatabase> MusicHoardBuilder<LIB, DB> { impl<Database: IDatabase, Library: ILibrary> MusicHoardBuilder<Database, Library> {
/// Build [`MusicHoard`] with the currently set library and database. /// Build [`MusicHoard`] with the currently set library and database.
pub fn build(self) -> Result<MusicHoard<LIB, DB>, Error> { pub fn build(self) -> Result<MusicHoard<Database, Library>, Error> {
MusicHoard::new(self.library, self.database) MusicHoard::new(self.database, self.library)
} }
} }

View File

@ -58,7 +58,7 @@ struct DbOpt {
no_database: bool, no_database: bool,
} }
fn with<LIB: ILibrary, DB: IDatabase>(builder: MusicHoardBuilder<LIB, DB>) { fn with<Database: IDatabase, Library: ILibrary>(builder: MusicHoardBuilder<Database, Library>) {
let music_hoard = builder.build().expect("failed to initialise MusicHoard"); let music_hoard = builder.build().expect("failed to initialise MusicHoard");
// Initialize the terminal user interface. // Initialize the terminal user interface.
@ -76,7 +76,10 @@ fn with<LIB: ILibrary, DB: IDatabase>(builder: MusicHoardBuilder<LIB, DB>) {
Tui::run(terminal, app, ui, handler, listener).expect("failed to run tui"); Tui::run(terminal, app, ui, handler, listener).expect("failed to run tui");
} }
fn with_database<LIB: ILibrary>(db_opt: DbOpt, builder: MusicHoardBuilder<LIB, NoDatabase>) { fn with_database<Library: ILibrary>(
db_opt: DbOpt,
builder: MusicHoardBuilder<NoDatabase, Library>,
) {
if db_opt.no_database { if db_opt.no_database {
with(builder.set_database(NullDatabase)); with(builder.set_database(NullDatabase));
} else { } else {
@ -103,7 +106,7 @@ fn with_database<LIB: ILibrary>(db_opt: DbOpt, builder: MusicHoardBuilder<LIB, N
}; };
} }
fn with_library(lib_opt: LibOpt, db_opt: DbOpt, builder: MusicHoardBuilder<NoLibrary, NoDatabase>) { fn with_library(lib_opt: LibOpt, db_opt: DbOpt, builder: MusicHoardBuilder<NoDatabase, NoLibrary>) {
if lib_opt.no_library { if lib_opt.no_library {
with_database(db_opt, builder.set_library(NullLibrary)); with_database(db_opt, builder.set_library(NullLibrary));
} else if let Some(uri) = lib_opt.beets_ssh_uri { } else if let Some(uri) = lib_opt.beets_ssh_uri {

View File

@ -14,9 +14,9 @@ pub trait IMusicHoard {
} }
// GRCOV_EXCL_START // GRCOV_EXCL_START
impl<LIB: ILibrary, DB: IDatabase> IMusicHoard for MusicHoard<LIB, DB> { impl<Database: IDatabase, Library: ILibrary> IMusicHoard for MusicHoard<Database, Library> {
fn rescan_library(&mut self) -> Result<(), musichoard::Error> { fn rescan_library(&mut self) -> Result<(), musichoard::Error> {
MusicHoard::<LIB, DB>::rescan_library(self) MusicHoard::<Database, Library>::rescan_library(self)
} }
fn reload_database(&mut self) -> Result<(), musichoard::Error> { fn reload_database(&mut self) -> Result<(), musichoard::Error> {

View File

@ -29,7 +29,7 @@ fn merge_library_then_database() {
let backend = JsonDatabaseFileBackend::new(&*database::json::DATABASE_TEST_FILE); let backend = JsonDatabaseFileBackend::new(&*database::json::DATABASE_TEST_FILE);
let database = JsonDatabase::new(backend); let database = JsonDatabase::new(backend);
let mut music_hoard = MusicHoard::new(library, database).unwrap(); let mut music_hoard = MusicHoard::new(database, library).unwrap();
music_hoard.rescan_library().unwrap(); music_hoard.rescan_library().unwrap();
music_hoard.reload_database().unwrap(); music_hoard.reload_database().unwrap();
@ -52,7 +52,7 @@ fn merge_database_then_library() {
let backend = JsonDatabaseFileBackend::new(&*database::json::DATABASE_TEST_FILE); let backend = JsonDatabaseFileBackend::new(&*database::json::DATABASE_TEST_FILE);
let database = JsonDatabase::new(backend); let database = JsonDatabase::new(backend);
let mut music_hoard = MusicHoard::new(library, database).unwrap(); let mut music_hoard = MusicHoard::new(database, library).unwrap();
music_hoard.reload_database().unwrap(); music_hoard.reload_database().unwrap();
music_hoard.rescan_library().unwrap(); music_hoard.rescan_library().unwrap();