mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-11-28 13:26:16 +03:00
make watchers into a struct
This commit is contained in:
parent
723823bccd
commit
dfa16066c1
@ -14,18 +14,11 @@ use log;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use storage::Storage;
|
||||
use tauri::{Manager, Runtime, State, Window};
|
||||
use tauri::{Manager, Window};
|
||||
use tauri_plugin_log::{
|
||||
fern::colors::{Color, ColoredLevelConfig},
|
||||
LogTarget,
|
||||
};
|
||||
use watchers::WatcherCollection;
|
||||
|
||||
struct AppState {
|
||||
watchers: WatcherCollection,
|
||||
projects_storage: projects::Storage,
|
||||
users_storage: users::Storage,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Error {
|
||||
@ -44,10 +37,14 @@ fn app_title() -> String {
|
||||
|
||||
#[tauri::command]
|
||||
fn list_sessions(
|
||||
state: State<'_, AppState>,
|
||||
handle: tauri::AppHandle,
|
||||
project_id: &str,
|
||||
) -> Result<Vec<sessions::Session>, Error> {
|
||||
match state.projects_storage.get_project(project_id) {
|
||||
let path_resolver = handle.path_resolver();
|
||||
let storage = storage::Storage::new(&path_resolver);
|
||||
let projects_storage = projects::Storage::new(&storage);
|
||||
|
||||
match projects_storage.get_project(project_id) {
|
||||
Ok(Some(project)) => {
|
||||
let repo = Repository::open(project.path).map_err(|e| {
|
||||
log::error!("{}", e);
|
||||
@ -76,8 +73,12 @@ fn list_sessions(
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn get_user(state: State<'_, AppState>) -> Result<Option<users::User>, Error> {
|
||||
state.users_storage.get().map_err(|e| {
|
||||
fn get_user(handle: tauri::AppHandle) -> Result<Option<users::User>, Error> {
|
||||
let path_resolver = handle.path_resolver();
|
||||
let storage = storage::Storage::new(&path_resolver);
|
||||
let users_storage = users::Storage::new(&storage);
|
||||
|
||||
users_storage.get().map_err(|e| {
|
||||
log::error!("{}", e);
|
||||
Error {
|
||||
message: "Failed to get user".to_string(),
|
||||
@ -86,8 +87,12 @@ fn get_user(state: State<'_, AppState>) -> Result<Option<users::User>, Error> {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn set_user(state: State<'_, AppState>, user: users::User) -> Result<(), Error> {
|
||||
state.users_storage.set(&user).map_err(|e| {
|
||||
fn set_user(handle: tauri::AppHandle, user: users::User) -> Result<(), Error> {
|
||||
let path_resolver = handle.path_resolver();
|
||||
let storage = storage::Storage::new(&path_resolver);
|
||||
let users_storage = users::Storage::new(&storage);
|
||||
|
||||
users_storage.set(&user).map_err(|e| {
|
||||
log::error!("{}", e);
|
||||
Error {
|
||||
message: "Failed to save user".to_string(),
|
||||
@ -97,8 +102,12 @@ fn set_user(state: State<'_, AppState>, user: users::User) -> Result<(), Error>
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn delete_user(state: State<'_, AppState>) -> Result<(), Error> {
|
||||
state.users_storage.delete().map_err(|e| {
|
||||
fn delete_user(handle: tauri::AppHandle) -> Result<(), Error> {
|
||||
let path_resolver = handle.path_resolver();
|
||||
let storage = storage::Storage::new(&path_resolver);
|
||||
let users_storage = users::Storage::new(&storage);
|
||||
|
||||
users_storage.delete().map_err(|e| {
|
||||
log::error!("{}", e);
|
||||
Error {
|
||||
message: "Failed to delete user".to_string(),
|
||||
@ -109,27 +118,35 @@ fn delete_user(state: State<'_, AppState>) -> Result<(), Error> {
|
||||
|
||||
#[tauri::command]
|
||||
fn update_project(
|
||||
state: State<'_, AppState>,
|
||||
handle: tauri::AppHandle,
|
||||
project: projects::UpdateRequest,
|
||||
) -> Result<projects::Project, Error> {
|
||||
state
|
||||
.projects_storage
|
||||
.update_project(&project)
|
||||
.map_err(|e| {
|
||||
log::error!("{}", e);
|
||||
Error {
|
||||
message: "Failed to update project".to_string(),
|
||||
}
|
||||
})
|
||||
let path_resolver = handle.path_resolver();
|
||||
let storage = storage::Storage::new(&path_resolver);
|
||||
let projects_storage = projects::Storage::new(&storage);
|
||||
|
||||
projects_storage.update_project(&project).map_err(|e| {
|
||||
log::error!("{}", e);
|
||||
Error {
|
||||
message: "Failed to update project".to_string(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn add_project<R: Runtime>(
|
||||
window: Window<R>,
|
||||
state: State<'_, AppState>,
|
||||
fn add_project(
|
||||
handle: tauri::AppHandle,
|
||||
window: Window,
|
||||
path: &str,
|
||||
) -> Result<projects::Project, Error> {
|
||||
for project in state.projects_storage.list_projects().map_err(|e| {
|
||||
let path_resolver = handle.path_resolver();
|
||||
let storage = storage::Storage::new(&path_resolver);
|
||||
let projects_storage = projects::Storage::new(&storage);
|
||||
|
||||
let watchers_collection = handle.state::<watchers::WatcherCollection>();
|
||||
let watchers = watchers::Watcher::new(&watchers_collection);
|
||||
|
||||
for project in projects_storage.list_projects().map_err(|e| {
|
||||
log::error!("{}", e);
|
||||
Error {
|
||||
message: "Failed to list projects".to_string(),
|
||||
@ -145,13 +162,13 @@ fn add_project<R: Runtime>(
|
||||
let project = projects::Project::from_path(path.to_string());
|
||||
if project.is_ok() {
|
||||
let project = project.unwrap();
|
||||
state.projects_storage.add_project(&project).map_err(|e| {
|
||||
projects_storage.add_project(&project).map_err(|e| {
|
||||
log::error!("{}", e);
|
||||
Error {
|
||||
message: "Failed to add project".to_string(),
|
||||
}
|
||||
})?;
|
||||
watchers::watch(window, &state.watchers, &project).map_err(|e| {
|
||||
watchers.watch(window, &project).map_err(|e| {
|
||||
log::error!("{}", e);
|
||||
Error {
|
||||
message: "Failed to watch project".to_string(),
|
||||
@ -166,8 +183,12 @@ fn add_project<R: Runtime>(
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn list_projects(state: State<'_, AppState>) -> Result<Vec<projects::Project>, Error> {
|
||||
state.projects_storage.list_projects().map_err(|e| {
|
||||
fn list_projects(handle: tauri::AppHandle) -> Result<Vec<projects::Project>, Error> {
|
||||
let path_resolver = handle.path_resolver();
|
||||
let storage = storage::Storage::new(&path_resolver);
|
||||
let projects_storage = projects::Storage::new(&storage);
|
||||
|
||||
projects_storage.list_projects().map_err(|e| {
|
||||
log::error!("{}", e);
|
||||
Error {
|
||||
message: "Failed to list projects".to_string(),
|
||||
@ -176,17 +197,24 @@ fn list_projects(state: State<'_, AppState>) -> Result<Vec<projects::Project>, E
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn delete_project(state: State<'_, AppState>, id: &str) -> Result<(), Error> {
|
||||
match state.projects_storage.get_project(id) {
|
||||
fn delete_project(handle: tauri::AppHandle, id: &str) -> Result<(), Error> {
|
||||
let path_resolver = handle.path_resolver();
|
||||
let storage = storage::Storage::new(&path_resolver);
|
||||
let projects_storage = projects::Storage::new(&storage);
|
||||
|
||||
let watchers_collection = handle.state::<watchers::WatcherCollection>();
|
||||
let watchers = watchers::Watcher::new(&watchers_collection);
|
||||
|
||||
match projects_storage.get_project(id) {
|
||||
Ok(Some(project)) => {
|
||||
watchers::unwatch(&state.watchers, project).map_err(|e| {
|
||||
watchers.unwatch(project).map_err(|e| {
|
||||
log::error!("{}", e);
|
||||
Error {
|
||||
message: "Failed to unwatch project".to_string(),
|
||||
}
|
||||
})?;
|
||||
|
||||
state.projects_storage.delete_project(id).map_err(|e| {
|
||||
projects_storage.delete_project(id).map_err(|e| {
|
||||
log::error!("{}", e);
|
||||
Error {
|
||||
message: "Failed to delete project".to_string(),
|
||||
@ -207,11 +235,15 @@ fn delete_project(state: State<'_, AppState>, id: &str) -> Result<(), Error> {
|
||||
|
||||
#[tauri::command]
|
||||
fn list_session_files(
|
||||
state: State<'_, AppState>,
|
||||
handle: tauri::AppHandle,
|
||||
project_id: &str,
|
||||
session_id: &str,
|
||||
) -> Result<HashMap<String, String>, Error> {
|
||||
match state.projects_storage.get_project(project_id) {
|
||||
let path_resolver = handle.path_resolver();
|
||||
let storage = storage::Storage::new(&path_resolver);
|
||||
let projects_storage = projects::Storage::new(&storage);
|
||||
|
||||
match projects_storage.get_project(project_id) {
|
||||
Ok(Some(project)) => {
|
||||
let repo = Repository::open(&project.path).map_err(|e| {
|
||||
log::error!("{}", e);
|
||||
@ -243,11 +275,15 @@ fn list_session_files(
|
||||
|
||||
#[tauri::command]
|
||||
fn list_deltas(
|
||||
state: State<'_, AppState>,
|
||||
handle: tauri::AppHandle,
|
||||
project_id: &str,
|
||||
session_id: &str,
|
||||
) -> Result<HashMap<String, Vec<Delta>>, Error> {
|
||||
match state.projects_storage.get_project(project_id) {
|
||||
let path_resolver = handle.path_resolver();
|
||||
let storage = storage::Storage::new(&path_resolver);
|
||||
let projects_storage = projects::Storage::new(&storage);
|
||||
|
||||
match projects_storage.get_project(project_id) {
|
||||
Ok(Some(project)) => {
|
||||
let repo = Repository::open(&project.path).map_err(|e| {
|
||||
log::error!("{}", e);
|
||||
@ -350,14 +386,15 @@ fn main() {
|
||||
.setup(move |app| {
|
||||
let resolver = app.path_resolver();
|
||||
let storage = Storage::new(&resolver);
|
||||
let projects_storage = projects::Storage::new(storage.clone());
|
||||
let users_storage = users::Storage::new(storage.clone());
|
||||
let projects_storage = projects::Storage::new(&storage);
|
||||
|
||||
let watchers = watchers::WatcherCollection::default();
|
||||
let watcher_collection = watchers::WatcherCollection::default();
|
||||
let watchers = watchers::Watcher::new(&watcher_collection);
|
||||
|
||||
if let Ok(projects) = projects_storage.list_projects() {
|
||||
for project in projects {
|
||||
watchers::watch(app.get_window("main").unwrap(), &watchers, &project)
|
||||
watchers
|
||||
.watch(app.get_window("main").unwrap(), &project)
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
} else {
|
||||
@ -367,11 +404,7 @@ fn main() {
|
||||
#[cfg(debug_assertions)]
|
||||
app.get_window("main").unwrap().open_devtools();
|
||||
|
||||
app.manage(AppState {
|
||||
watchers,
|
||||
projects_storage,
|
||||
users_storage,
|
||||
});
|
||||
app.manage(watcher_collection);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
|
@ -5,8 +5,8 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
const PROJECTS_FILE: &str = "projects.json";
|
||||
|
||||
pub struct Storage {
|
||||
storage: storage::Storage,
|
||||
pub struct Storage<'a> {
|
||||
storage: &'a storage::Storage,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@ -16,8 +16,8 @@ pub struct UpdateRequest {
|
||||
api: Option<project::ApiProject>,
|
||||
}
|
||||
|
||||
impl Storage {
|
||||
pub fn new(storage: storage::Storage) -> Self {
|
||||
impl<'a> Storage<'a> {
|
||||
pub fn new(storage: &'a storage::Storage) -> Self {
|
||||
Self { storage }
|
||||
}
|
||||
|
||||
|
@ -4,12 +4,12 @@ use anyhow::Result;
|
||||
|
||||
const USER_FILE: &str = "user.json";
|
||||
|
||||
pub struct Storage {
|
||||
storage: storage::Storage,
|
||||
pub struct Storage<'a> {
|
||||
storage: &'a storage::Storage,
|
||||
}
|
||||
|
||||
impl Storage {
|
||||
pub fn new(storage: storage::Storage) -> Self {
|
||||
impl<'a> Storage<'a> {
|
||||
pub fn new(storage: &'a storage::Storage) -> Self {
|
||||
Self { storage }
|
||||
}
|
||||
|
||||
|
@ -13,73 +13,79 @@ use std::{collections::HashMap, sync::Mutex};
|
||||
#[derive(Default)]
|
||||
pub struct WatcherCollection(Mutex<HashMap<String, RecommendedWatcher>>);
|
||||
|
||||
pub fn unwatch(watchers: &WatcherCollection, project: projects::Project) -> Result<()> {
|
||||
let mut watchers = watchers.0.lock().unwrap();
|
||||
if let Some(mut watcher) = watchers.remove(&project.path) {
|
||||
watcher.unwatch(Path::new(&project.path))?;
|
||||
}
|
||||
Ok(())
|
||||
pub struct DeltaWatchers<'a> {
|
||||
watchers: &'a WatcherCollection,
|
||||
}
|
||||
|
||||
pub fn watch<R: tauri::Runtime>(
|
||||
window: tauri::Window<R>,
|
||||
watchers: &WatcherCollection,
|
||||
project: projects::Project,
|
||||
) -> Result<()> {
|
||||
log::info!("Watching deltas for {}", project.path);
|
||||
let project_path = Path::new(&project.path);
|
||||
impl<'a> DeltaWatchers<'a> {
|
||||
pub fn new(watchers: &'a WatcherCollection) -> Self {
|
||||
Self { watchers }
|
||||
}
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
|
||||
pub fn watch(&self, window: tauri::Window, project: projects::Project) -> Result<()> {
|
||||
log::info!("Watching deltas for {}", project.path);
|
||||
let project_path = Path::new(&project.path);
|
||||
|
||||
watcher.watch(project_path, RecursiveMode::Recursive)?;
|
||||
let (tx, rx) = channel();
|
||||
let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
|
||||
|
||||
watchers
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(project.path.clone(), watcher);
|
||||
watcher.watch(project_path, RecursiveMode::Recursive)?;
|
||||
|
||||
let repo = Repository::open(project_path);
|
||||
thread::spawn(move || {
|
||||
if repo.is_err() {
|
||||
log::error!("failed to open git repo: {:?}", repo.err());
|
||||
return;
|
||||
}
|
||||
let repo = repo.unwrap();
|
||||
self.watchers
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(project.path.clone(), watcher);
|
||||
|
||||
while let Ok(event) = rx.recv() {
|
||||
if let Ok(event) = event {
|
||||
for file_path in event.paths {
|
||||
let relative_file_path =
|
||||
file_path.strip_prefix(repo.workdir().unwrap()).unwrap();
|
||||
match register_file_change(
|
||||
&window,
|
||||
&project,
|
||||
&repo,
|
||||
&event.kind,
|
||||
&relative_file_path,
|
||||
) {
|
||||
Ok(Some((session, deltas))) => {
|
||||
events::deltas(
|
||||
&window,
|
||||
&project,
|
||||
&session,
|
||||
&deltas,
|
||||
&relative_file_path,
|
||||
);
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(e) => log::error!("Error: {:?}", e),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::error!("Error: {:?}", event);
|
||||
let repo = Repository::open(project_path);
|
||||
thread::spawn(move || {
|
||||
if repo.is_err() {
|
||||
log::error!("failed to open git repo: {:?}", repo.err());
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
let repo = repo.unwrap();
|
||||
|
||||
Ok(())
|
||||
while let Ok(event) = rx.recv() {
|
||||
if let Ok(event) = event {
|
||||
for file_path in event.paths {
|
||||
let relative_file_path =
|
||||
file_path.strip_prefix(repo.workdir().unwrap()).unwrap();
|
||||
match register_file_change(
|
||||
&window,
|
||||
&project,
|
||||
&repo,
|
||||
&event.kind,
|
||||
&relative_file_path,
|
||||
) {
|
||||
Ok(Some((session, deltas))) => {
|
||||
events::deltas(
|
||||
&window,
|
||||
&project,
|
||||
&session,
|
||||
&deltas,
|
||||
&relative_file_path,
|
||||
);
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(e) => log::error!("Error: {:?}", e),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::error!("Error: {:?}", event);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unwatch(&self, project: projects::Project) -> Result<()> {
|
||||
let mut watchers = self.watchers.0.lock().unwrap();
|
||||
if let Some(mut watcher) = watchers.remove(&project.path) {
|
||||
watcher.unwatch(Path::new(&project.path))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// this is what is called when the FS watcher detects a change
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{butler, events, projects, sessions};
|
||||
use git2::Repository;
|
||||
use anyhow::Result;
|
||||
use git2::Repository;
|
||||
use std::{
|
||||
thread,
|
||||
time::{Duration, SystemTime},
|
||||
@ -9,48 +9,49 @@ use std::{
|
||||
const FIVE_MINUTES: u64 = Duration::new(5 * 60, 0).as_secs();
|
||||
const ONE_HOUR: u64 = Duration::new(60 * 60, 0).as_secs();
|
||||
|
||||
pub fn watch<R: tauri::Runtime>(
|
||||
window: tauri::Window<R>,
|
||||
project: projects::Project,
|
||||
) -> Result<() > {
|
||||
let repo = git2::Repository::open(&project.path)?;
|
||||
thread::spawn(move || loop {
|
||||
match repo.revparse_single(format!("refs/{}/current", butler::refname()).as_str()) {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
// make sure all the files are tracked by gitbutler session
|
||||
if sessions::Session::from_head(&repo).is_err() {
|
||||
log::error!(
|
||||
"Error while creating session for {}",
|
||||
repo.workdir().unwrap().display()
|
||||
);
|
||||
pub struct GitWatcher {}
|
||||
|
||||
impl GitWatcher {
|
||||
pub fn watch(&self, window: tauri::Window, project: projects::Project) -> Result<()> {
|
||||
let repo = git2::Repository::open(&project.path)?;
|
||||
thread::spawn(move || loop {
|
||||
match repo.revparse_single(format!("refs/{}/current", butler::refname()).as_str()) {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
// make sure all the files are tracked by gitbutler session
|
||||
if sessions::Session::from_head(&repo).is_err() {
|
||||
log::error!(
|
||||
"Error while creating session for {}",
|
||||
repo.workdir().unwrap().display()
|
||||
);
|
||||
}
|
||||
if sessions::flush_current_session(&repo).is_err() {
|
||||
log::error!(
|
||||
"Error while flushing current session for {}",
|
||||
repo.workdir().unwrap().display()
|
||||
);
|
||||
}
|
||||
}
|
||||
if sessions::flush_current_session(&repo).is_err() {
|
||||
}
|
||||
|
||||
match check_for_changes(&repo) {
|
||||
Ok(Some(session)) => {
|
||||
events::session(&window, &project, &session);
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(error) => {
|
||||
log::error!(
|
||||
"Error while flushing current session for {}",
|
||||
repo.workdir().unwrap().display()
|
||||
"Error while checking {} for changes: {}",
|
||||
repo.workdir().unwrap().display(),
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
thread::sleep(Duration::from_secs(10));
|
||||
});
|
||||
|
||||
match check_for_changes(&repo) {
|
||||
Ok(Some(session)) => {
|
||||
events::session(&window, &project, &session);
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(error) => {
|
||||
log::error!(
|
||||
"Error while checking {} for changes: {}",
|
||||
repo.workdir().unwrap().display(),
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
thread::sleep(Duration::from_secs(10));
|
||||
});
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// main thing called in a loop to check for changes and write our custom commit data
|
||||
|
@ -4,20 +4,32 @@ mod git;
|
||||
pub use self::delta::WatcherCollection;
|
||||
use crate::projects;
|
||||
use anyhow::Result;
|
||||
use tauri::{Runtime, Window};
|
||||
use tauri;
|
||||
|
||||
pub fn watch<R: Runtime>(
|
||||
window: Window<R>,
|
||||
watchers: &WatcherCollection,
|
||||
project: &projects::Project,
|
||||
) -> Result<()> {
|
||||
self::delta::watch(window.clone(), watchers, project.clone())?;
|
||||
self::git::watch(window.clone(), project.clone())?;
|
||||
Ok(())
|
||||
pub struct Watcher<'a> {
|
||||
git_watcher: git::GitWatcher,
|
||||
delta_watcher: delta::DeltaWatchers<'a>,
|
||||
}
|
||||
|
||||
pub fn unwatch(watchers: &WatcherCollection, project: projects::Project) -> Result<()> {
|
||||
delta::unwatch(watchers, project)?;
|
||||
// TODO: how to unwatch git ?
|
||||
Ok(())
|
||||
impl<'a> Watcher<'a> {
|
||||
pub fn new(watchers: &'a delta::WatcherCollection) -> Self {
|
||||
let git_watcher = git::GitWatcher {};
|
||||
let delta_watcher = delta::DeltaWatchers::new(watchers);
|
||||
Self {
|
||||
git_watcher,
|
||||
delta_watcher,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn watch(&self, window: tauri::Window, project: &projects::Project) -> Result<()> {
|
||||
self.delta_watcher.watch(window.clone(), project.clone())?;
|
||||
self.git_watcher.watch(window.clone(), project.clone())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unwatch(&self, project: projects::Project) -> Result<()> {
|
||||
self.delta_watcher.unwatch(project)?;
|
||||
// TODO: how to unwatch git ?
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user