/* Copyright 2023, Savanni D'Gerinel This file is part of FitnessTrax. FitnessTrax 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. FitnessTrax 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 FitnessTrax. If not, see . */ use emseries::Series; use ft_core::TraxRecord; use std::{ path::{Path, PathBuf}, sync::{Arc, RwLock}, }; /// Invocations are how parts of the application, primarily the UI, will send requests to the core. #[derive(Debug)] pub enum AppInvocation { /// Tell the core to try to open a database. OpenDatabase(PathBuf), /// Request a set of records from the core. // Note: this will require a time range, but doesn't yet. RequestRecords, } /// Responses are messages that the core sends to the UI. Though they are called responses, the /// could actually be pre-emptively sent, such as notifications. The UI will need to be able to /// process those any time they arrive. /// /// A typical use would be for the UI to send an [AppInvocation::RequestRecords] request and /// receive [AppResponse::Records]. #[derive(Debug)] pub enum AppResponse { /// No database is available. The UI should typically display a placeholder, such as the /// welcome view. NoDatabase, /// The database is open and here is a set of records. Typically, the set of records will be /// all of the records within a time frame, but this can actually be any set of records. Records, /// The database has been changed. This message is useful for telling the UI that a significant /// change has happened. Further, the UI needs to save PathBuf to settings, because the /// gio::Settings system can't be run in the fully async background. DatabaseChanged(PathBuf), } /// The real, headless application. This is where all of the logic will reside. #[derive(Clone)] pub struct App { database: Arc>>>, } impl App { pub fn new(db_path: Option) -> Self { let database = db_path.map(|path| Series::open(path).unwrap()); let s = Self { database: Arc::new(RwLock::new(database)), }; s } pub async fn process_invocation(&self, invocation: AppInvocation) -> AppResponse { match invocation { AppInvocation::OpenDatabase(db_path) => { self.open_db(&db_path); AppResponse::DatabaseChanged(db_path) } AppInvocation::RequestRecords => { if self.database.read().unwrap().is_none() { AppResponse::NoDatabase } else { AppResponse::Records } } } } fn open_db(&self, path: &Path) { let db = Series::open(path).unwrap(); *self.database.write().unwrap() = Some(db); } }