Append I to interface traits and rename structs

This commit is contained in:
Wojciech Kozlowski 2023-05-10 22:21:31 +02:00
parent 712d3a65a7
commit b707da0c71
13 changed files with 87 additions and 87 deletions

View File

@ -6,7 +6,7 @@ use serde::Serialize;
#[cfg(test)]
use mockall::automock;
use super::{Database, Error};
use super::{Error, IDatabase};
pub mod backend;
@ -39,7 +39,7 @@ impl<JDB: JsonDatabaseBackend> JsonDatabase<JDB> {
}
}
impl<JDB: JsonDatabaseBackend> Database for JsonDatabase<JDB> {
impl<JDB: JsonDatabaseBackend> IDatabase for JsonDatabase<JDB> {
fn read<D: DeserializeOwned>(&self, collection: &mut D) -> Result<(), Error> {
let serialized = self.backend.read()?;
*collection = serde_json::from_str(&serialized)?;

View File

@ -10,6 +10,16 @@ use mockall::automock;
#[cfg(feature = "database-json")]
pub mod json;
/// Trait for interacting with the database.
#[cfg_attr(test, automock)]
pub trait IDatabase {
/// Read collection from the database.
fn read<D: DeserializeOwned + 'static>(&self, collection: &mut D) -> Result<(), Error>;
/// Write collection to the database.
fn write<S: Serialize + 'static>(&mut self, collection: &S) -> Result<(), Error>;
}
/// Error type for database calls.
#[derive(Debug)]
pub enum Error {
@ -36,16 +46,6 @@ impl From<std::io::Error> for Error {
}
}
/// Trait for interacting with the database.
#[cfg_attr(test, automock)]
pub trait Database {
/// Read collection from the database.
fn read<D: DeserializeOwned + 'static>(&self, collection: &mut D) -> Result<(), Error>;
/// Write collection to the database.
fn write<S: Serialize + 'static>(&mut self, collection: &S) -> Result<(), Error>;
}
#[cfg(test)]
mod tests {
use std::io;

View File

@ -5,8 +5,8 @@ pub mod library;
use std::fmt;
use database::Database;
use library::{Library, Query};
use database::IDatabase;
use library::{ILibrary, Query};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
@ -99,7 +99,7 @@ pub struct MusicHoard<LIB, DB> {
collection: Collection,
}
impl<LIB: Library, DB: Database> MusicHoard<LIB, DB> {
impl<LIB: ILibrary, DB: IDatabase> MusicHoard<LIB, DB> {
/// Create a new [`MusicHoard`] with the provided [`Library`] and [`Database`].
pub fn new(library: LIB, database: DB) -> Self {
MusicHoard {
@ -133,7 +133,7 @@ mod tests {
use mockall::predicate;
use once_cell::sync::Lazy;
use crate::{database::MockDatabase, library::MockLibrary};
use crate::{database::MockIDatabase, library::MockILibrary};
use super::*;
@ -141,8 +141,8 @@ mod tests {
#[test]
fn read_get_write() {
let mut library = MockLibrary::new();
let mut database = MockDatabase::new();
let mut library = MockILibrary::new();
let mut database = MockIDatabase::new();
let library_input = Query::new();
let library_result = Ok(COLLECTION.to_owned());
@ -171,8 +171,8 @@ mod tests {
#[test]
fn library_error() {
let mut library = MockLibrary::new();
let database = MockDatabase::new();
let mut library = MockILibrary::new();
let database = MockIDatabase::new();
let library_result = Err(library::Error::Invalid(String::from("invalid data")));
@ -193,8 +193,8 @@ mod tests {
#[test]
fn database_error() {
let library = MockLibrary::new();
let mut database = MockDatabase::new();
let library = MockILibrary::new();
let mut database = MockIDatabase::new();
let database_result = Err(database::Error::IoError(String::from("I/O error")));

View File

@ -11,7 +11,7 @@ use mockall::automock;
use crate::{Album, AlbumId, Artist, ArtistId, Track, TrackFormat};
use super::{Error, Field, Library, Query};
use super::{Error, Field, ILibrary, Query};
pub mod executor;
@ -101,7 +101,7 @@ impl<BLE: BeetsLibraryExecutor> BeetsLibrary<BLE> {
}
}
impl<BLE: BeetsLibraryExecutor> Library for BeetsLibrary<BLE> {
impl<BLE: BeetsLibraryExecutor> ILibrary for BeetsLibrary<BLE> {
fn list(&mut self, query: &Query) -> Result<Vec<Artist>, Error> {
let cmd = Self::list_cmd_and_args(query);
let output = self.executor.exec(&cmd)?;

View File

@ -10,6 +10,13 @@ use crate::Artist;
#[cfg(feature = "library-beets")]
pub mod beets;
/// Trait for interacting with the music library.
#[cfg_attr(test, automock)]
pub trait ILibrary {
/// List lirbary items that match the a specific query.
fn list(&mut self, query: &Query) -> Result<Vec<Artist>, Error>;
}
/// Individual fields that can be queried on.
#[derive(Debug, Hash, PartialEq, Eq)]
pub enum Field {
@ -103,13 +110,6 @@ impl From<Utf8Error> for Error {
}
}
/// Trait for interacting with the music library.
#[cfg_attr(test, automock)]
pub trait Library {
/// List lirbary items that match the a specific query.
fn list(&mut self, query: &Query) -> Result<Vec<Artist>, Error>;
}
#[cfg(test)]
mod tests {
use std::io;

View File

@ -7,21 +7,21 @@ use structopt::StructOpt;
use musichoard::{
database::{
json::{backend::JsonDatabaseFileBackend, JsonDatabase},
Database,
IDatabase,
},
library::{
beets::{
executor::{ssh::BeetsLibrarySshExecutor, BeetsLibraryProcessExecutor},
BeetsLibrary,
},
Library,
ILibrary,
},
MusicHoard,
};
mod tui;
use tui::ui::MhUi;
use tui::{event::EventChannel, handler::TuiEventHandler, listener::TuiEventListener, Tui};
use tui::ui::Ui;
use tui::{event::EventChannel, handler::TuiEventHandler, listener::EventListener, Tui};
#[derive(StructOpt)]
struct Opt {
@ -39,7 +39,7 @@ struct Opt {
database_file_path: PathBuf,
}
fn with<LIB: Library, DB: Database>(lib: LIB, db: DB) {
fn with<LIB: ILibrary, DB: IDatabase>(lib: LIB, db: DB) {
let music_hoard = MusicHoard::new(lib, db);
// Initialize the terminal user interface.
@ -47,10 +47,10 @@ fn with<LIB: Library, DB: Database>(lib: LIB, db: DB) {
let terminal = Terminal::new(backend).expect("failed to initialise terminal");
let channel = EventChannel::new();
let listener = TuiEventListener::new(channel.sender());
let listener = EventListener::new(channel.sender());
let handler = TuiEventHandler::new(channel.receiver());
let ui = MhUi::new(music_hoard).expect("failed to initialise ui");
let ui = Ui::new(music_hoard).expect("failed to initialise ui");
// Run the TUI application.
Tui::run(terminal, ui, handler, listener).expect("failed to run tui");

View File

@ -5,7 +5,7 @@ use mockall::automock;
use super::{
event::{Event, EventError, EventReceiver},
ui::Ui,
ui::IUi,
};
#[cfg_attr(test, automock)]
@ -28,7 +28,7 @@ impl TuiEventHandler {
}
}
impl<UI: Ui> EventHandler<UI> for TuiEventHandler {
impl<UI: IUi> EventHandler<UI> for TuiEventHandler {
fn handle_next_event(&self, ui: &mut UI) -> Result<(), EventError> {
match self.events.recv()? {
Event::Key(key_event) => Self::handle_key_event(ui, key_event),
@ -39,7 +39,7 @@ impl<UI: Ui> EventHandler<UI> for TuiEventHandler {
}
}
impl<UI: Ui> EventHandlerPrivate<UI> for TuiEventHandler {
impl<UI: IUi> EventHandlerPrivate<UI> for TuiEventHandler {
fn handle_key_event(ui: &mut UI, key_event: KeyEvent) {
match key_event.code {
// Exit application on `ESC` or `q`.

View File

@ -1,4 +1,4 @@
use musichoard::{MusicHoard, library::Library, database::Database, Collection};
use musichoard::{database::IDatabase, library::ILibrary, Collection, MusicHoard};
#[cfg(test)]
use mockall::automock;
@ -18,7 +18,7 @@ impl From<musichoard::Error> for Error {
}
// GRCOV_EXCL_START
impl<LIB: Library, DB: Database> IMusicHoard for MusicHoard<LIB, DB> {
impl<LIB: ILibrary, DB: IDatabase> IMusicHoard for MusicHoard<LIB, DB> {
fn rescan_library(&mut self) -> Result<(), Error> {
Ok(MusicHoard::rescan_library(self)?)
}

View File

@ -7,22 +7,22 @@ use mockall::automock;
use super::event::{Event, EventError, EventSender};
#[cfg_attr(test, automock)]
pub trait EventListener {
pub trait IEventListener {
fn spawn(self) -> thread::JoinHandle<EventError>;
}
pub struct TuiEventListener {
pub struct EventListener {
events: EventSender,
}
// GRCOV_EXCL_START
impl TuiEventListener {
impl EventListener {
pub fn new(events: EventSender) -> Self {
TuiEventListener { events }
EventListener { events }
}
}
impl EventListener for TuiEventListener {
impl IEventListener for EventListener {
fn spawn(self) -> thread::JoinHandle<EventError> {
thread::spawn(move || {
loop {

View File

@ -14,8 +14,8 @@ use std::marker::PhantomData;
use self::event::EventError;
use self::handler::EventHandler;
use self::listener::EventListener;
use self::ui::Ui;
use self::listener::IEventListener;
use self::ui::IUi;
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
@ -42,7 +42,7 @@ pub struct Tui<B: Backend, UI> {
_phantom: PhantomData<UI>,
}
impl<B: Backend, UI: Ui> Tui<B, UI> {
impl<B: Backend, UI: IUi> Tui<B, UI> {
fn init(&mut self) -> Result<(), Error> {
self.terminal.hide_cursor()?;
self.terminal.clear()?;
@ -72,7 +72,7 @@ impl<B: Backend, UI: Ui> Tui<B, UI> {
term: Terminal<B>,
ui: UI,
handler: impl EventHandler<UI>,
listener: impl EventListener,
listener: impl IEventListener,
) -> Result<(), Error> {
let mut tui = Tui {
terminal: term,
@ -131,7 +131,7 @@ impl<B: Backend, UI: Ui> Tui<B, UI> {
term: Terminal<B>,
ui: UI,
handler: impl EventHandler<UI>,
listener: impl EventListener,
listener: impl IEventListener,
) -> Result<(), Error> {
Self::enable()?;
let result = Self::main(term, ui, handler, listener);
@ -164,8 +164,8 @@ mod tests {
event::EventError,
handler::MockEventHandler,
lib::MockIMusicHoard,
listener::MockEventListener,
ui::{MhUi, Ui},
listener::MockIEventListener,
ui::{IUi, Ui},
Error, Tui,
};
@ -174,17 +174,17 @@ mod tests {
Terminal::new(backend).unwrap()
}
pub fn ui(collection: Collection) -> MhUi<MockIMusicHoard> {
pub fn ui(collection: Collection) -> Ui<MockIMusicHoard> {
let mut music_hoard = MockIMusicHoard::new();
music_hoard.expect_rescan_library().returning(|| Ok(()));
music_hoard.expect_get_collection().return_const(collection);
MhUi::new(music_hoard).unwrap()
Ui::new(music_hoard).unwrap()
}
fn listener() -> MockEventListener {
let mut listener = MockEventListener::new();
fn listener() -> MockIEventListener {
let mut listener = MockIEventListener::new();
listener.expect_spawn().return_once(|| {
thread::spawn(|| {
thread::park();
@ -194,11 +194,11 @@ mod tests {
listener
}
fn handler() -> MockEventHandler<MhUi<MockIMusicHoard>> {
fn handler() -> MockEventHandler<Ui<MockIMusicHoard>> {
let mut handler = MockEventHandler::new();
handler
.expect_handle_next_event()
.return_once(|ui: &mut MhUi<MockIMusicHoard>| {
.return_once(|ui: &mut Ui<MockIMusicHoard>| {
ui.quit();
Ok(())
});
@ -246,7 +246,7 @@ mod tests {
let listener_handle: thread::JoinHandle<EventError> = thread::spawn(|| error);
while !listener_handle.is_finished() {}
let mut listener = MockEventListener::new();
let mut listener = MockIEventListener::new();
listener.expect_spawn().return_once(|| listener_handle);
let mut handler = MockEventHandler::new();
@ -269,7 +269,7 @@ mod tests {
let listener_handle: thread::JoinHandle<EventError> = thread::spawn(|| panic!());
while !listener_handle.is_finished() {}
let mut listener = MockEventListener::new();
let mut listener = MockIEventListener::new();
listener.expect_spawn().return_once(|| listener_handle);
let mut handler = MockEventHandler::new();

View File

@ -9,6 +9,19 @@ use ratatui::{
use super::{lib::IMusicHoard, Error};
pub trait IUi {
fn is_running(&self) -> bool;
fn quit(&mut self);
fn increment_category(&mut self);
fn decrement_category(&mut self);
fn increment_selection(&mut self);
fn decrement_selection(&mut self);
fn render<B: Backend>(&mut self, frame: &mut Frame<'_, B>);
}
struct TrackSelection {
state: ListState,
}
@ -236,7 +249,7 @@ impl Selection {
}
}
pub struct MhUi<MH> {
pub struct Ui<MH> {
music_hoard: MH,
selection: Selection,
running: bool,
@ -425,11 +438,11 @@ impl<'a, 'b> TrackState<'a, 'b> {
}
}
impl<MH: IMusicHoard> MhUi<MH> {
impl<MH: IMusicHoard> Ui<MH> {
pub fn new(mut music_hoard: MH) -> Result<Self, Error> {
music_hoard.rescan_library()?;
let selection = Selection::new(Some(music_hoard.get_collection()));
Ok(MhUi {
Ok(Ui {
music_hoard,
selection,
running: true,
@ -509,20 +522,7 @@ impl<MH: IMusicHoard> MhUi<MH> {
}
}
pub trait Ui {
fn is_running(&self) -> bool;
fn quit(&mut self);
fn increment_category(&mut self);
fn decrement_category(&mut self);
fn increment_selection(&mut self);
fn decrement_selection(&mut self);
fn render<B: Backend>(&mut self, frame: &mut Frame<'_, B>);
}
impl<MH: IMusicHoard> Ui for MhUi<MH> {
impl<MH: IMusicHoard> IUi for Ui<MH> {
fn is_running(&self) -> bool {
self.running
}
@ -767,7 +767,7 @@ mod tests {
.expect_get_collection()
.return_const(COLLECTION.to_owned());
let mut ui = MhUi::new(music_hoard).unwrap();
let mut ui = Ui::new(music_hoard).unwrap();
assert!(ui.is_running());
ui.quit();
@ -786,7 +786,7 @@ mod tests {
.expect_get_collection()
.return_const(COLLECTION.to_owned());
let mut ui = MhUi::new(music_hoard).unwrap();
let mut ui = Ui::new(music_hoard).unwrap();
assert!(ui.is_running());
assert_eq!(ui.selection.active, Category::Artist);
@ -885,7 +885,7 @@ mod tests {
.return_once(|| Ok(()));
music_hoard.expect_get_collection().return_const(collection);
let mut app = MhUi::new(music_hoard).unwrap();
let mut app = Ui::new(music_hoard).unwrap();
assert!(app.is_running());
assert_eq!(app.selection.active, Category::Artist);
@ -921,7 +921,7 @@ mod tests {
.return_once(|| Ok(()));
music_hoard.expect_get_collection().return_const(collection);
let mut app = MhUi::new(music_hoard).unwrap();
let mut app = Ui::new(music_hoard).unwrap();
assert!(app.is_running());
assert_eq!(app.selection.active, Category::Artist);
@ -969,7 +969,7 @@ mod tests {
.return_once(|| Ok(()));
music_hoard.expect_get_collection().return_const(collection);
let mut app = MhUi::new(music_hoard).unwrap();
let mut app = Ui::new(music_hoard).unwrap();
assert!(app.is_running());
assert_eq!(app.selection.active, Category::Artist);

View File

@ -3,7 +3,7 @@ use std::{fs, path::PathBuf};
use musichoard::{
database::{
json::{backend::JsonDatabaseFileBackend, JsonDatabase},
Database,
IDatabase,
},
Artist,
};

View File

@ -9,7 +9,7 @@ use once_cell::sync::Lazy;
use musichoard::{
library::{
beets::{executor::BeetsLibraryProcessExecutor, BeetsLibrary},
Field, Library, Query,
Field, ILibrary, Query,
},
Artist,
};