Add collection manager

This commit is contained in:
Wojciech Kozlowski 2023-04-11 11:11:53 +02:00
parent 1c29c68321
commit 0ca084cacc
6 changed files with 113 additions and 24 deletions

54
src/collection/mod.rs Normal file
View File

@ -0,0 +1,54 @@
use crate::{database::{Database, self}, library::{Library, Query, self}, Artist};
/// The collection type.
pub type Collection = Vec<Artist>;
/// Error type for collection manager.
#[derive(Debug)]
pub enum Error {
/// The [`CollectionManager`] failed to read/write from/to the library.
LibraryError(String),
/// The [`CollectionManager`] failed to read/write from/to the database.
DatabaseError(String),
}
impl From<library::Error> for Error {
fn from(err: library::Error) -> Error {
Error::LibraryError(err.to_string())
}
}
impl From<database::Error> for Error {
fn from(err: database::Error) -> Error {
Error::DatabaseError(err.to_string())
}
}
pub struct CollectionManager {
library: Box<dyn Library + Send + Sync>,
database: Box<dyn Database + Send + Sync>,
collection: Collection,
}
impl CollectionManager {
pub fn new(
library: Box<dyn Library + Send + Sync>,
database: Box<dyn Database + Send + Sync>,
) -> Self {
CollectionManager {
library,
database,
collection: vec![],
}
}
pub fn rescan_library(&mut self) -> Result<(), Error> {
self.collection = self.library.list(&Query::default())?;
Ok(())
}
pub fn save_to_database(&mut self) -> Result<(), Error> {
self.database.write(&self.collection)?;
Ok(())
}
}

View File

@ -3,10 +3,15 @@
use std::fs; use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use serde::de::DeserializeOwned; use crate::collection::Collection;
use serde::Serialize;
use super::{Database, DatabaseRead, DatabaseWrite}; use super::{Database, DatabaseRead, DatabaseWrite, Error};
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Error {
Error::SerDeError(err.to_string())
}
}
/// Trait for the JSON database backend. /// Trait for the JSON database backend.
pub trait JsonDatabaseBackend { pub trait JsonDatabaseBackend {
@ -19,21 +24,18 @@ pub trait JsonDatabaseBackend {
/// JSON database. /// JSON database.
pub struct JsonDatabase { pub struct JsonDatabase {
backend: Box<dyn JsonDatabaseBackend + Send>, backend: Box<dyn JsonDatabaseBackend + Send + Sync>,
} }
impl JsonDatabase { impl JsonDatabase {
/// Create a new JSON database with the provided backend, e.g. [JsonDatabaseFileBackend]. /// Create a new JSON database with the provided backend, e.g. [JsonDatabaseFileBackend].
pub fn new(backend: Box<dyn JsonDatabaseBackend + Send>) -> Self { pub fn new(backend: Box<dyn JsonDatabaseBackend + Send + Sync>) -> Self {
JsonDatabase { backend } JsonDatabase { backend }
} }
} }
impl DatabaseRead for JsonDatabase { impl DatabaseRead for JsonDatabase {
fn read<D>(&self, collection: &mut D) -> Result<(), std::io::Error> fn read(&self, collection: &mut Collection) -> Result<(), Error> {
where
D: DeserializeOwned,
{
let serialized = self.backend.read()?; let serialized = self.backend.read()?;
*collection = serde_json::from_str(&serialized)?; *collection = serde_json::from_str(&serialized)?;
Ok(()) Ok(())
@ -41,10 +43,7 @@ impl DatabaseRead for JsonDatabase {
} }
impl DatabaseWrite for JsonDatabase { impl DatabaseWrite for JsonDatabase {
fn write<S>(&mut self, collection: &S) -> Result<(), std::io::Error> fn write(&mut self, collection: &Collection) -> Result<(), Error> {
where
S: Serialize,
{
let serialized = serde_json::to_string(&collection)?; let serialized = serde_json::to_string(&collection)?;
self.backend.write(&serialized)?; self.backend.write(&serialized)?;
Ok(()) Ok(())

View File

@ -1,24 +1,47 @@
//! Module for storing MusicHoard data in a database. //! Module for storing MusicHoard data in a database.
use serde::de::DeserializeOwned; use std::fmt;
use serde::Serialize;
use crate::collection::Collection;
pub mod json; pub mod json;
/// Error type for database calls.
#[derive(Debug)]
pub enum Error {
/// The database experienced an I/O error.
IoError(String),
/// The database experienced a (de)serialisation error.
SerDeError(String),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::IoError(ref s) => write!(f, "the database experienced an I/O error: {s}"),
Self::SerDeError(ref s) => {
write!(f, "the database experienced a (de)serialisation error: {s}")
}
}
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Error {
Error::IoError(err.to_string())
}
}
/// Trait for database reads. /// Trait for database reads.
pub trait DatabaseRead { pub trait DatabaseRead {
/// Read collection from the database. /// Read collection from the database.
fn read<D>(&self, collection: &mut D) -> Result<(), std::io::Error> fn read(&self, collection: &mut Collection) -> Result<(), Error>;
where
D: DeserializeOwned;
} }
/// Trait for database writes. /// Trait for database writes.
pub trait DatabaseWrite { pub trait DatabaseWrite {
/// Write collection to the database. /// Write collection to the database.
fn write<S>(&mut self, collection: &S) -> Result<(), std::io::Error> fn write(&mut self, collection: &Collection) -> Result<(), Error>;
where
S: Serialize;
} }
/// Trait for database reads and writes. /// Trait for database reads and writes.

View File

@ -3,6 +3,7 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uuid::Uuid; use uuid::Uuid;
pub mod collection;
pub mod database; pub mod database;
pub mod library; pub mod library;

View File

@ -91,7 +91,7 @@ pub trait BeetsLibraryExecutor {
/// Beets library. /// Beets library.
pub struct BeetsLibrary { pub struct BeetsLibrary {
executor: Box<dyn BeetsLibraryExecutor + Send>, executor: Box<dyn BeetsLibraryExecutor + Send + Sync>,
} }
trait LibraryPrivate { trait LibraryPrivate {
@ -107,7 +107,7 @@ trait LibraryPrivate {
impl BeetsLibrary { 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. [BeetsLibraryCommandExecutor].
pub fn new(executor: Box<dyn BeetsLibraryExecutor + Send>) -> BeetsLibrary { pub fn new(executor: Box<dyn BeetsLibraryExecutor + Send + Sync>) -> BeetsLibrary {
BeetsLibrary { executor } BeetsLibrary { executor }
} }
} }

View File

@ -1,6 +1,6 @@
//! Module for interacting with the music library. //! Module for interacting with the music library.
use std::{num::ParseIntError, str::Utf8Error}; use std::{num::ParseIntError, str::Utf8Error, fmt};
use crate::Artist; use crate::Artist;
@ -111,6 +111,18 @@ pub enum Error {
Utf8Error(String), Utf8Error(String),
} }
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::CmdExecError(ref s) => write!(f, "the library failed to execute a command: {s}"),
Self::InvalidData(ref s) => write!(f, "the library returned invalid data: {s}"),
Self::IoError(ref s) => write!(f, "the library experienced an I/O error: {s}"),
Self::ParseIntError(ref s) => write!(f, "the library returned an invalid integer: {s}"),
Self::Utf8Error(ref s) => write!(f, "the library returned invalid UTF-8: {s}"),
}
}
}
impl From<std::io::Error> for Error { impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Error { fn from(err: std::io::Error) -> Error {
Error::IoError(err.to_string()) Error::IoError(err.to_string())