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::path::{Path, PathBuf};
use serde::de::DeserializeOwned;
use serde::Serialize;
use crate::collection::Collection;
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.
pub trait JsonDatabaseBackend {
@ -19,21 +24,18 @@ pub trait JsonDatabaseBackend {
/// JSON database.
pub struct JsonDatabase {
backend: Box<dyn JsonDatabaseBackend + Send>,
backend: Box<dyn JsonDatabaseBackend + Send + Sync>,
}
impl JsonDatabase {
/// 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 }
}
}
impl DatabaseRead for JsonDatabase {
fn read<D>(&self, collection: &mut D) -> Result<(), std::io::Error>
where
D: DeserializeOwned,
{
fn read(&self, collection: &mut Collection) -> Result<(), Error> {
let serialized = self.backend.read()?;
*collection = serde_json::from_str(&serialized)?;
Ok(())
@ -41,10 +43,7 @@ impl DatabaseRead for JsonDatabase {
}
impl DatabaseWrite for JsonDatabase {
fn write<S>(&mut self, collection: &S) -> Result<(), std::io::Error>
where
S: Serialize,
{
fn write(&mut self, collection: &Collection) -> Result<(), Error> {
let serialized = serde_json::to_string(&collection)?;
self.backend.write(&serialized)?;
Ok(())

View File

@ -1,24 +1,47 @@
//! Module for storing MusicHoard data in a database.
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::fmt;
use crate::collection::Collection;
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.
pub trait DatabaseRead {
/// Read collection from the database.
fn read<D>(&self, collection: &mut D) -> Result<(), std::io::Error>
where
D: DeserializeOwned;
fn read(&self, collection: &mut Collection) -> Result<(), Error>;
}
/// Trait for database writes.
pub trait DatabaseWrite {
/// Write collection to the database.
fn write<S>(&mut self, collection: &S) -> Result<(), std::io::Error>
where
S: Serialize;
fn write(&mut self, collection: &Collection) -> Result<(), Error>;
}
/// Trait for database reads and writes.

View File

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

View File

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

View File

@ -1,6 +1,6 @@
//! Module for interacting with the music library.
use std::{num::ParseIntError, str::Utf8Error};
use std::{num::ParseIntError, str::Utf8Error, fmt};
use crate::Artist;
@ -111,6 +111,18 @@ pub enum Error {
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 {
fn from(err: std::io::Error) -> Error {
Error::IoError(err.to_string())