diff --git a/ps-core/src/database/database_like.rs b/ps-core/src/database/database_like.rs index ede09be..101b41e 100644 --- a/ps-core/src/database/database_like.rs +++ b/ps-core/src/database/database_like.rs @@ -8,7 +8,11 @@ use crate::Config; use super::{db_message::DBMessage, query::Query, query_result::QueryResult}; -pub trait DatabaseLike: Clone + Send + 'static { +pub trait DatabaseQuery: Send + 'static { + fn query(&self, query: &Query) -> Result>; +} + +pub trait DatabaseLike: DatabaseQuery + Clone { fn new(path: impl AsRef) -> Result where Self: Sized; @@ -16,7 +20,6 @@ pub trait DatabaseLike: Clone + Send + 'static { where Self: Sized; fn total_mails(&self) -> Result; - fn query(&self, query: &Query) -> Result>; fn import(self) -> (Sender, JoinHandle>); fn save_config(&self, config: Config) -> Result<()>; } diff --git a/ps-core/src/lib.rs b/ps-core/src/lib.rs index 73e2f81..3d1b082 100644 --- a/ps-core/src/lib.rs +++ b/ps-core/src/lib.rs @@ -4,7 +4,7 @@ pub mod message_adapter; pub mod model; mod types; -pub use database::database_like::DatabaseLike; +pub use database::database_like::{DatabaseLike, DatabaseQuery}; pub use database::db_message::DBMessage; pub use database::query::{Field, Filter, OtherQuery, Query, ValueField, AMOUNT_FIELD_NAME}; pub use database::query_result::{QueryResult, QueryRow}; diff --git a/ps-core/src/model/engine.rs b/ps-core/src/model/engine.rs index c1bc41f..31559ed 100644 --- a/ps-core/src/model/engine.rs +++ b/ps-core/src/model/engine.rs @@ -5,6 +5,7 @@ //! - [`segmentations::`] //! - [`items::`] use eyre::{bail, Result}; + use lru::LruCache; use crate::database::query::{Field, Filter, OtherQuery, Query, ValueField}; @@ -49,7 +50,12 @@ pub struct Engine { impl Engine { pub fn new(config: &Config) -> Result { + #[cfg(not(target_arch = "wasm32"))] let link = super::link::run::<_, Database>(config)?; + + #[cfg(target_arch = "wasm32")] + let link = super::link::run::<_, Database>(config, Database::new(&config.database_path)?)?; + let engine = Engine { link, search_stack: Vec::new(), diff --git a/ps-core/src/model/link.rs b/ps-core/src/model/link.rs index 0883f61..c926ed5 100644 --- a/ps-core/src/model/link.rs +++ b/ps-core/src/model/link.rs @@ -11,12 +11,13 @@ use std::sync::{ }; use std::{collections::HashSet, convert::TryInto}; +#[allow(unused)] use crossbeam_channel::{unbounded, Receiver, Sender}; use eyre::Result; use serde_json::Value; use crate::database::{ - database_like::DatabaseLike, + database_like::DatabaseQuery, query::Query, query_result::{QueryResult, QueryRow}, }; @@ -35,8 +36,14 @@ pub enum Response { pub(super) type InputSender = Sender<(Query, Context)>; pub(super) type OutputReciever = Receiver>>; +// FIXME: Instead of this wasm mess, two different link types? pub(super) struct Link { + database: Box, + + #[cfg(not(target_arch = "wasm32"))] pub input_sender: InputSender, + + #[cfg(not(target_arch = "wasm32"))] pub output_receiver: OutputReciever, // We need to account for the brief moment where the processing channel is empty // but we're applying the results. If there is a UI update in this window, @@ -46,8 +53,47 @@ pub(super) struct Link { // put into the output channel. In order to account for all of this, we employ a // request counter to know how many requests are currently in the pipeline request_counter: Arc, + + #[cfg(target_arch = "wasm32")] + response: Vec>, +} + +#[cfg(target_arch = "wasm32")] +impl Link { + pub fn request(&mut self, query: &Query, context: Context) -> Result<()> { + let result = self.database.query(&query)?; + if let Some(response) = process_query(query.clone(), result, context).ok() { + self.response.insert(0, response); + } + Ok(()) + } + + pub fn receive(&mut self) -> Result>> { + Ok(self.response.pop()) + } + + pub fn is_processing(&self) -> bool { + self.request_counter.load(Ordering::Relaxed) > 0 + } + + pub fn request_counter(&self) -> Arc { + self.request_counter.clone() + } +} + +#[cfg(target_arch = "wasm32")] +pub(super) fn run( + config: &Config, + database: Database, +) -> Result> { + Ok(Link { + database: Box::new(database), + request_counter: Arc::new(AtomicUsize::new(0)), + response: Vec::new(), + }) } +#[cfg(not(target_arch = "wasm32"))] impl Link { pub fn request(&mut self, query: &Query, context: Context) -> Result<()> { self.request_counter.fetch_add(1, Ordering::Relaxed); @@ -81,7 +127,8 @@ impl Link { } } -pub(super) fn run( +#[cfg(not(target_arch = "wasm32"))] +pub(super) fn run( config: &Config, ) -> Result> { // Create a new database connection, just for reading @@ -96,7 +143,8 @@ pub(super) fn run( +#[cfg(not(target_arch = "wasm32"))] +fn inner_loop( database: Database, input_receiver: Receiver<(Query, Context)>, output_sender: Sender>>, @@ -104,39 +152,54 @@ fn inner_loop( loop { let (query, context) = input_receiver.recv()?; let result = database.query(&query)?; - let response = match query { - Query::Grouped { .. } => { - let segmentations = calculate_segmentations(&result)?; - Response::Grouped(query, context, segmentations) - } - Query::Normal { .. } => { - let converted = calculate_rows(&result)?; - Response::Normal(query, context, converted) - } - Query::Other { .. } => { - let mut results = HashSet::new(); - for entry in result { - match entry { - QueryResult::Other(field) => match field.value() { - Value::Array(s) => { - for n in s { - if let Value::String(s) = n { - if !results.contains(s) { - results.insert(s.to_owned()); - } + let response = process_query(query, result, context); + output_sender.send(response)?; + } +} + +fn process_query( + query: Query, + result: Vec, + context: Context, +) -> Result> { + let response = match query { + Query::Grouped { .. } => { + let segmentations = calculate_segmentations(&result)?; + Response::Grouped(query, context, segmentations) + } + Query::Normal { .. } => { + let converted = calculate_rows(&result)?; + Response::Normal(query, context, converted) + } + Query::Other { .. } => { + let mut results = HashSet::new(); + for entry in result { + match entry { + QueryResult::Other(field) => match field.value() { + Value::Array(s) => { + for n in s { + if let Value::String(s) = n { + if !results.contains(s) { + results.insert(s.to_owned()); } } } - _ => panic!("Should not end up here"), - }, - _ => panic!("Should not end up here"), + } + _ => { + #[cfg(debug_assertions)] + panic!("Should not end up here") + } + }, + _ => { + #[cfg(debug_assertions)] + panic!("Should not end up here") } } - Response::Other(query, context, results.into_iter().collect()) } - }; - output_sender.send(Ok(response))?; - } + Response::Other(query, context, results.into_iter().collect()) + } + }; + Ok(response) } fn calculate_segmentations(result: &[QueryResult]) -> Result {