diff --git a/Cargo.lock b/Cargo.lock index 2eb9451f5..d3e1d9df0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2164,16 +2164,17 @@ name = "gitbutler-watcher" version = "0.0.0" dependencies = [ "anyhow", - "async-trait", "backoff", "crossbeam-channel", "futures", + "git2", + "gitbutler-analytics", "gitbutler-core", "gitbutler-testsupport", "itertools 0.12.1", "notify", "notify-debouncer-full", - "tauri", + "once_cell", "tempfile", "thiserror", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 8301ddbc5..bfc35afd5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ tokio = { version = "1.37.0" } gitbutler-git = { path = "crates/gitbutler-git" } gitbutler-core = { path = "crates/gitbutler-core" } +gitbutler-analytics = { path = "crates/gitbutler-analytics" } gitbutler-testsupport = { path = "crates/gitbutler-testsupport" } [profile.release] diff --git a/crates/gitbutler-watcher/Cargo.toml b/crates/gitbutler-watcher/Cargo.toml index 84b9aa397..fddb9ef17 100644 --- a/crates/gitbutler-watcher/Cargo.toml +++ b/crates/gitbutler-watcher/Cargo.toml @@ -8,6 +8,7 @@ publish = false doctest = false [dependencies] +gitbutler-analytics.workspace = true gitbutler-core.workspace = true thiserror.workspace = true anyhow = "1.0.81" @@ -15,7 +16,6 @@ futures = "0.3.30" tokio = { workspace = true, features = [ "full", "sync" ] } tokio-util = "0.7.10" tracing = "0.1.40" -async-trait = "0.1.79" backoff = "0.4.0" notify = { version = "6.0.1" } @@ -23,19 +23,11 @@ notify-debouncer-full = "0.3.1" crossbeam-channel = "0.5.12" itertools = "0.12" -# TODO(ST): remove this dependency, abstract it -[dependencies.tauri] -version = "1.6.1" -features = [ - "http-all", "os-all", "dialog-open", "fs-read-file", - "path-all", "process-relaunch", "protocol-asset", - "shell-open", "window-maximize", "window-start-dragging", - "window-unmaximize" -] - [dev-dependencies] -tempfile = "3.10" gitbutler-testsupport.workspace = true +git2.workspace = true +tempfile = "3.10" +once_cell = "1.19" [lints.clippy] all = "deny" diff --git a/crates/gitbutler-watcher/src/events.rs b/crates/gitbutler-watcher/src/events.rs index dcd32ea24..f582df598 100644 --- a/crates/gitbutler-watcher/src/events.rs +++ b/crates/gitbutler-watcher/src/events.rs @@ -1,12 +1,13 @@ use std::fmt::Display; use std::path::PathBuf; +use gitbutler_core::{deltas, reader, sessions::SessionId, virtual_branches}; use gitbutler_core::{projects::ProjectId, sessions}; -/// An event for internal use, as merge between [super::file_monitor::Event] and [Event]. +/// An event for internal use, as merge between [super::file_monitor::Event] and [Action]. #[derive(Debug)] pub(super) enum InternalEvent { - // From public API + // From public action API Flush(ProjectId, sessions::Session), CalculateVirtualBranches(ProjectId), FetchGitbutlerData(ProjectId), @@ -21,31 +22,33 @@ pub(super) enum InternalEvent { // TODO(ST): This should not have to be implemented in the Watcher, figure out how this can be moved // to application logic at least. However, it's called through a trait in `core`. #[derive(Debug, PartialEq, Clone)] -pub enum Event { +#[allow(missing_docs)] +pub enum Action { Flush(ProjectId, sessions::Session), CalculateVirtualBranches(ProjectId), FetchGitbutlerData(ProjectId), PushGitbutlerData(ProjectId), } -impl Event { +impl Action { + /// Return the action's associated project id. pub fn project_id(&self) -> ProjectId { match self { - Event::FetchGitbutlerData(project_id) - | Event::Flush(project_id, _) - | Event::CalculateVirtualBranches(project_id) - | Event::PushGitbutlerData(project_id) => *project_id, + Action::FetchGitbutlerData(project_id) + | Action::Flush(project_id, _) + | Action::CalculateVirtualBranches(project_id) + | Action::PushGitbutlerData(project_id) => *project_id, } } } -impl From for InternalEvent { - fn from(value: Event) -> Self { +impl From for InternalEvent { + fn from(value: Action) -> Self { match value { - Event::Flush(a, b) => InternalEvent::Flush(a, b), - Event::CalculateVirtualBranches(v) => InternalEvent::CalculateVirtualBranches(v), - Event::FetchGitbutlerData(v) => InternalEvent::FetchGitbutlerData(v), - Event::PushGitbutlerData(v) => InternalEvent::PushGitbutlerData(v), + Action::Flush(a, b) => InternalEvent::Flush(a, b), + Action::CalculateVirtualBranches(v) => InternalEvent::CalculateVirtualBranches(v), + Action::FetchGitbutlerData(v) => InternalEvent::FetchGitbutlerData(v), + Action::PushGitbutlerData(v) => InternalEvent::PushGitbutlerData(v), } } } @@ -96,3 +99,38 @@ fn comma_separated_paths(paths: &[PathBuf]) -> String { listing } } + +/// An event telling the receiver something about the state of the application which just changed. +#[derive(Debug, Clone)] +#[allow(missing_docs)] +pub enum Change { + GitIndex(ProjectId), + GitFetch(ProjectId), + GitHead { + project_id: ProjectId, + head: String, + }, + GitActivity(ProjectId), + File { + project_id: ProjectId, + session_id: SessionId, + // TODO(ST): Should probably not be a string, but rather, relative. + file_path: String, + contents: Option, + }, + Session { + project_id: ProjectId, + session: sessions::Session, + }, + Deltas { + project_id: ProjectId, + session_id: SessionId, + // TODO(ST): check if this is ever more than one + deltas: Vec, + relative_file_path: PathBuf, + }, + VirtualBranches { + project_id: ProjectId, + virtual_branches: virtual_branches::VirtualBranches, + }, +} diff --git a/crates/gitbutler-watcher/src/handler.rs b/crates/gitbutler-watcher/src/handler.rs index 790b72621..edbd69b04 100644 --- a/crates/gitbutler-watcher/src/handler.rs +++ b/crates/gitbutler-watcher/src/handler.rs @@ -14,12 +14,12 @@ use gitbutler_core::{ assets, deltas, gb_repository, git, project_repository, projects, reader, sessions, users, virtual_branches, }; -use tauri::{AppHandle, Manager}; use tracing::instrument; -use super::events; -use crate::{analytics, events as app_events}; +use super::{events, Change}; +/// A type that contains enough state to make decisions based on changes in the filesystem, which themselves +/// may trigger [Changes](Change) // NOTE: This is `Clone` as each incoming event is spawned onto a thread for processing. #[derive(Clone)] pub struct Handler { @@ -29,7 +29,7 @@ pub struct Handler { // the tauri app, assuming that such application would not be `Send + Sync` everywhere and thus would // need extra protection. users: users::Controller, - analytics: analytics::Client, + analytics: gitbutler_analytics::Client, local_data_dir: path::PathBuf, projects: projects::Controller, vbranch_controller: virtual_branches::Controller, @@ -39,42 +39,7 @@ pub struct Handler { /// A function to send events - decoupled from app-handle for testing purposes. #[allow(clippy::type_complexity)] - send_event: Arc Result<()> + Send + Sync + 'static>, -} - -impl Handler { - pub fn from_app(app: &AppHandle) -> Result { - let app_data_dir = app - .path_resolver() - .app_data_dir() - .context("failed to get app data dir")?; - let analytics = app - .try_state::() - .map_or(analytics::Client::default(), |client| { - client.inner().clone() - }); - let users = app.state::().inner().clone(); - let projects = app.state::().inner().clone(); - let vbranches = app.state::().inner().clone(); - let assets_proxy = app.state::().inner().clone(); - let sessions_db = app.state::().inner().clone(); - let deltas_db = app.state::().inner().clone(); - - Ok(Handler::new( - app_data_dir.clone(), - analytics, - users, - projects, - vbranches, - assets_proxy, - sessions_db, - deltas_db, - { - let app = app.clone(); - move |event: &crate::events::Event| event.send(&app) - }, - )) - } + send_event: Arc Result<()> + Send + Sync + 'static>, } impl Handler { @@ -82,14 +47,14 @@ impl Handler { #[allow(clippy::too_many_arguments)] pub fn new( local_data_dir: PathBuf, - analytics: analytics::Client, + analytics: gitbutler_analytics::Client, users: users::Controller, projects: projects::Controller, vbranch_controller: virtual_branches::Controller, assets_proxy: assets::Proxy, sessions_db: sessions::Database, deltas_db: deltas::Database, - send_event: impl Fn(&app_events::Event) -> Result<()> + Send + Sync + 'static, + send_event: impl Fn(Change) -> Result<()> + Send + Sync + 'static, ) -> Self { Handler { local_data_dir, @@ -144,7 +109,7 @@ impl Handler { } impl Handler { - fn emit_app_event(&self, event: &crate::events::Event) -> Result<()> { + fn emit_app_event(&self, event: Change) -> Result<()> { (self.send_event)(event).context("failed to send event") } @@ -153,16 +118,16 @@ impl Handler { project_id: ProjectId, session_id: SessionId, file_path: &Path, - contents: Option<&reader::Content>, + contents: Option, ) -> Result<()> { - self.emit_app_event(&app_events::Event::file( + self.emit_app_event(Change::File { project_id, session_id, - &file_path.display().to_string(), + file_path: file_path.display().to_string(), contents, - )) + }) } - fn send_analytics_event_none_blocking(&self, event: &analytics::Event) -> Result<()> { + fn send_analytics_event_none_blocking(&self, event: &gitbutler_analytics::Event) -> Result<()> { if let Some(user) = self.users.get_user().context("failed to get user")? { self.analytics .send_non_anonymous_event_nonblocking(&user, event); @@ -193,7 +158,7 @@ impl Handler { .flush_session(&project_repository, session, user.as_ref()) .context(format!("failed to flush session {}", session.id))?; - self.index_session(project_id, &session)?; + self.index_session(project_id, session)?; let push_gb_data = tokio::task::spawn_blocking({ let this = self.clone(); @@ -213,13 +178,13 @@ impl Handler { { Ok((branches, skipped_files)) => { let branches = self.assets_proxy.proxy_virtual_branches(branches).await; - self.emit_app_event(&app_events::Event::virtual_branches( + self.emit_app_event(Change::VirtualBranches { project_id, - &VirtualBranches { + virtual_branches: VirtualBranches { branches, skipped_files, }, - )) + }) } Err(err) if err.is::() => Ok(()), Err(err) => Err(err.context("failed to list virtual branches").into()), @@ -314,7 +279,7 @@ impl Handler { let sessions_after_fetch = gb_repo.get_sessions_iterator()?.filter_map(Result::ok); let new_sessions = sessions_after_fetch.filter(|s| !sessions_before_fetch.contains(s)); for session in new_sessions { - self.index_session(project_id, &session)?; + self.index_session(project_id, session)?; } Ok(()) } @@ -358,11 +323,11 @@ impl Handler { }; match file_name { "FETCH_HEAD" => { - self.emit_app_event(&app_events::Event::git_fetch(project_id))?; + self.emit_app_event(Change::GitFetch(project_id))?; self.calculate_virtual_branches(project_id).await?; } "logs/HEAD" => { - self.emit_app_event(&app_events::Event::git_activity(project.id))?; + self.emit_app_event(Change::GitActivity(project.id))?; } "GB_FLUSH" => { let user = self.users.get_user()?; @@ -404,18 +369,20 @@ impl Handler { integration_reference.delete()?; } if let Some(head) = head_ref.name() { - self.send_analytics_event_none_blocking(&analytics::Event::HeadChange { + self.send_analytics_event_none_blocking( + &gitbutler_analytics::Event::HeadChange { + project_id, + reference_name: head_ref_name.to_string(), + }, + )?; + self.emit_app_event(Change::GitHead { project_id, - reference_name: head_ref_name.to_string(), + head: head.to_string(), })?; - self.emit_app_event(&app_events::Event::git_head( - project_id, - &head.to_string(), - ))?; } } "index" => { - self.emit_app_event(&app_events::Event::git_index(project.id))?; + self.emit_app_event(Change::GitIndex(project.id))?; } _ => {} } diff --git a/crates/gitbutler-watcher/src/handler/calculate_deltas.rs b/crates/gitbutler-watcher/src/handler/calculate_deltas.rs index a231a921c..cc9e38cd3 100644 --- a/crates/gitbutler-watcher/src/handler/calculate_deltas.rs +++ b/crates/gitbutler-watcher/src/handler/calculate_deltas.rs @@ -1,3 +1,4 @@ +use crate::Change; use anyhow::{Context, Result}; use gitbutler_core::{ deltas, gb_repository, project_repository, projects::ProjectId, reader, sessions, @@ -43,7 +44,7 @@ impl super::Handler { .context("failed to get or create current session")?; let session = current_session.clone(); - let process = move |path: &Path| -> Result { + let process = move |path: PathBuf| -> Result { let _span = tracing::span!(tracing::Level::TRACE, "processing", ?path).entered(); let current_session_reader = sessions::Reader::open(&gb_repository, ¤t_session) @@ -51,18 +52,18 @@ impl super::Handler { let deltas_reader = deltas::Reader::new(¤t_session_reader); let writer = deltas::Writer::new(&gb_repository).context("failed to open deltas writer")?; - let current_wd_file_content = match Self::file_content(&project_repository, path) { + let current_wd_file_content = match Self::file_content(&project_repository, &path) { Ok(content) => Some(content), Err(reader::Error::NotFound) => None, Err(err) => Err(err).context("failed to get file content")?, }; - let latest_file_content = match current_session_reader.file(path) { + let latest_file_content = match current_session_reader.file(&path) { Ok(content) => Some(content), Err(reader::Error::NotFound) => None, Err(err) => Err(err).context("failed to get file content")?, }; let current_deltas = deltas_reader - .read_file(path) + .read_file(&path) .context("failed to get file deltas")?; let mut text_doc = deltas::Document::new( latest_file_content.as_ref(), @@ -78,30 +79,30 @@ impl super::Handler { let deltas = text_doc.get_deltas(); writer - .write(path, &deltas) + .write(&path, &deltas) .context("failed to write deltas")?; match ¤t_wd_file_content { - Some(reader::Content::UTF8(text)) => writer.write_wd_file(path, text), - Some(_) => writer.write_wd_file(path, ""), - None => writer.remove_wd_file(path), + Some(reader::Content::UTF8(text)) => writer.write_wd_file(&path, text), + Some(_) => writer.write_wd_file(&path, ""), + None => writer.remove_wd_file(&path), }?; let session_id = current_session.id; - self.emit_session_file(project_id, session_id, path, latest_file_content.as_ref())?; + self.emit_session_file(project_id, session_id, &path, latest_file_content)?; self.index_deltas( project_id, session_id, - path, + &path, std::slice::from_ref(&new_delta), ) .context("failed to index deltas")?; - self.emit_app_event(&app_events::Event::deltas( + self.emit_app_event(Change::Deltas { project_id, session_id, - std::slice::from_ref(&new_delta), - path, - ))?; + deltas: vec![new_delta], + relative_file_path: path, + })?; Ok(true) }; Ok((process, session)) @@ -116,7 +117,7 @@ impl super::Handler { let current_session = if num_threads < 2 { let (process, session) = make_processor()?; for path in paths { - if !process(path.as_path())? { + if !process(path)? { num_no_delta += 1; } } @@ -135,7 +136,7 @@ impl super::Handler { let mut num_no_delta = 0; let (process, _) = make_processor()?; for path in rx { - if !process(path.as_path())? { + if !process(path)? { num_no_delta += 1; } } @@ -158,7 +159,7 @@ impl super::Handler { let (_, session) = make_processor()?; session }; - self.index_session(project_id, ¤t_session)?; + self.index_session(project_id, current_session)?; Ok(num_no_delta) })?; tracing::debug!(%project_id, paths_without_deltas = num_no_delta, paths_with_delta = num_paths - num_no_delta); diff --git a/crates/gitbutler-watcher/src/handler/index.rs b/crates/gitbutler-watcher/src/handler/index.rs index fbbb51c11..f17aca35f 100644 --- a/crates/gitbutler-watcher/src/handler/index.rs +++ b/crates/gitbutler-watcher/src/handler/index.rs @@ -7,7 +7,7 @@ use gitbutler_core::{ sessions::{self, SessionId}, }; -use crate::events as app_events; +use crate::Change; impl super::Handler { pub(super) fn index_deltas( @@ -36,7 +36,7 @@ impl super::Handler { let sessions_iter = gb_repository.get_sessions_iterator()?; for session in sessions_iter { - self.process_session(&gb_repository, &session?)?; + self.process_session(&gb_repository, session?)?; } Ok(()) } @@ -44,7 +44,7 @@ impl super::Handler { pub(super) fn index_session( &self, project_id: ProjectId, - session: &sessions::Session, + session: sessions::Session, ) -> Result<()> { let project = self.projects.get(&project_id)?; let project_repository = @@ -63,21 +63,21 @@ impl super::Handler { fn process_session( &self, gb_repository: &gb_repository::Repository, - session: &sessions::Session, + session: sessions::Session, ) -> Result<()> { let project_id = gb_repository.get_project_id(); // now, index session if it has changed to the database. let from_db = self.sessions_db.get_by_id(&session.id)?; - if from_db.map_or(false, |from_db| from_db == *session) { + if from_db.map_or(false, |from_db| from_db == session) { return Ok(()); } self.sessions_db - .insert(project_id, &[session]) + .insert(project_id, &[&session]) .context("failed to insert session into database")?; - let session_reader = sessions::Reader::open(gb_repository, session)?; + let session_reader = sessions::Reader::open(gb_repository, &session)?; let deltas_reader = deltas::Reader::new(&session_reader); for (file_path, deltas) in deltas_reader .read(None) @@ -86,7 +86,10 @@ impl super::Handler { self.index_deltas(*project_id, session.id, &file_path, &deltas)?; } - (self.send_event)(&app_events::Event::session(*project_id, session))?; + (self.send_event)(Change::Session { + project_id: *project_id, + session, + })?; Ok(()) } } diff --git a/crates/gitbutler-watcher/src/lib.rs b/crates/gitbutler-watcher/src/lib.rs index dca1c8305..9c7ac1754 100644 --- a/crates/gitbutler-watcher/src/lib.rs +++ b/crates/gitbutler-watcher/src/lib.rs @@ -1,104 +1,29 @@ //! Implement the file-monitoring agent that informs about changes in interesting locations. -#![deny(missing_docs)] +#![deny(unsafe_code, rust_2018_idioms)] #![allow(clippy::doc_markdown, clippy::missing_errors_doc)] #![feature(slice_as_chunks)] mod events; -pub use events::Event; use events::InternalEvent; +pub use events::{Action, Change}; mod file_monitor; mod handler; pub use handler::Handler; use std::path::Path; -use std::{sync::Arc, time}; +use std::time; use anyhow::{Context, Result}; -use futures::executor::block_on; -use gitbutler_core::projects::{self, Project, ProjectId}; -use tauri::AppHandle; +use gitbutler_core::projects::ProjectId; use tokio::{ sync::mpsc::{unbounded_channel, UnboundedSender}, task, }; use tokio_util::sync::CancellationToken; -use tracing::instrument; - -/// Note that this type is managed in Tauri and thus needs to be send and sync. -#[derive(Clone)] -pub struct Watchers { - /// NOTE: This handle is required for this type to be self-contained as it's used by `core` through a trait. - app_handle: AppHandle, - /// The watcher of the currently active project. - /// NOTE: This is a `tokio` mutex as this needs to lock the inner option from within async. - watcher: Arc>>, -} - -impl Watchers { - pub fn new(app_handle: AppHandle) -> Self { - Self { - app_handle, - watcher: Default::default(), - } - } - - #[instrument(skip(self, project), err(Debug))] - pub fn watch(&self, project: &projects::Project) -> Result<()> { - let handler = handler::Handler::from_app(&self.app_handle)?; - - let project_id = project.id; - let project_path = project.path.clone(); - - let handle = watch_in_background(handler, project_path, project_id)?; - block_on(self.watcher.lock()).replace(handle); - Ok(()) - } - - pub async fn post(&self, event: Event) -> Result<()> { - let watcher = self.watcher.lock().await; - if let Some(handle) = watcher - .as_ref() - .filter(|watcher| watcher.project_id == event.project_id()) - { - handle.post(event).await.context("failed to post event") - } else { - Err(anyhow::anyhow!("watcher not found",)) - } - } - - pub async fn stop(&self, project_id: ProjectId) { - let mut handle = self.watcher.lock().await; - if handle - .as_ref() - .map_or(false, |handle| handle.project_id == project_id) - { - handle.take(); - } - } -} - -#[async_trait::async_trait] -impl gitbutler_core::projects::Watchers for Watchers { - fn watch(&self, project: &Project) -> Result<()> { - Watchers::watch(self, project) - } - - async fn stop(&self, id: ProjectId) { - Watchers::stop(self, id).await - } - - async fn fetch_gb_data(&self, id: ProjectId) -> Result<()> { - self.post(Event::FetchGitbutlerData(id)).await - } - - async fn push_gb_data(&self, id: ProjectId) -> Result<()> { - self.post(Event::PushGitbutlerData(id)).await - } -} /// An abstraction over a link to the spawned watcher, which runs in the background. -struct WatcherHandle { +pub struct WatcherHandle { /// A way to post events and interact with the actual handler in the background. tx: UnboundedSender, /// The id of the project we are watching. @@ -114,10 +39,18 @@ impl Drop for WatcherHandle { } impl WatcherHandle { - pub async fn post(&self, event: Event) -> Result<()> { - self.tx.send(event.into()).context("failed to send event")?; + /// Post an `action` for the watcher to perform. + pub async fn post(&self, action: Action) -> Result<()> { + self.tx + .send(action.into()) + .context("failed to send event")?; Ok(()) } + + /// Return the id of the project we are watching. + pub fn project_id(&self) -> ProjectId { + self.project_id + } } /// Run our file watcher processing loop in the background and let `handler` deal with them. @@ -128,7 +61,7 @@ impl WatcherHandle { /// /// It runs in such a way that each filesystem event is processed concurrently with others, which is why /// spamming massive amounts of events should be avoided! -fn watch_in_background( +pub fn watch_in_background( handler: handler::Handler, path: impl AsRef, project_id: ProjectId, diff --git a/crates/gitbutler-watcher/tests/handler/calculate_delta.rs b/crates/gitbutler-watcher/tests/handler/calculate_delta.rs index 216b835ed..8ff0b9563 100644 --- a/crates/gitbutler-watcher/tests/handler/calculate_delta.rs +++ b/crates/gitbutler-watcher/tests/handler/calculate_delta.rs @@ -11,18 +11,17 @@ use gitbutler_core::{ reader, sessions, virtual_branches::{self, branch, VirtualBranchesHandle}, }; -use gitbutler_tauri::watcher; use once_cell::sync::Lazy; use self::branch::BranchId; -use crate::watcher::handler::support::Fixture; +use crate::handler::support::Fixture; use gitbutler_testsupport::{commit_all, Case}; static TEST_TARGET_INDEX: Lazy = Lazy::new(|| AtomicUsize::new(0)); #[derive(Clone)] pub struct State { - inner: watcher::Handler, + inner: gitbutler_watcher::Handler, } impl State { diff --git a/crates/gitbutler-watcher/tests/handler/fetch_gitbutler_data.rs b/crates/gitbutler-watcher/tests/handler/fetch_gitbutler_data.rs index 218d96497..526d73833 100644 --- a/crates/gitbutler-watcher/tests/handler/fetch_gitbutler_data.rs +++ b/crates/gitbutler-watcher/tests/handler/fetch_gitbutler_data.rs @@ -1,10 +1,9 @@ use std::time::SystemTime; use gitbutler_core::projects; -use pretty_assertions::assert_eq; -use crate::watcher::handler::support::Fixture; -use crate::watcher::handler::test_remote_repository; +use crate::handler::support::Fixture; +use crate::handler::test_remote_repository; use gitbutler_testsupport::Case; #[tokio::test] diff --git a/crates/gitbutler-watcher/tests/handler/git_file_change.rs b/crates/gitbutler-watcher/tests/handler/git_file_change.rs index 6d2cca9d9..dfb55556e 100644 --- a/crates/gitbutler-watcher/tests/handler/git_file_change.rs +++ b/crates/gitbutler-watcher/tests/handler/git_file_change.rs @@ -2,11 +2,10 @@ use std::fs; use anyhow::Result; use gitbutler_core::projects; -use gitbutler_tauri::watcher; -use pretty_assertions::assert_eq; -use crate::watcher::handler::support::Fixture; +use crate::handler::support::Fixture; use gitbutler_testsupport::Case; +use gitbutler_watcher::Change; #[tokio::test] async fn flush_session() -> Result<()> { @@ -32,10 +31,10 @@ async fn flush_session() -> Result<()> { let events = fixture.events(); assert_eq!(events.len(), 4); - assert!(events[0].name().ends_with("/files")); - assert!(events[1].name().ends_with("/deltas")); - assert!(events[2].name().ends_with("/sessions")); - assert!(events[3].name().ends_with("/sessions")); + assert!(matches!(events[0], Change::File { .. })); + assert!(matches!(events[1], Change::Deltas { .. })); + assert!(matches!(events[2], Change::Session { .. })); + assert!(matches!(events[3], Change::Session { .. })); Ok(()) } @@ -57,9 +56,9 @@ async fn do_not_flush_session_if_file_is_missing() -> Result<()> { } let events = fixture.events(); assert_eq!(events.len(), 3); - assert!(events[0].name().ends_with("/files")); - assert!(events[1].name().ends_with("/deltas")); - assert!(events[2].name().ends_with("/sessions")); + assert!(matches!(events[0], Change::File { .. })); + assert!(matches!(events[1], Change::Deltas { .. })); + assert!(matches!(events[2], Change::Session { .. })); Ok(()) } @@ -83,7 +82,7 @@ async fn flush_deletes_flush_file_without_session_to_flush() -> Result<()> { fn create_new_session_via_new_file( project: &projects::Project, fixture: &mut Fixture, -) -> watcher::Handler { +) -> gitbutler_watcher::Handler { fs::write(project.path.join("test.txt"), "test").unwrap(); let handler = fixture.new_handler(); diff --git a/crates/gitbutler-watcher/tests/handler/mod.rs b/crates/gitbutler-watcher/tests/handler/mod.rs index 76046b261..ffa8c57a2 100644 --- a/crates/gitbutler-watcher/tests/handler/mod.rs +++ b/crates/gitbutler-watcher/tests/handler/mod.rs @@ -2,7 +2,6 @@ use tempfile::TempDir; mod support { use gitbutler_core::{assets, deltas, git, sessions, virtual_branches}; - use gitbutler_tauri::{analytics, watcher}; use tempfile::TempDir; /// Like [`gitbutler_testsupport::Suite`], but with all the instances needed to build a handler @@ -13,8 +12,8 @@ mod support { pub vbranch_controller: virtual_branches::Controller, pub assets_proxy: assets::Proxy, - /// Keeps events emitted from the last created handler. - events: Option>, + /// Keeps changes emitted from the last created handler. + changes: Option>, /// Storage for the databases, to be dropped last. _tmp: TempDir, } @@ -49,7 +48,7 @@ mod support { deltas_db, vbranch_controller, assets_proxy, - events: None, + changes: None, _tmp: tmp, } } @@ -59,12 +58,12 @@ mod support { /// Must be mut as handler events are collected into the fixture automatically. /// /// Note that this only works for the most recent created handler. - pub fn new_handler(&mut self) -> watcher::Handler { + pub fn new_handler(&mut self) -> gitbutler_watcher::Handler { let (tx, rx) = std::sync::mpsc::channel(); - self.events = Some(rx); - watcher::Handler::new( + self.changes = Some(rx); + gitbutler_watcher::Handler::new( self.local_app_data().to_owned(), - analytics::Client::default(), + gitbutler_analytics::Client::default(), self.users.clone(), self.projects.clone(), self.vbranch_controller.clone(), @@ -76,8 +75,8 @@ mod support { } /// Returns the events that were emitted to the tauri app. - pub fn events(&mut self) -> Vec { - let Some(rx) = self.events.as_ref() else { + pub fn events(&mut self) -> Vec { + let Some(rx) = self.changes.as_ref() else { return Vec::new(); }; let mut out = Vec::new(); diff --git a/crates/gitbutler-watcher/tests/handler/push_project_to_gitbutler.rs b/crates/gitbutler-watcher/tests/handler/push_project_to_gitbutler.rs index c40703f50..bf8b75527 100644 --- a/crates/gitbutler-watcher/tests/handler/push_project_to_gitbutler.rs +++ b/crates/gitbutler-watcher/tests/handler/push_project_to_gitbutler.rs @@ -3,8 +3,8 @@ use std::{collections::HashMap, path::PathBuf}; use anyhow::Result; use gitbutler_core::{git, project_repository::LogUntil, projects}; -use crate::watcher::handler::support::Fixture; -use crate::watcher::handler::test_remote_repository; +use crate::handler::support::Fixture; +use crate::handler::test_remote_repository; use gitbutler_testsupport::{virtual_branches::set_test_target, Case}; fn log_walk(repo: &git2::Repository, head: git::Oid) -> Vec {