mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-28 12:05:22 +03:00
create tauri command to list deltas
This commit is contained in:
parent
3346f2f65c
commit
2eab889720
19
src-tauri/Cargo.lock
generated
19
src-tauri/Cargo.lock
generated
@ -1026,7 +1026,6 @@ dependencies = [
|
||||
"git2",
|
||||
"log",
|
||||
"notify",
|
||||
"path-absolutize",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
@ -1976,24 +1975,6 @@ version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
|
||||
|
||||
[[package]]
|
||||
name = "path-absolutize"
|
||||
version = "3.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f1d4993b16f7325d90c18c3c6a3327db7808752db8d208cea0acee0abd52c52"
|
||||
dependencies = [
|
||||
"path-dedot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "path-dedot"
|
||||
version = "3.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a81540d94551664b72b72829b12bd167c73c9d25fbac0e04fafa8023f7e4901"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.2.0"
|
||||
|
@ -27,7 +27,6 @@ yrs = "0.16.1"
|
||||
difference = "2.0.0"
|
||||
futures = "0.3.26"
|
||||
git2 = "0.16.1"
|
||||
path-absolutize = "3.0.14"
|
||||
chrono = "0.4.23"
|
||||
|
||||
[features]
|
||||
|
@ -274,7 +274,6 @@ fn test_document_complex() {
|
||||
assert_eq!(document.to_string(), "held!");
|
||||
assert_eq!(document.get_deltas().len(), 3);
|
||||
assert_eq!(document.get_deltas()[2].operations.len(), 2);
|
||||
println!("{:?}", document.get_deltas()[2].operations);
|
||||
assert_eq!(
|
||||
document.get_deltas()[2].operations[0],
|
||||
Operation::Delete((3, 7))
|
||||
|
@ -1,12 +1,11 @@
|
||||
use crate::crdt::{self, TextDocument};
|
||||
use crate::crdt::TextDocument;
|
||||
use crate::projects::Project;
|
||||
use futures::{
|
||||
channel::mpsc::{channel, Receiver},
|
||||
SinkExt, StreamExt,
|
||||
};
|
||||
use git2::{Commit, Oid, Repository};
|
||||
use git2::{Commit, Repository};
|
||||
use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
|
||||
use path_absolutize::*;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
@ -66,24 +65,18 @@ fn register_file_change(
|
||||
kind: EventKind,
|
||||
files: Vec<PathBuf>,
|
||||
) {
|
||||
// update meta files every time file change is detected
|
||||
write_beginning_meta_files(&repo);
|
||||
|
||||
let project_path = PathBuf::from(&project.path);
|
||||
for file_path in files {
|
||||
if !file_path.is_file() {
|
||||
// only handle file changes
|
||||
continue;
|
||||
}
|
||||
|
||||
let relative_file_path = file_path
|
||||
.absolutize()
|
||||
.unwrap()
|
||||
.strip_prefix(project_path.clone())
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
let relative_file_path = Path::new(file_path.strip_prefix(&project.path).unwrap());
|
||||
if repo.is_path_ignored(&relative_file_path).unwrap_or(true) {
|
||||
// make sure we're not watching ignored files
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -95,23 +88,6 @@ fn register_file_change(
|
||||
log::info!("File removed: {:?}", file_path);
|
||||
}
|
||||
|
||||
let relative_deltas_path = PathBuf::from(".git/gb/session/deltas/");
|
||||
let delta_path = project_path
|
||||
.join(relative_deltas_path)
|
||||
.join(relative_file_path.clone())
|
||||
.clone();
|
||||
std::fs::create_dir_all(delta_path.parent().unwrap()).unwrap();
|
||||
|
||||
let deltas = if delta_path.exists() {
|
||||
let raw_deltas = std::fs::read_to_string(delta_path.clone())
|
||||
.expect(format!("Failed to read {}", delta_path.to_str().unwrap()).as_str());
|
||||
let deltas: Vec<crdt::Delta> = serde_json::from_str(&raw_deltas)
|
||||
.expect(format!("Failed to parse {}", delta_path.to_str().unwrap()).as_str());
|
||||
Some(deltas)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// first, we need to check if the file exists in the meta commit
|
||||
let meta_commit = get_meta_commit(&repo);
|
||||
let tree = meta_commit.tree().unwrap();
|
||||
@ -124,40 +100,34 @@ fn register_file_change(
|
||||
None
|
||||
};
|
||||
|
||||
// second, get non-flushed file deltas
|
||||
let deltas = project.get_file_deltas(Path::new(&relative_file_path));
|
||||
|
||||
// depending on the above, we can create TextDocument
|
||||
let mut text_doc = match (commit_blob, deltas) {
|
||||
(Some(contents), Some(deltas)) => {
|
||||
println!("found deltas and commit blob");
|
||||
TextDocument::new(&contents, deltas)
|
||||
}
|
||||
(Some(contents), None) => {
|
||||
println!("found commit blob, no deltas");
|
||||
TextDocument::new(&contents, vec![])
|
||||
}
|
||||
(None, Some(deltas)) => {
|
||||
println!("found deltas, no commit blob");
|
||||
TextDocument::from_deltas(deltas)
|
||||
}
|
||||
(None, None) => {
|
||||
println!("no deltas or commit blob");
|
||||
TextDocument::from_deltas(vec![])
|
||||
}
|
||||
};
|
||||
|
||||
// update the TextDocument with the new file contents
|
||||
let contents = std::fs::read_to_string(file_path.clone())
|
||||
.expect(format!("Failed to read {}", file_path.to_str().unwrap()).as_str());
|
||||
|
||||
if !text_doc.update(&contents) {
|
||||
// if the document hasn't changed, we don't need to write a delta.
|
||||
continue;
|
||||
if text_doc.update(&contents) {
|
||||
// if the file was modified, save the deltas
|
||||
let deltas = text_doc.get_deltas();
|
||||
project.save_file_deltas(relative_file_path, deltas);
|
||||
}
|
||||
|
||||
let deltas = text_doc.get_deltas();
|
||||
|
||||
log::info!("Writing delta to {}", delta_path.to_str().unwrap());
|
||||
|
||||
let mut file = File::create(delta_path).unwrap();
|
||||
file.write_all(serde_json::to_string(&deltas).unwrap().as_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
19
src-tauri/src/fs.rs
Normal file
19
src-tauri/src/fs.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use std::{fs::read_dir, path::Path};
|
||||
|
||||
// return a list of files in directory recursively
|
||||
pub fn list_files(path: &Path) -> Vec<String> {
|
||||
let mut files = Vec::new();
|
||||
if path.is_dir() {
|
||||
for entry in read_dir(path).unwrap() {
|
||||
let entry = entry.unwrap();
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
files.append(&mut list_files(&path));
|
||||
} else {
|
||||
files.push(path.to_str().unwrap().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
files.sort();
|
||||
files
|
||||
}
|
@ -1,19 +1,17 @@
|
||||
mod crdt;
|
||||
mod delta_watchers;
|
||||
mod fs;
|
||||
mod projects;
|
||||
mod storage;
|
||||
|
||||
use crdt::TextDocument;
|
||||
use crdt::Delta;
|
||||
use delta_watchers::watch;
|
||||
use fs::list_files;
|
||||
use log;
|
||||
use projects::Project;
|
||||
use std::collections::HashMap;
|
||||
use std::thread;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
sync::Mutex,
|
||||
};
|
||||
use std::{fs::read_to_string, path::Path};
|
||||
use storage::Storage;
|
||||
use tauri::{InvokeError, Manager, State};
|
||||
use tauri_plugin_log::{
|
||||
@ -25,24 +23,6 @@ struct AppState {
|
||||
projects_storage: projects::Storage,
|
||||
}
|
||||
|
||||
// return a list of files in directory recursively
|
||||
fn list_files(path: &Path) -> Vec<String> {
|
||||
let mut files = Vec::new();
|
||||
if path.is_dir() {
|
||||
for entry in fs::read_dir(path).unwrap() {
|
||||
let entry = entry.unwrap();
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
files.append(&mut list_files(&path));
|
||||
} else {
|
||||
files.push(path.to_str().unwrap().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
files.sort();
|
||||
files
|
||||
}
|
||||
|
||||
// returns a list of files in directory recursively
|
||||
#[tauri::command]
|
||||
fn read_dir(path: &str) -> Result<Vec<String>, InvokeError> {
|
||||
@ -58,7 +38,7 @@ fn read_dir(path: &str) -> Result<Vec<String>, InvokeError> {
|
||||
// reads file contents and returns it
|
||||
#[tauri::command]
|
||||
fn read_file(file_path: &str) -> Result<String, InvokeError> {
|
||||
let contents = fs::read_to_string(file_path);
|
||||
let contents = read_to_string(file_path);
|
||||
if contents.is_ok() {
|
||||
return Ok(contents.unwrap());
|
||||
} else {
|
||||
@ -100,8 +80,17 @@ fn delete_project(state: State<'_, AppState>, id: &str) -> Result<(), InvokeErro
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CRDTSCollection(Mutex<HashMap<PathBuf, TextDocument>>);
|
||||
#[tauri::command]
|
||||
fn list_deltas(
|
||||
state: State<'_, AppState>,
|
||||
project_id: &str,
|
||||
) -> Result<HashMap<String, Vec<Delta>>, InvokeError> {
|
||||
if let Some(project) = state.projects_storage.get_project(project_id)? {
|
||||
Ok(project.list_deltas())
|
||||
} else {
|
||||
Err("Project not found".into())
|
||||
}
|
||||
}
|
||||
|
||||
fn watch_project(project: &Project) {
|
||||
log::info!("Watching project: {}", project.path);
|
||||
@ -155,7 +144,8 @@ fn main() {
|
||||
read_dir,
|
||||
add_project,
|
||||
list_projects,
|
||||
delete_project
|
||||
delete_project,
|
||||
list_deltas
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
|
@ -1,7 +1,9 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::storage;
|
||||
use crate::{crdt::Delta, fs::list_files, storage};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
pub struct Project {
|
||||
@ -38,6 +40,51 @@ impl Project {
|
||||
})
|
||||
.ok_or("Unable to get title".to_string())
|
||||
}
|
||||
|
||||
fn deltas_path(&self) -> PathBuf {
|
||||
let path = PathBuf::from(&self.path).join(PathBuf::from(".git/gb/session/deltas"));
|
||||
std::fs::create_dir_all(path.clone()).unwrap();
|
||||
path
|
||||
}
|
||||
|
||||
pub fn save_file_deltas(&self, file_path: &Path, deltas: Vec<Delta>) {
|
||||
if deltas.is_empty() {
|
||||
return;
|
||||
}
|
||||
let project_deltas_path = self.deltas_path();
|
||||
let delta_path = project_deltas_path.join(file_path.to_path_buf());
|
||||
log::info!("Writing delta to {}", delta_path.to_str().unwrap());
|
||||
let raw_deltas = serde_json::to_string(&deltas).unwrap();
|
||||
std::fs::write(delta_path, raw_deltas).unwrap();
|
||||
}
|
||||
|
||||
pub fn get_file_deltas(&self, file_path: &Path) -> Option<Vec<Delta>> {
|
||||
let project_deltas_path = self.deltas_path();
|
||||
let delta_path = project_deltas_path.join(file_path.to_path_buf());
|
||||
if delta_path.exists() {
|
||||
let raw_deltas = std::fs::read_to_string(delta_path.clone())
|
||||
.expect(format!("Failed to read {}", delta_path.to_str().unwrap()).as_str());
|
||||
let deltas: Vec<Delta> = serde_json::from_str(&raw_deltas)
|
||||
.expect(format!("Failed to parse {}", delta_path.to_str().unwrap()).as_str());
|
||||
Some(deltas)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list_deltas(&self) -> HashMap<String, Vec<Delta>> {
|
||||
let deltas_path = self.deltas_path();
|
||||
let file_paths = list_files(&deltas_path);
|
||||
let mut deltas = HashMap::new();
|
||||
for file_path in file_paths {
|
||||
let file_path = Path::new(&file_path);
|
||||
let file_deltas = self.get_file_deltas(file_path);
|
||||
if let Some(file_deltas) = file_deltas {
|
||||
deltas.insert(file_path.to_str().unwrap().to_string(), file_deltas);
|
||||
}
|
||||
}
|
||||
deltas
|
||||
}
|
||||
}
|
||||
|
||||
const PROJECTS_FILE: &str = "projects.json";
|
||||
|
Loading…
Reference in New Issue
Block a user