monorepo/otg/core/src/api.rs

260 lines
8.3 KiB
Rust

/*
Copyright 2024, Savanni D'Gerinel <savanni@luminescent-dreams.com>
This file is part of On the Grid.
On the Grid is free software: you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
On the Grid is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along with On the Grid. If not, see <https://www.gnu.org/licenses/>.
*/
use crate::{
database::Database,
library, settings,
types::{Config, LibraryPath},
};
use async_std::channel::{Receiver, Sender};
use serde::{Deserialize, Serialize};
use std::sync::{Arc, RwLock, RwLockReadGuard};
pub trait Observable<T> {
fn subscribe(&self) -> Receiver<T>;
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum CoreRequest {
Library(library::LibraryRequest),
Settings(settings::SettingsRequest),
/*
ChangeSetting(ChangeSettingRequest),
CreateGame(CreateGameRequest),
Home,
OpenConfiguration,
PlayingField,
PlayStone(PlayStoneRequest),
StartGame,
*/
}
/*
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum ChangeSettingRequest {
LibraryPath(String),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PlayStoneRequest {
pub column: u8,
pub row: u8,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CreateGameRequest {
pub black_player: PlayerInfoRequest,
pub white_player: PlayerInfoRequest,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum PlayerInfoRequest {
Hotseat(HotseatPlayerRequest),
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct HotseatPlayerRequest {
pub name: String,
pub rank: Option<String>,
}
impl From<HotseatPlayerRequest> for Player {
fn from(p: HotseatPlayerRequest) -> Self {
Self {
name: p.name,
rank: p.rank.and_then(|r| Rank::try_from(r.as_ref()).ok()),
}
}
}
*/
#[derive(Clone, Debug)]
pub enum CoreResponse {
Library(library::LibraryResponse),
Settings(settings::SettingsResponse),
}
impl From<library::LibraryResponse> for CoreResponse {
fn from(r: library::LibraryResponse) -> Self {
Self::Library(r)
}
}
impl From<settings::SettingsResponse> for CoreResponse {
fn from(r: settings::SettingsResponse) -> Self {
Self::Settings(r)
}
}
#[derive(Clone, Debug)]
pub enum CoreNotification {
ConfigurationUpdated(Config),
BoardUpdated,
}
#[derive(Clone, Debug)]
pub struct Core {
config: Arc<RwLock<Config>>,
// state: Arc<RwLock<AppState>>,
library: Arc<RwLock<Option<Database>>>,
subscribers: Arc<RwLock<Vec<Sender<CoreNotification>>>>,
}
impl Core {
pub fn new(config: Config) -> Self {
println!("config: {:?}", config);
let library = match config.get::<LibraryPath>() {
Some(ref path) if path.to_path_buf().exists() => {
Some(Database::open_path(path.to_path_buf()).unwrap())
}
_ => None,
};
Self {
config: Arc::new(RwLock::new(config)),
// state,
library: Arc::new(RwLock::new(library)),
subscribers: Arc::new(RwLock::new(vec![])),
}
}
pub fn get_config(&self) -> Config {
self.config.read().unwrap().clone()
}
/// Change the configuration of the Core. This function will update any relevant core
/// functions, especially the contents of the library, and it will notify any subscribed objects
/// that the configuration has changed.
///
/// It will not handle persisting the new configuration, as the backing store for the
/// configuration is not a decision for the core library.
pub async fn set_config(&self, config: Config) {
*self.config.write().unwrap() = config.clone();
// let db = library::read_library(self.config.read().unwrap().get::<LibraryPath>()).await;
let library_path = self.config.read().unwrap().get::<LibraryPath>();
if let Some(ref path) = library_path {
self.load_library(path);
}
self.notify(CoreNotification::ConfigurationUpdated(config.clone()))
.await;
}
fn load_library(&self, path: &LibraryPath) {
let db = Database::open_path(path.to_path_buf()).unwrap();
*self.library.write().unwrap() = Some(db);
}
pub fn library(&self) -> RwLockReadGuard<'_, Option<Database>> {
self.library.read().unwrap()
}
pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse {
match request {
CoreRequest::Library(request) => library::handle(self, request).await.into(),
CoreRequest::Settings(request) => settings::handle(self, request).await.into(),
}
}
pub async fn notify(&self, notification: CoreNotification) {
let subscribers = self.subscribers.read().unwrap().clone();
for subscriber in subscribers {
let subscriber = subscriber.clone();
let _ = subscriber.send(notification.clone()).await;
}
}
/*
pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse {
match request {
CoreRequest::ChangeSetting(request) => match request {
ChangeSettingRequest::LibraryPath(path) => {
// let mut config = self.config.write().unwrap();
// config.set(ConfigOption::DatabasePath(DatabasePath(PathBuf::from(
// path,
// ))));
// CoreResponse::UpdatedConfigurationView(configuration(&config))
unimplemented!()
}
},
CoreRequest::CreateGame(create_request) => {
/*
let mut app_state = self.state.write().unwrap();
let white_player = {
match create_request.white_player {
PlayerInfoRequest::Hotseat(request) => Player::from(request),
}
};
let black_player = {
match create_request.black_player {
PlayerInfoRequest::Hotseat(request) => Player::from(request),
}
};
app_state.game = Some(GameState {
white_player,
black_player,
..GameState::default()
});
let game_state = app_state.game.as_ref().unwrap();
CoreResponse::PlayingFieldView(playing_field(game_state))
*/
unimplemented!()
}
CoreRequest::Home => {
// CoreResponse::HomeView(home(self.state.read().unwrap().database.all_games()))
unimplemented!()
}
CoreRequest::OpenConfiguration => {
// CoreResponse::ConfigurationView(configuration(&self.config.read().unwrap()))
unimplemented!()
}
CoreRequest::PlayingField => {
// let app_state = self.state.read().unwrap();
// let game = app_state.game.as_ref().unwrap();
// CoreResponse::PlayingFieldView(playing_field(game))
unimplemented!()
}
CoreRequest::PlayStone(request) => {
// let mut app_state = self.state.write().unwrap();
// app_state.place_stone(request);
// let game = app_state.game.as_ref().unwrap();
// CoreResponse::PlayingFieldView(playing_field(game))
unimplemented!()
}
CoreRequest::StartGame => {
unimplemented!()
}
}
}
*/
// pub async fn run(&self) {}
}
impl Observable<CoreNotification> for Core {
fn subscribe(&self) -> Receiver<CoreNotification> {
let mut subscribers = self.subscribers.write().unwrap();
let (sender, receiver) = async_std::channel::unbounded();
subscribers.push(sender);
receiver
}
}