musicus/backend/src/lib.rs

110 lines
3.4 KiB
Rust
Raw Normal View History

2022-01-23 14:35:33 +01:00
use musicus_database::Database;
use std::cell::RefCell;
2020-11-17 15:52:47 +01:00
use std::path::PathBuf;
use std::rc::Rc;
use tokio::sync::{broadcast, broadcast::Sender};
2020-11-14 22:32:21 +01:00
2021-02-05 10:56:42 +01:00
pub use musicus_database as db;
pub use musicus_import as import;
2021-02-03 23:03:47 +01:00
2021-02-04 16:31:37 +01:00
pub mod error;
pub use error::*;
2020-11-17 15:52:47 +01:00
pub mod library;
pub use library::*;
2021-04-26 00:33:25 +02:00
mod logger;
2021-02-03 23:24:41 +01:00
pub mod player;
pub use player::*;
2020-11-17 15:52:47 +01:00
/// General states the application can be in.
2022-01-23 14:35:33 +01:00
#[derive(Debug, Copy, Clone)]
2020-11-17 15:52:47 +01:00
pub enum BackendState {
/// The backend is not set up yet. This means that no backend methods except for setting the
/// music library path should be called. The user interface should adapt and only present this
/// option.
NoMusicLibrary,
/// The backend is loading the music library. No methods should be called. The user interface
/// should represent that state by prohibiting all interaction.
Loading,
/// The backend is ready and all methods may be called.
Ready,
}
/// A collection of all backend state and functionality.
pub struct Backend {
2022-01-23 14:35:33 +01:00
/// A closure that will be called whenever the backend state changes.
state_cb: RefCell<Option<Box<dyn Fn(BackendState)>>>,
2021-02-05 10:40:14 +01:00
/// Access to GSettings.
2020-11-17 15:52:47 +01:00
settings: gio::Settings,
2021-02-05 10:40:14 +01:00
/// The current path to the music library, which is used by the player and the database. This
/// is guaranteed to be Some, when the state is set to BackendState::Ready.
2020-11-17 15:52:47 +01:00
music_library_path: RefCell<Option<PathBuf>>,
2021-02-05 10:40:14 +01:00
2021-04-24 18:38:23 +02:00
/// The sender for sending library update notifications.
2021-04-25 12:26:43 +02:00
library_updated_sender: Sender<()>,
2021-04-24 18:38:23 +02:00
2021-02-05 10:40:14 +01:00
/// The database. This can be assumed to exist, when the state is set to BackendState::Ready.
2022-01-23 14:35:33 +01:00
database: RefCell<Option<Rc<Database>>>,
2021-02-05 10:40:14 +01:00
/// The player handling playlist and playback. This can be assumed to exist, when the state is
/// set to BackendState::Ready.
2020-11-17 15:52:47 +01:00
player: RefCell<Option<Rc<Player>>>,
}
impl Backend {
/// Create a new backend initerface. The user interface should subscribe to the state stream
2021-04-26 00:33:25 +02:00
/// and call init() afterwards. There may be only one backend for a process and this method
/// may only be called exactly once. Otherwise it will panic.
2020-11-17 15:52:47 +01:00
pub fn new() -> Self {
2021-04-26 00:33:25 +02:00
logger::register();
2021-04-25 12:26:43 +02:00
let (library_updated_sender, _) = broadcast::channel(1024);
2020-11-17 15:52:47 +01:00
Backend {
2022-01-23 14:35:33 +01:00
state_cb: RefCell::new(None),
2020-11-17 15:52:47 +01:00
settings: gio::Settings::new("de.johrpan.musicus"),
music_library_path: RefCell::new(None),
2021-04-25 12:26:43 +02:00
library_updated_sender,
2020-11-17 15:52:47 +01:00
database: RefCell::new(None),
2022-01-23 14:35:33 +01:00
player: RefCell::new(None),
2020-11-17 15:52:47 +01:00
}
}
2022-01-23 14:35:33 +01:00
/// Set the closure to be called whenever the backend state changes.
pub fn set_state_cb<F: Fn(BackendState) + 'static>(&self, cb: F) {
self.state_cb.replace(Some(Box::new(cb)));
2021-02-05 15:50:31 +01:00
}
2022-01-23 14:35:33 +01:00
/// Initialize the backend. A state callback should already have been registered using
/// [`set_state_cb()`] to react to the result.
pub fn init(&self) -> Result<()> {
self.init_library()?;
2020-11-17 15:52:47 +01:00
2022-01-23 14:35:33 +01:00
match self.get_music_library_path() {
None => self.set_state(BackendState::NoMusicLibrary),
Some(_) => self.set_state(BackendState::Ready),
};
2021-02-05 15:50:31 +01:00
2021-02-04 21:47:22 +01:00
Ok(())
}
2020-11-17 15:52:47 +01:00
/// Set the current state and notify the user interface.
fn set_state(&self, state: BackendState) {
2022-01-23 14:35:33 +01:00
if let Some(cb) = &*self.state_cb.borrow() {
cb(state);
}
}
}
impl Default for Backend {
fn default() -> Self {
Self::new()
2020-11-17 15:52:47 +01:00
}
}