From 000b0ad9b876a65dce8bdccf0950fce3a56ba10b Mon Sep 17 00:00:00 2001 From: Thomas Castiglione Date: Mon, 18 Mar 2024 12:07:25 +0800 Subject: [PATCH] refactor mod worker, strengthening the abstraction boundary --- src-tauri/src/callbacks.rs | 2 +- src-tauri/src/main.rs | 6 +- src-tauri/src/{ => worker}/gui_util.rs | 43 +--- src-tauri/src/worker/mod.rs | 328 ++++--------------------- src-tauri/src/worker/mutations.rs | 16 +- src-tauri/src/worker/queries.rs | 12 +- src-tauri/src/worker/session.rs | 297 ++++++++++++++++++++++ src-tauri/src/{ => worker}/tests.rs | 12 +- 8 files changed, 362 insertions(+), 354 deletions(-) rename src-tauri/src/{ => worker}/gui_util.rs (93%) create mode 100644 src-tauri/src/worker/session.rs rename src-tauri/src/{ => worker}/tests.rs (95%) diff --git a/src-tauri/src/callbacks.rs b/src-tauri/src/callbacks.rs index 2a2a1d2..b3ffb97 100644 --- a/src-tauri/src/callbacks.rs +++ b/src-tauri/src/callbacks.rs @@ -11,7 +11,7 @@ use anyhow::Result; use jj_lib::{git::RemoteCallbacks, repo::MutableRepo}; use tauri::{Manager, WebviewWindow}; -use crate::{gui_util::WorkerCallbacks, messages::InputRequest, AppState}; +use crate::{messages::InputRequest, worker::WorkerCallbacks, AppState}; pub struct FrontendCallbacks(pub WebviewWindow); diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index a835280..8914c13 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -2,12 +2,9 @@ mod callbacks; mod config; -mod gui_util; mod handler; mod menu; mod messages; -#[cfg(all(test, not(feature = "ts-rs")))] -mod tests; mod worker; use std::collections::HashMap; @@ -23,14 +20,13 @@ use tauri::{ipc::InvokeError, Manager}; use tauri::{State, WebviewWindow, Window, WindowEvent, Wry}; use tauri_plugin_window_state::StateFlags; -use gui_util::WorkerSession; use messages::{ AbandonRevisions, CheckoutRevision, CopyChanges, CreateRevision, DescribeRevision, DuplicateRevisions, FetchRemote, InputResponse, InsertRevision, MoveBranch, MoveChanges, MoveRevision, MoveSource, MutationResult, PushRemote, RevId, TrackBranch, UndoOperation, UntrackBranch, }; -use worker::{Mutation, Session, SessionEvent}; +use worker::{Mutation, Session, SessionEvent, WorkerSession}; use crate::callbacks::FrontendCallbacks; diff --git a/src-tauri/src/gui_util.rs b/src-tauri/src/worker/gui_util.rs similarity index 93% rename from src-tauri/src/gui_util.rs rename to src-tauri/src/worker/gui_util.rs index 28e9386..bce3bd3 100644 --- a/src-tauri/src/gui_util.rs +++ b/src-tauri/src/worker/gui_util.rs @@ -12,7 +12,7 @@ use jj_cli::{ config::LayeredConfigs, git_util::is_colocated_git_workspace, }; -use jj_lib::{backend::BackendError, default_index::{AsCompositeIndex, DefaultReadonlyIndex}, file_util::relative_path, git::RemoteCallbacks, gitignore::GitIgnoreFile, op_store::WorkspaceId, repo::{MutableRepo, RepoLoaderError}, repo_path::RepoPath, revset::{RevsetEvaluationError, RevsetIteratorExt, RevsetResolutionError}, rewrite, view::View, working_copy::{CheckoutStats, SnapshotOptions}}; +use jj_lib::{backend::BackendError, default_index::{AsCompositeIndex, DefaultReadonlyIndex}, file_util::relative_path, gitignore::GitIgnoreFile, op_store::WorkspaceId, repo::RepoLoaderError, repo_path::RepoPath, revset::{RevsetEvaluationError, RevsetIteratorExt, RevsetResolutionError}, rewrite, view::View, working_copy::{CheckoutStats, SnapshotOptions}}; use jj_lib::{ backend::{ChangeId, CommitId}, commit::Commit, @@ -35,44 +35,7 @@ use jj_lib::{ use thiserror::Error; use crate::{config::GGSettings, messages::{self, RevId}}; - -pub trait WorkerCallbacks { - fn with_git(&self, repo: &mut MutableRepo, f: &dyn Fn(&mut MutableRepo, RemoteCallbacks<'_>) -> Result<()>) -> Result<()>; -} - -struct NoCallbacks; - -impl WorkerCallbacks for NoCallbacks { - fn with_git(&self, repo: &mut MutableRepo, f: &dyn Fn(&mut MutableRepo, RemoteCallbacks<'_>) -> Result<()>) -> Result<()> { - f(repo, RemoteCallbacks::default()) - } -} - -/// state that doesn't depend on jj-lib borrowings -pub struct WorkerSession { - pub force_log_page_size: Option, - pub latest_query: Option, - pub callbacks: Box -} - -impl WorkerSession { - pub fn new(callbacks: T) -> Self { - WorkerSession { - callbacks: Box::new(callbacks), - ..Default::default() - } - } -} - -impl Default for WorkerSession { - fn default() -> Self { - WorkerSession { - force_log_page_size: None, - latest_query: None, - callbacks: Box::new(NoCallbacks) - } - } -} +use super::WorkerSession; /// jj-dependent state, available when a workspace is open pub struct WorkspaceSession<'a> { @@ -120,7 +83,7 @@ impl WorkerSession { let defaults = Config::builder() .add_source(jj_cli::config::default_config()) - .add_source(config::File::from_str(include_str!("config/gg.toml"), config::FileFormat::Toml)) + .add_source(config::File::from_str(include_str!("../config/gg.toml"), config::FileFormat::Toml)) .build()?; let mut configs = LayeredConfigs::from_environment(defaults); diff --git a/src-tauri/src/worker/mod.rs b/src-tauri/src/worker/mod.rs index 174bb60..9dc5c00 100644 --- a/src-tauri/src/worker/mod.rs +++ b/src-tauri/src/worker/mod.rs @@ -1,31 +1,21 @@ //! Worker per window, owning repo data (jj-lib is not thread-safe) //! The worker thread is a state machine, running different handle functions based on loaded data -use std::{ - fmt::Debug, - panic::{catch_unwind, AssertUnwindSafe}, - path::PathBuf, - sync::mpsc::{Receiver, Sender}, -}; +mod gui_util; +mod mutations; +mod queries; +mod session; +#[cfg(all(test, not(feature = "ts-rs")))] +mod tests; -use anyhow::{anyhow, Context, Result}; +use std::fmt::Debug; -use crate::messages::{self, RevId}; -use crate::{ - gui_util::{WorkerSession, WorkspaceSession}, - messages::LogPage, -}; +use anyhow::Result; +use jj_lib::{git::RemoteCallbacks, repo::MutableRepo}; -use self::queries::LogQueryState; - -pub mod mutations; -pub mod queries; - -/// implemented by states of the event loop -pub trait Session { - type Transition; - fn handle_events(self, rx: &Receiver) -> Result; -} +use crate::messages; +use gui_util::WorkspaceSession; +pub use session::{Session, SessionEvent}; /// implemented by structured-change commands pub trait Mutation: Debug { @@ -43,279 +33,49 @@ pub trait Mutation: Debug { } } -/// messages sent to a worker from other threads. most come with a channel allowing a response -#[derive(Debug)] -pub enum SessionEvent { - #[allow(dead_code)] - EndSession, - OpenWorkspace { - tx: Sender>, - wd: Option, - }, - QueryLog { - tx: Sender>, - query: String, - }, - QueryLogNextPage { - tx: Sender>, - }, - QueryRevision { - tx: Sender>, - id: RevId, - }, - ExecuteSnapshot { - tx: Sender>, - }, - ExecuteMutation { - tx: Sender, - mutation: Box, - }, -} - -/// transitions for a workspace session -pub enum WorkspaceResult { - Reopen(Sender>, Option), // workspace -> workspace - SessionComplete, // workspace -> worker +/// implemented by UI layers to request user input and receive progress +pub trait WorkerCallbacks { + fn with_git( + &self, + repo: &mut MutableRepo, + f: &dyn Fn(&mut MutableRepo, RemoteCallbacks<'_>) -> Result<()>, + ) -> Result<()>; } -/// transition for a query session -pub struct QueryResult(SessionEvent, LogQueryState); // query -> workspace - -/// event loop state for a workspace session -#[derive(Default)] -struct WorkspaceState { - pub unhandled_event: Option, - pub unpaged_query: Option, -} - -impl Session for WorkerSession { - type Transition = (); - - fn handle_events(mut self, rx: &Receiver) -> Result<()> { - let mut latest_wd: Option = None; +struct NoCallbacks; - loop { - let evt = rx.recv(); - log::debug!("WorkerSession handling {evt:?}"); - match evt { - Ok(SessionEvent::EndSession) => return Ok(()), - Ok(SessionEvent::ExecuteSnapshot { .. }) => (), - Ok(SessionEvent::OpenWorkspace { mut tx, mut wd }) => loop { - let resolved_wd = match wd.clone().or(latest_wd) { - Some(wd) => wd, - None => match std::env::current_dir().context("current_dir") { - Ok(wd) => wd, - Err(err) => { - latest_wd = None; - tx.send(Ok(messages::RepoConfig::LoadError { - absolute_path: PathBuf::new().into(), - message: format!("{err:#}"), - }))?; - break; - } - }, - }; - - let mut ws = match self.load_directory(&resolved_wd) { - Ok(ws) => ws, - Err(err) => { - latest_wd = None; - tx.send(Ok(messages::RepoConfig::LoadError { - absolute_path: resolved_wd.into(), - message: format!("{err:#}"), - }))?; - break; - } - }; - - latest_wd = Some(resolved_wd); - - ws.import_and_snapshot(false)?; - - tx.send(ws.format_config())?; - - match ws.handle_events(rx).context("WorkspaceSession")? { - WorkspaceResult::Reopen(new_tx, new_cwd) => (tx, wd) = (new_tx, new_cwd), - WorkspaceResult::SessionComplete => return Ok(()), - } - }, - Ok(evt) => { - log::error!( - "WorkerSession::handle_events(): repo not loaded when receiving {evt:?}" - ); - return Err(anyhow::anyhow!( - "A repo must be loaded before any other operations" - )); - } - Err(err) => { - log::error!("WorkerSession::handle_events(): {err}"); - return Err(anyhow!(err)); - } - }; - } +impl WorkerCallbacks for NoCallbacks { + fn with_git( + &self, + repo: &mut MutableRepo, + f: &dyn Fn(&mut MutableRepo, RemoteCallbacks<'_>) -> Result<()>, + ) -> Result<()> { + f(repo, RemoteCallbacks::default()) } } -impl Session for WorkspaceSession<'_> { - type Transition = WorkspaceResult; - - fn handle_events(mut self, rx: &Receiver) -> Result { - let mut state = WorkspaceState::default(); - - loop { - let next_event = if state.unhandled_event.is_some() { - state.unhandled_event.take().unwrap() - } else { - let evt = rx.recv(); - log::debug!("WorkspaceSession handling {evt:?}"); - evt? - }; - - match next_event { - SessionEvent::EndSession => return Ok(WorkspaceResult::SessionComplete), - SessionEvent::OpenWorkspace { tx, wd: cwd } => { - return Ok(WorkspaceResult::Reopen(tx, cwd)); - } - SessionEvent::QueryRevision { tx, id } => { - tx.send(queries::query_revision(&self, id))? - } - SessionEvent::QueryLog { - tx, - query: revset_string, - } => { - let log_page_size = self.session.force_log_page_size.unwrap_or(1000); - handle_query( - &mut state, - &self, - tx, - rx, - Some(&revset_string), - Some(LogQueryState::new(log_page_size)), - )?; - - self.session.latest_query = Some(revset_string); - } - SessionEvent::QueryLogNextPage { tx } => { - let revset_string = self.session.latest_query.as_ref().map(|x| x.as_str()); - - handle_query(&mut state, &self, tx, rx, revset_string, None)?; - } - SessionEvent::ExecuteSnapshot { tx } => { - if self.import_and_snapshot(false).is_ok_and(|updated| updated) { - tx.send(Some(self.format_status()))?; - } else { - tx.send(None)?; - } - } - SessionEvent::ExecuteMutation { tx, mutation } => { - let name = mutation.as_ref().describe(); - match catch_unwind(AssertUnwindSafe(|| { - mutation.execute(&mut self).with_context(|| name.clone()) - })) { - Ok(result) => { - tx.send(match result { - Ok(result) => result, - Err(err) => { - log::error!("{err:?}"); - messages::MutationResult::InternalError { - message: (&*format!("{err:?}")).into(), - } - } - })?; - } - Err(panic) => { - let mut message = match panic.downcast::<&str>() { - Ok(v) => *v, - _ => "panic!()", - } - .to_owned(); - message.insert_str(0, ": "); - message.insert_str(0, &name); - log::error!("{message}"); - tx.send(messages::MutationResult::InternalError { - message: (&*message).into(), - })?; - } - } - } - }; - } - } +/// state that doesn't depend on jj-lib borrowings +pub struct WorkerSession { + pub force_log_page_size: Option, + pub latest_query: Option, + pub callbacks: Box, } -impl Session for queries::QuerySession<'_, '_> { - type Transition = QueryResult; - - fn handle_events(mut self, rx: &Receiver) -> Result { - loop { - let evt = rx.recv(); - log::debug!("LogQuery handling {evt:?}"); - match evt { - Ok(SessionEvent::QueryRevision { tx, id }) => { - tx.send(queries::query_revision(&self.ws, id))? - } - Ok(SessionEvent::QueryLogNextPage { tx }) => tx.send(self.get_page())?, - Ok(unhandled) => return Ok(QueryResult(unhandled, self.state)), - Err(err) => return Err(anyhow!(err)), - }; +impl WorkerSession { + pub fn new(callbacks: T) -> Self { + WorkerSession { + callbacks: Box::new(callbacks), + ..Default::default() } } } -/// helper function for transitioning from workspace state to query state -fn handle_query( - state: &mut WorkspaceState, - ws: &WorkspaceSession, - tx: Sender>, - rx: &Receiver, - revset_str: Option<&str>, - query_state: Option, -) -> Result<()> { - let query_state = match query_state.or_else(|| state.unpaged_query.take()) { - Some(x) => x, - None => { - tx.send(Err(anyhow!( - "page requested without query in progress or new query" - )))?; - - state.unhandled_event = None; - state.unpaged_query = None; - return Ok(()); - } - }; - - let revset_str = match revset_str { - Some(x) => x, - None => { - tx.send(Err(anyhow!("page requested without query in progress")))?; - - state.unhandled_event = None; - state.unpaged_query = None; - return Ok(()); +impl Default for WorkerSession { + fn default() -> Self { + WorkerSession { + force_log_page_size: None, + latest_query: None, + callbacks: Box::new(NoCallbacks), } - }; - - let revset = match ws - .evaluate_revset_str(revset_str) - .context("evaluate revset") - { - Ok(x) => x, - Err(err) => { - tx.send(Err(err))?; - - state.unhandled_event = None; - state.unpaged_query = None; - return Ok(()); - } - }; - - let mut query = queries::QuerySession::new(ws, &*revset, query_state); - let page = query.get_page(); - tx.send(page)?; - - let QueryResult(next_event, next_query) = query.handle_events(rx).context("LogQuery")?; - - state.unhandled_event = Some(next_event); - state.unpaged_query = Some(next_query); - Ok(()) + } } diff --git a/src-tauri/src/worker/mutations.rs b/src-tauri/src/worker/mutations.rs index 9068da6..252a4ad 100644 --- a/src-tauri/src/worker/mutations.rs +++ b/src-tauri/src/worker/mutations.rs @@ -20,18 +20,14 @@ use jj_lib::{ str_util::StringPattern, }; -use crate::{ - gui_util::WorkspaceSession, - messages::{ - AbandonRevisions, CheckoutRevision, CopyChanges, CreateRevision, DescribeRevision, - DuplicateRevisions, FetchRemote, InsertRevision, MoveBranch, MoveChanges, MoveRevision, - MoveSource, MutationResult, PushRemote, RefName, TrackBranch, TreePath, UndoOperation, - UntrackBranch, - }, +use super::{gui_util::WorkspaceSession, Mutation}; +use crate::messages::{ + AbandonRevisions, CheckoutRevision, CopyChanges, CreateRevision, DescribeRevision, + DuplicateRevisions, FetchRemote, InsertRevision, MoveBranch, MoveChanges, MoveRevision, + MoveSource, MutationResult, PushRemote, RefName, TrackBranch, TreePath, UndoOperation, + UntrackBranch, }; -use super::Mutation; - macro_rules! precondition { ($($args:tt)*) => { return Ok(MutationResult::PreconditionError { message: format!($($args)*) }) diff --git a/src-tauri/src/worker/queries.rs b/src-tauri/src/worker/queries.rs index 5071436..a2bf130 100644 --- a/src-tauri/src/worker/queries.rs +++ b/src-tauri/src/worker/queries.rs @@ -29,7 +29,7 @@ struct LogStem { } /// state used for init or restart of a query -pub struct LogQueryState { +pub struct QueryState { /// max number of rows per page page_size: usize, /// number of rows already yielded @@ -38,9 +38,9 @@ pub struct LogQueryState { stems: Vec>, } -impl LogQueryState { - pub fn new(page_size: usize) -> LogQueryState { - LogQueryState { +impl QueryState { + pub fn new(page_size: usize) -> QueryState { + QueryState { page_size, next_row: 0, stems: Vec::new(), @@ -58,14 +58,14 @@ pub struct QuerySession<'a, 'b: 'a> { >, >, >, - pub state: LogQueryState, + pub state: QueryState, } impl<'a, 'b> QuerySession<'a, 'b> { pub fn new( ws: &'a WorkspaceSession<'b>, revset: &'a dyn Revset, - state: LogQueryState, + state: QueryState, ) -> QuerySession<'a, 'b> { let iter = TopoGroupedRevsetGraphIterator::new(revset.iter_graph()) .skip(state.next_row) diff --git a/src-tauri/src/worker/session.rs b/src-tauri/src/worker/session.rs new file mode 100644 index 0000000..b1bbac5 --- /dev/null +++ b/src-tauri/src/worker/session.rs @@ -0,0 +1,297 @@ +use std::{ + panic::{catch_unwind, AssertUnwindSafe}, + path::PathBuf, + sync::mpsc::{Receiver, Sender}, +}; + +use anyhow::{anyhow, Context, Result}; + +use super::{ + gui_util::WorkspaceSession, + queries::{self, QueryState}, + Mutation, WorkerSession, +}; +use crate::messages; + +/// implemented by states of the event loop +pub trait Session { + type Transition; + fn handle_events(self, rx: &Receiver) -> Result; +} + +/// messages sent to a worker from other threads. most come with a channel allowing a response +#[derive(Debug)] +pub enum SessionEvent { + #[allow(dead_code)] + EndSession, + OpenWorkspace { + tx: Sender>, + wd: Option, + }, + QueryLog { + tx: Sender>, + query: String, + }, + QueryLogNextPage { + tx: Sender>, + }, + QueryRevision { + tx: Sender>, + id: messages::RevId, + }, + ExecuteSnapshot { + tx: Sender>, + }, + ExecuteMutation { + tx: Sender, + mutation: Box, + }, +} + +/// transitions for a workspace session +pub enum WorkspaceResult { + Reopen(Sender>, Option), // workspace -> workspace + SessionComplete, // workspace -> worker +} + +/// transition for a query session +pub struct QueryResult(SessionEvent, QueryState); // query -> workspace + +/// event loop state for a workspace session +#[derive(Default)] +struct WorkspaceState { + pub unhandled_event: Option, + pub unpaged_query: Option, +} + +impl Session for WorkerSession { + type Transition = (); + + fn handle_events(mut self, rx: &Receiver) -> Result<()> { + let mut latest_wd: Option = None; + + loop { + let evt = rx.recv(); + log::debug!("WorkerSession handling {evt:?}"); + match evt { + Ok(SessionEvent::EndSession) => return Ok(()), + Ok(SessionEvent::ExecuteSnapshot { .. }) => (), + Ok(SessionEvent::OpenWorkspace { mut tx, mut wd }) => loop { + let resolved_wd = match wd.clone().or(latest_wd) { + Some(wd) => wd, + None => match std::env::current_dir().context("current_dir") { + Ok(wd) => wd, + Err(err) => { + latest_wd = None; + tx.send(Ok(messages::RepoConfig::LoadError { + absolute_path: PathBuf::new().into(), + message: format!("{err:#}"), + }))?; + break; + } + }, + }; + + let mut ws = match self.load_directory(&resolved_wd) { + Ok(ws) => ws, + Err(err) => { + latest_wd = None; + tx.send(Ok(messages::RepoConfig::LoadError { + absolute_path: resolved_wd.into(), + message: format!("{err:#}"), + }))?; + break; + } + }; + + latest_wd = Some(resolved_wd); + + ws.import_and_snapshot(false)?; + + tx.send(ws.format_config())?; + + match ws.handle_events(rx).context("WorkspaceSession")? { + WorkspaceResult::Reopen(new_tx, new_cwd) => (tx, wd) = (new_tx, new_cwd), + WorkspaceResult::SessionComplete => return Ok(()), + } + }, + Ok(evt) => { + log::error!( + "WorkerSession::handle_events(): repo not loaded when receiving {evt:?}" + ); + return Err(anyhow::anyhow!( + "A repo must be loaded before any other operations" + )); + } + Err(err) => { + log::error!("WorkerSession::handle_events(): {err}"); + return Err(anyhow!(err)); + } + }; + } + } +} + +impl Session for WorkspaceSession<'_> { + type Transition = WorkspaceResult; + + fn handle_events(mut self, rx: &Receiver) -> Result { + let mut state = WorkspaceState::default(); + + loop { + let next_event = if state.unhandled_event.is_some() { + state.unhandled_event.take().unwrap() + } else { + let evt = rx.recv(); + log::debug!("WorkspaceSession handling {evt:?}"); + evt? + }; + + match next_event { + SessionEvent::EndSession => return Ok(WorkspaceResult::SessionComplete), + SessionEvent::OpenWorkspace { tx, wd: cwd } => { + return Ok(WorkspaceResult::Reopen(tx, cwd)); + } + SessionEvent::QueryRevision { tx, id } => { + tx.send(queries::query_revision(&self, id))? + } + SessionEvent::QueryLog { + tx, + query: revset_string, + } => { + let log_page_size = self.session.force_log_page_size.unwrap_or(1000); + handle_query( + &mut state, + &self, + tx, + rx, + Some(&revset_string), + Some(QueryState::new(log_page_size)), + )?; + + self.session.latest_query = Some(revset_string); + } + SessionEvent::QueryLogNextPage { tx } => { + let revset_string = self.session.latest_query.as_ref().map(|x| x.as_str()); + + handle_query(&mut state, &self, tx, rx, revset_string, None)?; + } + SessionEvent::ExecuteSnapshot { tx } => { + if self.import_and_snapshot(false).is_ok_and(|updated| updated) { + tx.send(Some(self.format_status()))?; + } else { + tx.send(None)?; + } + } + SessionEvent::ExecuteMutation { tx, mutation } => { + let name = mutation.as_ref().describe(); + match catch_unwind(AssertUnwindSafe(|| { + mutation.execute(&mut self).with_context(|| name.clone()) + })) { + Ok(result) => { + tx.send(match result { + Ok(result) => result, + Err(err) => { + log::error!("{err:?}"); + messages::MutationResult::InternalError { + message: (&*format!("{err:?}")).into(), + } + } + })?; + } + Err(panic) => { + let mut message = match panic.downcast::<&str>() { + Ok(v) => *v, + _ => "panic!()", + } + .to_owned(); + message.insert_str(0, ": "); + message.insert_str(0, &name); + log::error!("{message}"); + tx.send(messages::MutationResult::InternalError { + message: (&*message).into(), + })?; + } + } + } + }; + } + } +} + +impl Session for queries::QuerySession<'_, '_> { + type Transition = QueryResult; + + fn handle_events(mut self, rx: &Receiver) -> Result { + loop { + let evt = rx.recv(); + log::debug!("LogQuery handling {evt:?}"); + match evt { + Ok(SessionEvent::QueryRevision { tx, id }) => { + tx.send(queries::query_revision(&self.ws, id))? + } + Ok(SessionEvent::QueryLogNextPage { tx }) => tx.send(self.get_page())?, + Ok(unhandled) => return Ok(QueryResult(unhandled, self.state)), + Err(err) => return Err(anyhow!(err)), + }; + } + } +} + +/// helper function for transitioning from workspace state to query state +fn handle_query( + state: &mut WorkspaceState, + ws: &WorkspaceSession, + tx: Sender>, + rx: &Receiver, + revset_str: Option<&str>, + query_state: Option, +) -> Result<()> { + let query_state = match query_state.or_else(|| state.unpaged_query.take()) { + Some(x) => x, + None => { + tx.send(Err(anyhow!( + "page requested without query in progress or new query" + )))?; + + state.unhandled_event = None; + state.unpaged_query = None; + return Ok(()); + } + }; + + let revset_str = match revset_str { + Some(x) => x, + None => { + tx.send(Err(anyhow!("page requested without query in progress")))?; + + state.unhandled_event = None; + state.unpaged_query = None; + return Ok(()); + } + }; + + let revset = match ws + .evaluate_revset_str(revset_str) + .context("evaluate revset") + { + Ok(x) => x, + Err(err) => { + tx.send(Err(err))?; + + state.unhandled_event = None; + state.unpaged_query = None; + return Ok(()); + } + }; + + let mut query = queries::QuerySession::new(ws, &*revset, query_state); + let page = query.get_page(); + tx.send(page)?; + + let QueryResult(next_event, next_query) = query.handle_events(rx).context("LogQuery")?; + + state.unhandled_event = Some(next_event); + state.unpaged_query = Some(next_query); + Ok(()) +} diff --git a/src-tauri/src/tests.rs b/src-tauri/src/worker/tests.rs similarity index 95% rename from src-tauri/src/tests.rs rename to src-tauri/src/worker/tests.rs index b5a7503..ae319b0 100644 --- a/src-tauri/src/tests.rs +++ b/src-tauri/src/worker/tests.rs @@ -60,13 +60,11 @@ mod session { use anyhow::Result; use crate::{ - gui_util::WorkerSession, messages::{LogPage, RepoConfig, RevResult}, - tests::{mkid, revs}, - worker::{Session, SessionEvent}, + worker::{Session, SessionEvent, WorkerSession}, }; - use super::mkrepo; + use super::{mkid, mkrepo, revs}; #[test] fn start_and_stop() -> Result<()> { @@ -400,16 +398,14 @@ mod mutation { use jj_lib::{backend::TreeValue, repo_path::RepoPath}; use crate::{ - gui_util::WorkerSession, messages::{ CheckoutRevision, CreateRevision, DescribeRevision, MoveChanges, MutationResult, RevResult, TreePath, }, - tests::revs, - worker::{queries, Mutation}, + worker::{queries, Mutation, WorkerSession}, }; - use super::mkrepo; + use super::{mkrepo, revs}; #[test] fn wc_path_is_visible() -> Result<()> {