mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2025-01-06 01:27:24 +03:00
commit
04b9f7bd8b
@ -20,7 +20,7 @@ impl super::RunCommand for Branches {
|
||||
.context("failed to list branches")?;
|
||||
|
||||
for branch in branches {
|
||||
println!("{}", branch.id.red());
|
||||
println!("{}", branch.id.to_string().red());
|
||||
println!("{}", branch.name.red());
|
||||
for file in branch.files {
|
||||
println!(" {}", file.path.display().to_string().blue());
|
||||
|
@ -47,8 +47,11 @@ impl super::RunCommand for Commit {
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
let commit_branch = ids[selection].clone();
|
||||
println!("Committing virtual branch {}", commit_branch.red());
|
||||
let commit_branch = ids[selection];
|
||||
println!(
|
||||
"Committing virtual branch {}",
|
||||
commit_branch.to_string().red()
|
||||
);
|
||||
|
||||
// get the commit message
|
||||
let message: String = Input::with_theme(&ColorfulTheme::default())
|
||||
|
@ -26,7 +26,7 @@ impl super::RunCommand for Info {
|
||||
|
||||
// find the project in project storage that matches the cwd
|
||||
println!("{}", "project:".to_string().red());
|
||||
println!(" id: {}", app.project().id.blue());
|
||||
println!(" id: {}", app.project().id.to_string().blue());
|
||||
println!(" title: {}", app.project().title.blue());
|
||||
println!(
|
||||
" description: {}",
|
||||
@ -77,7 +77,7 @@ impl super::RunCommand for Info {
|
||||
.unwrap();
|
||||
//list the sessions
|
||||
for session in &sessions {
|
||||
println!(" id: {}", session.id.blue());
|
||||
println!(" id: {}", session.id.to_string().blue());
|
||||
}
|
||||
|
||||
// gitbutler repo stuff
|
||||
|
@ -22,7 +22,7 @@ impl super::RunCommand for Status {
|
||||
println!(" branch: {}", branch.name.blue());
|
||||
println!(" head: {}", branch.head.to_string().green());
|
||||
println!(" tree: {}", branch.tree.to_string().green());
|
||||
println!(" id: {}", branch.id.green());
|
||||
println!(" id: {}", branch.id.to_string().green());
|
||||
println!("applied: {}", branch.applied.to_string().green());
|
||||
println!(" files:");
|
||||
for file in files {
|
||||
|
@ -2,7 +2,7 @@ use std::{str, sync::Arc};
|
||||
|
||||
use tauri::AppHandle;
|
||||
|
||||
use crate::users::User;
|
||||
use crate::{projects::ProjectId, users::User};
|
||||
|
||||
mod posthog;
|
||||
|
||||
@ -13,13 +13,13 @@ pub struct Config<'c> {
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Event {
|
||||
HeadChange {
|
||||
project_id: String,
|
||||
project_id: ProjectId,
|
||||
reference_name: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub fn project_id(&self) -> &str {
|
||||
pub fn project_id(&self) -> &ProjectId {
|
||||
match self {
|
||||
Event::HeadChange { project_id, .. } => project_id,
|
||||
}
|
||||
|
@ -9,7 +9,10 @@ use crate::{
|
||||
keys,
|
||||
paths::DataDir,
|
||||
project_repository::{self, conflicts},
|
||||
projects, reader, search, sessions, users,
|
||||
projects::{self, ProjectId},
|
||||
reader, search,
|
||||
sessions::{self, SessionId},
|
||||
users,
|
||||
virtual_branches::{self, target},
|
||||
watcher,
|
||||
};
|
||||
@ -73,19 +76,19 @@ impl App {
|
||||
.with_context(|| "failed to list projects")?
|
||||
{
|
||||
if let Err(error) = self.init_project(&project).await {
|
||||
tracing::error!(project.id, ?error, "failed to init project");
|
||||
tracing::error!(%project.id, ?error, "failed to init project");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_project(&self, id: &str) -> Result<projects::Project, Error> {
|
||||
pub fn get_project(&self, id: &ProjectId) -> Result<projects::Project, Error> {
|
||||
self.projects.get(id).map_err(Error::GetProject)
|
||||
}
|
||||
|
||||
pub fn list_sessions(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
earliest_timestamp_ms: Option<u128>,
|
||||
) -> Result<Vec<sessions::Session>> {
|
||||
self.sessions_database
|
||||
@ -94,8 +97,8 @@ impl App {
|
||||
|
||||
pub fn list_session_files(
|
||||
&self,
|
||||
project_id: &str,
|
||||
session_id: &str,
|
||||
project_id: &ProjectId,
|
||||
session_id: &SessionId,
|
||||
paths: &Option<Vec<path::PathBuf>>,
|
||||
) -> Result<HashMap<path::PathBuf, reader::Content>, Error> {
|
||||
let session = self
|
||||
@ -120,7 +123,7 @@ impl App {
|
||||
.map_err(Error::Other)
|
||||
}
|
||||
|
||||
pub fn mark_resolved(&self, project_id: &str, path: &str) -> Result<(), Error> {
|
||||
pub fn mark_resolved(&self, project_id: &ProjectId, path: &str) -> Result<(), Error> {
|
||||
let project = self.projects.get(project_id)?;
|
||||
let project_repository = project_repository::Repository::try_from(&project)?;
|
||||
// mark file as resolved
|
||||
@ -128,7 +131,7 @@ impl App {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn fetch_from_target(&self, project_id: &str) -> Result<(), Error> {
|
||||
pub fn fetch_from_target(&self, project_id: &ProjectId) -> Result<(), Error> {
|
||||
let project = self.projects.get(project_id)?;
|
||||
let project_repository = project_repository::Repository::try_from(&project)?;
|
||||
let user = self.users.get_user().context("failed to get user")?;
|
||||
@ -197,7 +200,7 @@ impl App {
|
||||
|
||||
pub fn list_bookmarks(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
range: Option<ops::Range<u128>>,
|
||||
) -> Result<Vec<bookmarks::Bookmark>, Error> {
|
||||
self.bookmarks_database
|
||||
@ -207,8 +210,8 @@ impl App {
|
||||
|
||||
pub fn list_session_deltas(
|
||||
&self,
|
||||
project_id: &str,
|
||||
session_id: &str,
|
||||
project_id: &ProjectId,
|
||||
session_id: &SessionId,
|
||||
paths: &Option<Vec<&str>>,
|
||||
) -> Result<HashMap<String, Vec<deltas::Delta>>, Error> {
|
||||
self.deltas_database
|
||||
@ -218,7 +221,7 @@ impl App {
|
||||
|
||||
pub fn git_wd_diff(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
context_lines: u32,
|
||||
) -> Result<HashMap<path::PathBuf, String>, Error> {
|
||||
let project = self.projects.get(project_id)?;
|
||||
@ -255,7 +258,7 @@ impl App {
|
||||
|
||||
pub fn git_remote_branches(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
) -> Result<Vec<git::RemoteBranchName>, Error> {
|
||||
let project = self.projects.get(project_id)?;
|
||||
let project_repository = project_repository::Repository::try_from(&project)?;
|
||||
@ -266,7 +269,7 @@ impl App {
|
||||
|
||||
pub fn git_remote_branches_data(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
) -> Result<Vec<virtual_branches::RemoteBranch>, Error> {
|
||||
let project = self.projects.get(project_id)?;
|
||||
let project_repository = project_repository::Repository::try_from(&project)?;
|
||||
@ -281,7 +284,7 @@ impl App {
|
||||
.map_err(Error::Other)
|
||||
}
|
||||
|
||||
pub fn git_head(&self, project_id: &str) -> Result<String, Error> {
|
||||
pub fn git_head(&self, project_id: &ProjectId) -> Result<String, Error> {
|
||||
let project = self.projects.get(project_id)?;
|
||||
let project_repository = project_repository::Repository::try_from(&project)?;
|
||||
let head = project_repository
|
||||
@ -311,7 +314,7 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn git_gb_push(&self, project_id: &str) -> Result<(), Error> {
|
||||
pub fn git_gb_push(&self, project_id: &ProjectId) -> Result<(), Error> {
|
||||
let user = self.users.get_user().context("failed to get user")?;
|
||||
let project = self.projects.get(project_id)?;
|
||||
let project_repository = project_repository::Repository::try_from(&project)?;
|
||||
|
@ -3,7 +3,7 @@ use std::ops;
|
||||
use anyhow::{Context, Result};
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
use crate::database;
|
||||
use crate::{database, projects::ProjectId};
|
||||
|
||||
use super::Bookmark;
|
||||
|
||||
@ -27,7 +27,7 @@ impl From<&AppHandle> for Database {
|
||||
impl Database {
|
||||
fn get_by_project_id_timestamp_ms(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
timestamp_ms: &u128,
|
||||
) -> Result<Option<Bookmark>> {
|
||||
self.database.transaction(|tx| {
|
||||
@ -98,7 +98,7 @@ impl Database {
|
||||
|
||||
fn list_by_project_id_range(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
range: ops::Range<u128>,
|
||||
) -> Result<Vec<Bookmark>> {
|
||||
self.database.transaction(|tx| {
|
||||
@ -119,7 +119,7 @@ impl Database {
|
||||
})
|
||||
}
|
||||
|
||||
fn list_by_project_id_all(&self, project_id: &str) -> Result<Vec<Bookmark>> {
|
||||
fn list_by_project_id_all(&self, project_id: &ProjectId) -> Result<Vec<Bookmark>> {
|
||||
self.database.transaction(|tx| {
|
||||
let mut stmt = list_by_project_id_stmt(tx)
|
||||
.context("Failed to prepare list_by_project_id statement")?;
|
||||
@ -136,7 +136,7 @@ impl Database {
|
||||
|
||||
pub fn list_by_project_id(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
range: Option<ops::Range<u128>>,
|
||||
) -> Result<Vec<Bookmark>> {
|
||||
if let Some(range) = range {
|
||||
@ -250,7 +250,7 @@ mod tests {
|
||||
let database = Database::from(db);
|
||||
|
||||
let bookmark = Bookmark {
|
||||
project_id: "project_id".to_string(),
|
||||
project_id: ProjectId::generate(),
|
||||
timestamp_ms: 123,
|
||||
created_timestamp_ms: 0,
|
||||
updated_timestamp_ms: 0,
|
||||
@ -274,7 +274,7 @@ mod tests {
|
||||
let database = Database::from(db);
|
||||
|
||||
let bookmark_one = Bookmark {
|
||||
project_id: "project_id".to_string(),
|
||||
project_id: ProjectId::generate(),
|
||||
timestamp_ms: 123,
|
||||
created_timestamp_ms: 0,
|
||||
updated_timestamp_ms: 0,
|
||||
@ -284,7 +284,7 @@ mod tests {
|
||||
database.upsert(&bookmark_one)?;
|
||||
|
||||
let bookmark_two = Bookmark {
|
||||
project_id: "project_id".to_string(),
|
||||
project_id: ProjectId::generate(),
|
||||
timestamp_ms: 456,
|
||||
created_timestamp_ms: 0,
|
||||
updated_timestamp_ms: 1,
|
||||
@ -309,7 +309,7 @@ mod tests {
|
||||
let database = Database::from(db);
|
||||
|
||||
let bookmark = Bookmark {
|
||||
project_id: "project_id".to_string(),
|
||||
project_id: ProjectId::generate(),
|
||||
timestamp_ms: 123,
|
||||
created_timestamp_ms: 0,
|
||||
updated_timestamp_ms: 0,
|
||||
|
@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Bookmark {
|
||||
pub project_id: String,
|
||||
pub project_id: ProjectId,
|
||||
pub timestamp_ms: u128,
|
||||
pub created_timestamp_ms: u128,
|
||||
pub updated_timestamp_ms: u128,
|
||||
@ -18,3 +18,5 @@ pub struct Bookmark {
|
||||
pub use database::Database;
|
||||
pub use reader::BookmarksReader as Reader;
|
||||
pub use writer::BookmarksWriter as Writer;
|
||||
|
||||
use crate::projects::ProjectId;
|
||||
|
@ -24,7 +24,7 @@ impl<'writer> BookmarksWriter<'writer> {
|
||||
)?;
|
||||
|
||||
tracing::debug!(
|
||||
project_id = self.repository.get_project_id(),
|
||||
project_id = %self.repository.get_project_id(),
|
||||
timestamp_ms = bookmark.timestamp_ms,
|
||||
"wrote bookmark",
|
||||
);
|
||||
|
@ -5,7 +5,11 @@ use tauri::Manager;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::{
|
||||
app, assets, bookmarks, deltas, error::Error, git, reader, search, sessions, virtual_branches,
|
||||
app, assets, bookmarks, deltas,
|
||||
error::{Code, Error},
|
||||
git, reader, search,
|
||||
sessions::{self, SessionId},
|
||||
virtual_branches,
|
||||
};
|
||||
|
||||
impl From<app::Error> for Error {
|
||||
@ -50,7 +54,11 @@ pub async fn list_sessions(
|
||||
earliest_timestamp_ms: Option<u128>,
|
||||
) -> Result<Vec<sessions::Session>, Error> {
|
||||
let app = handle.state::<app::App>();
|
||||
let sessions = app.list_sessions(project_id, earliest_timestamp_ms)?;
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let sessions = app.list_sessions(&project_id, earliest_timestamp_ms)?;
|
||||
Ok(sessions)
|
||||
}
|
||||
|
||||
@ -63,7 +71,15 @@ pub async fn list_session_files(
|
||||
paths: Option<Vec<path::PathBuf>>,
|
||||
) -> Result<HashMap<path::PathBuf, reader::Content>, Error> {
|
||||
let app = handle.state::<app::App>();
|
||||
let files = app.list_session_files(project_id, session_id, &paths)?;
|
||||
let session_id: SessionId = session_id.parse().map_err(|_| Error::UserError {
|
||||
message: "Malformed session id".to_string(),
|
||||
code: Code::Sessions,
|
||||
})?;
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let files = app.list_session_files(&project_id, &session_id, &paths)?;
|
||||
Ok(files)
|
||||
}
|
||||
|
||||
@ -76,7 +92,15 @@ pub async fn list_deltas(
|
||||
paths: Option<Vec<&str>>,
|
||||
) -> Result<HashMap<String, Vec<deltas::Delta>>, Error> {
|
||||
let app = handle.state::<app::App>();
|
||||
let deltas = app.list_session_deltas(project_id, session_id, &paths)?;
|
||||
let session_id = session_id.parse().map_err(|_| Error::UserError {
|
||||
message: "Malformed session id".to_string(),
|
||||
code: Code::Sessions,
|
||||
})?;
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let deltas = app.list_session_deltas(&project_id, &session_id, &paths)?;
|
||||
Ok(deltas)
|
||||
}
|
||||
|
||||
@ -88,7 +112,11 @@ pub async fn git_wd_diff(
|
||||
context_lines: u32,
|
||||
) -> Result<HashMap<path::PathBuf, String>, Error> {
|
||||
let app = handle.state::<app::App>();
|
||||
let diff = app.git_wd_diff(project_id, context_lines)?;
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let diff = app.git_wd_diff(&project_id, context_lines)?;
|
||||
Ok(diff)
|
||||
}
|
||||
|
||||
@ -99,7 +127,11 @@ pub async fn git_remote_branches(
|
||||
project_id: &str,
|
||||
) -> Result<Vec<git::RemoteBranchName>, Error> {
|
||||
let app = handle.state::<app::App>();
|
||||
let branches = app.git_remote_branches(project_id)?;
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let branches = app.git_remote_branches(&project_id)?;
|
||||
Ok(branches)
|
||||
}
|
||||
|
||||
@ -110,7 +142,11 @@ pub async fn git_remote_branches_data(
|
||||
project_id: &str,
|
||||
) -> Result<Vec<virtual_branches::RemoteBranch>, Error> {
|
||||
let app = handle.state::<app::App>();
|
||||
let branches = app.git_remote_branches_data(project_id)?;
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let branches = app.git_remote_branches_data(&project_id)?;
|
||||
let branches = handle
|
||||
.state::<assets::Proxy>()
|
||||
.proxy_remote_branches(&branches)
|
||||
@ -122,7 +158,11 @@ pub async fn git_remote_branches_data(
|
||||
#[instrument(skip(handle))]
|
||||
pub async fn git_head(handle: tauri::AppHandle, project_id: &str) -> Result<String, Error> {
|
||||
let app = handle.state::<app::App>();
|
||||
let head = app.git_head(project_id)?;
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let head = app.git_head(&project_id)?;
|
||||
Ok(head)
|
||||
}
|
||||
|
||||
@ -148,6 +188,10 @@ pub async fn upsert_bookmark(
|
||||
.elapsed()
|
||||
.context("failed to get time")?
|
||||
.as_millis();
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let bookmark = bookmarks::Bookmark {
|
||||
project_id,
|
||||
timestamp_ms: timestamp_ms
|
||||
@ -170,7 +214,11 @@ pub async fn list_bookmarks(
|
||||
range: Option<ops::Range<u128>>,
|
||||
) -> Result<Vec<bookmarks::Bookmark>, Error> {
|
||||
let app = handle.state::<app::App>();
|
||||
let bookmarks = app.list_bookmarks(project_id, range)?;
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let bookmarks = app.list_bookmarks(&project_id, range)?;
|
||||
Ok(bookmarks)
|
||||
}
|
||||
|
||||
@ -178,7 +226,11 @@ pub async fn list_bookmarks(
|
||||
#[instrument(skip(handle))]
|
||||
pub async fn fetch_from_target(handle: tauri::AppHandle, project_id: &str) -> Result<(), Error> {
|
||||
let app = handle.state::<app::App>();
|
||||
app.fetch_from_target(project_id)?;
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
app.fetch_from_target(&project_id)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -190,7 +242,11 @@ pub async fn mark_resolved(
|
||||
path: &str,
|
||||
) -> Result<(), Error> {
|
||||
let app = handle.state::<app::App>();
|
||||
app.mark_resolved(project_id, path)?;
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
app.mark_resolved(&project_id, path)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ use std::{collections::HashMap, path};
|
||||
use anyhow::{Context, Result};
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
use crate::database;
|
||||
use crate::{database, projects::ProjectId, sessions::SessionId};
|
||||
|
||||
use super::{delta, operations};
|
||||
|
||||
@ -27,8 +27,8 @@ impl From<&AppHandle> for Database {
|
||||
impl Database {
|
||||
pub fn insert(
|
||||
&self,
|
||||
project_id: &str,
|
||||
session_id: &str,
|
||||
project_id: &ProjectId,
|
||||
session_id: &SessionId,
|
||||
file_path: &path::Path,
|
||||
deltas: &Vec<delta::Delta>,
|
||||
) -> Result<()> {
|
||||
@ -55,8 +55,8 @@ impl Database {
|
||||
|
||||
pub fn list_by_project_id_session_id(
|
||||
&self,
|
||||
project_id: &str,
|
||||
session_id: &str,
|
||||
project_id: &ProjectId,
|
||||
session_id: &SessionId,
|
||||
file_path_filter: &Option<Vec<&str>>,
|
||||
) -> Result<HashMap<String, Vec<delta::Delta>>> {
|
||||
self.database
|
||||
@ -141,8 +141,8 @@ mod tests {
|
||||
let db = test_utils::test_database();
|
||||
let database = Database::from(db);
|
||||
|
||||
let project_id = "project_id";
|
||||
let session_id = "session_id";
|
||||
let project_id = ProjectId::generate();
|
||||
let session_id = SessionId::generate();
|
||||
let file_path = path::PathBuf::from("file_path");
|
||||
let delta1 = delta::Delta {
|
||||
timestamp_ms: 0,
|
||||
@ -150,10 +150,10 @@ mod tests {
|
||||
};
|
||||
let deltas = vec![delta1.clone()];
|
||||
|
||||
database.insert(project_id, session_id, &file_path, &deltas)?;
|
||||
database.insert(&project_id, &session_id, &file_path, &deltas)?;
|
||||
|
||||
assert_eq!(
|
||||
database.list_by_project_id_session_id(project_id, session_id, &None)?,
|
||||
database.list_by_project_id_session_id(&project_id, &session_id, &None)?,
|
||||
vec![(file_path.display().to_string(), vec![delta1])]
|
||||
.into_iter()
|
||||
.collect()
|
||||
@ -167,8 +167,8 @@ mod tests {
|
||||
let db = test_utils::test_database();
|
||||
let database = Database::from(db);
|
||||
|
||||
let project_id = "project_id";
|
||||
let session_id = "session_id";
|
||||
let project_id = ProjectId::generate();
|
||||
let session_id = SessionId::generate();
|
||||
let file_path = path::PathBuf::from("file_path");
|
||||
let delta1 = delta::Delta {
|
||||
timestamp_ms: 0,
|
||||
@ -182,11 +182,11 @@ mod tests {
|
||||
))],
|
||||
};
|
||||
|
||||
database.insert(project_id, session_id, &file_path, &vec![delta1])?;
|
||||
database.insert(project_id, session_id, &file_path, &vec![delta2.clone()])?;
|
||||
database.insert(&project_id, &session_id, &file_path, &vec![delta1])?;
|
||||
database.insert(&project_id, &session_id, &file_path, &vec![delta2.clone()])?;
|
||||
|
||||
assert_eq!(
|
||||
database.list_by_project_id_session_id(project_id, session_id, &None)?,
|
||||
database.list_by_project_id_session_id(&project_id, &session_id, &None)?,
|
||||
vec![(file_path.display().to_string(), vec![delta2])]
|
||||
.into_iter()
|
||||
.collect()
|
||||
@ -200,8 +200,8 @@ mod tests {
|
||||
let db = test_utils::test_database();
|
||||
let database = Database::from(db);
|
||||
|
||||
let project_id = "project_id";
|
||||
let session_id = "session_id";
|
||||
let project_id = ProjectId::generate();
|
||||
let session_id = SessionId::generate();
|
||||
let file_path1 = path::PathBuf::from("file_path1");
|
||||
let file_path2 = path::PathBuf::from("file_path2");
|
||||
let delta1 = delta::Delta {
|
||||
@ -216,12 +216,12 @@ mod tests {
|
||||
))],
|
||||
};
|
||||
|
||||
database.insert(project_id, session_id, &file_path1, &vec![delta1.clone()])?;
|
||||
database.insert(project_id, session_id, &file_path2, &vec![delta1.clone()])?;
|
||||
database.insert(project_id, session_id, &file_path2, &vec![delta2.clone()])?;
|
||||
database.insert(&project_id, &session_id, &file_path1, &vec![delta1.clone()])?;
|
||||
database.insert(&project_id, &session_id, &file_path2, &vec![delta1.clone()])?;
|
||||
database.insert(&project_id, &session_id, &file_path2, &vec![delta2.clone()])?;
|
||||
|
||||
assert_eq!(
|
||||
database.list_by_project_id_session_id(project_id, session_id, &None)?,
|
||||
database.list_by_project_id_session_id(&project_id, &session_id, &None)?,
|
||||
vec![
|
||||
(file_path1.display().to_string(), vec![delta1.clone()]),
|
||||
(file_path2.display().to_string(), vec![delta1, delta2])
|
||||
|
@ -30,7 +30,7 @@ impl<'writer> DeltasWriter<'writer> {
|
||||
.write_string(&format!("session/deltas/{}", path.display()), &raw_deltas)?;
|
||||
|
||||
tracing::debug!(
|
||||
project_id = self.repository.get_project_id(),
|
||||
project_id = %self.repository.get_project_id(),
|
||||
path = %path.display(),
|
||||
"wrote deltas"
|
||||
);
|
||||
@ -48,7 +48,7 @@ impl<'writer> DeltasWriter<'writer> {
|
||||
.write_string(&format!("session/wd/{}", path.display()), contents)?;
|
||||
|
||||
tracing::debug!(
|
||||
project_id = self.repository.get_project_id(),
|
||||
project_id = %self.repository.get_project_id(),
|
||||
path = %path.display(),
|
||||
"wrote session wd file"
|
||||
);
|
||||
|
@ -5,6 +5,8 @@ use serde::{ser::SerializeMap, Serialize};
|
||||
#[derive(Debug)]
|
||||
pub enum Code {
|
||||
Unknown,
|
||||
Sessions,
|
||||
Branches,
|
||||
Projects,
|
||||
ProjectGitAuth,
|
||||
ProjectGitRemote,
|
||||
@ -16,6 +18,8 @@ impl fmt::Display for Code {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Code::Unknown => write!(f, "errors.unknown"),
|
||||
Code::Branches => write!(f, "errors.branches"),
|
||||
Code::Sessions => write!(f, "errors.sessions"),
|
||||
Code::Projects => write!(f, "errors.projects"),
|
||||
Code::ProjectGitAuth => write!(f, "errors.projects.git.auth"),
|
||||
Code::ProjectGitRemote => write!(f, "errors.projects.git.remote"),
|
||||
|
@ -1,7 +1,12 @@
|
||||
use anyhow::{Context, Result};
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
use crate::{bookmarks, deltas, reader, sessions};
|
||||
use crate::{
|
||||
bookmarks, deltas,
|
||||
projects::ProjectId,
|
||||
reader,
|
||||
sessions::{self, SessionId},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Sender {
|
||||
@ -30,7 +35,7 @@ impl Sender {
|
||||
pub struct Event {
|
||||
name: String,
|
||||
payload: serde_json::Value,
|
||||
project_id: String,
|
||||
project_id: ProjectId,
|
||||
}
|
||||
|
||||
impl Event {
|
||||
@ -38,45 +43,45 @@ impl Event {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn project_id(&self) -> &str {
|
||||
pub fn project_id(&self) -> &ProjectId {
|
||||
&self.project_id
|
||||
}
|
||||
|
||||
pub fn git_index(project_id: &str) -> Self {
|
||||
pub fn git_index(project_id: &ProjectId) -> Self {
|
||||
Event {
|
||||
name: format!("project://{}/git/index", project_id),
|
||||
payload: serde_json::json!({}),
|
||||
project_id: project_id.to_string(),
|
||||
project_id: *project_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn git_fetch(project_id: &str) -> Self {
|
||||
pub fn git_fetch(project_id: &ProjectId) -> Self {
|
||||
Event {
|
||||
name: format!("project://{}/git/fetch", project_id),
|
||||
payload: serde_json::json!({}),
|
||||
project_id: project_id.to_string(),
|
||||
project_id: *project_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn git_head(project_id: &str, head: &str) -> Self {
|
||||
pub fn git_head(project_id: &ProjectId, head: &str) -> Self {
|
||||
Event {
|
||||
name: format!("project://{}/git/head", project_id),
|
||||
payload: serde_json::json!({ "head": head }),
|
||||
project_id: project_id.to_string(),
|
||||
project_id: *project_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn git_activity(project_id: &str) -> Self {
|
||||
pub fn git_activity(project_id: &ProjectId) -> Self {
|
||||
Event {
|
||||
name: format!("project://{}/git/activity", project_id),
|
||||
payload: serde_json::json!({}),
|
||||
project_id: project_id.to_string(),
|
||||
project_id: *project_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file(
|
||||
project_id: &str,
|
||||
session_id: &str,
|
||||
project_id: &ProjectId,
|
||||
session_id: &SessionId,
|
||||
file_path: &str,
|
||||
contents: Option<&reader::Content>,
|
||||
) -> Self {
|
||||
@ -86,29 +91,29 @@ impl Event {
|
||||
"filePath": file_path,
|
||||
"contents": contents,
|
||||
}),
|
||||
project_id: project_id.to_string(),
|
||||
project_id: *project_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session(project_id: &str, session: &sessions::Session) -> Self {
|
||||
pub fn session(project_id: &ProjectId, session: &sessions::Session) -> Self {
|
||||
Event {
|
||||
name: format!("project://{}/sessions", project_id),
|
||||
payload: serde_json::to_value(session).unwrap(),
|
||||
project_id: project_id.to_string(),
|
||||
project_id: *project_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bookmark(project_id: &str, bookmark: &bookmarks::Bookmark) -> Self {
|
||||
pub fn bookmark(project_id: &ProjectId, bookmark: &bookmarks::Bookmark) -> Self {
|
||||
Event {
|
||||
name: format!("project://{}/bookmarks", project_id),
|
||||
payload: serde_json::to_value(bookmark).unwrap(),
|
||||
project_id: project_id.to_string(),
|
||||
project_id: *project_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deltas(
|
||||
project_id: &str,
|
||||
session_id: &str,
|
||||
project_id: &ProjectId,
|
||||
session_id: &SessionId,
|
||||
deltas: &Vec<deltas::Delta>,
|
||||
relative_file_path: &std::path::Path,
|
||||
) -> Self {
|
||||
@ -118,7 +123,7 @@ impl Event {
|
||||
"deltas": deltas,
|
||||
"filePath": relative_file_path,
|
||||
}),
|
||||
project_id: project_id.to_string(),
|
||||
project_id: *project_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,12 +9,13 @@ use std::{
|
||||
use anyhow::{anyhow, Context, Ok, Result};
|
||||
use filetime::FileTime;
|
||||
use sha2::{Digest, Sha256};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
fs, git, lock,
|
||||
paths::DataDir,
|
||||
projects, users,
|
||||
projects::{self, ProjectId},
|
||||
sessions::SessionId,
|
||||
users,
|
||||
virtual_branches::{self, target},
|
||||
};
|
||||
|
||||
@ -56,7 +57,7 @@ impl Repository {
|
||||
|
||||
let projects_dir = root.to_path_buf().join("projects");
|
||||
|
||||
let path = projects_dir.join(&project.id);
|
||||
let path = projects_dir.join(project.id.to_string());
|
||||
let lock_path = projects_dir.join(format!("{}.lock", project.id));
|
||||
if path.exists() {
|
||||
let git_repository = git::Repository::open(path.clone())
|
||||
@ -95,7 +96,7 @@ impl Repository {
|
||||
.migrate(project)
|
||||
.context("failed to migrate")?
|
||||
{
|
||||
tracing::info!(project_id = gb_repository.project.id, "repository migrated");
|
||||
tracing::info!(project_id = %gb_repository.project.id, "repository migrated");
|
||||
return Result::Ok(gb_repository);
|
||||
}
|
||||
|
||||
@ -111,7 +112,7 @@ impl Repository {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_project_id(&self) -> &str {
|
||||
pub fn get_project_id(&self) -> &ProjectId {
|
||||
&self.project.id
|
||||
}
|
||||
|
||||
@ -150,7 +151,7 @@ impl Repository {
|
||||
let mut callbacks = git2::RemoteCallbacks::new();
|
||||
callbacks.push_update_reference(move |refname, message| {
|
||||
tracing::debug!(
|
||||
project_id = self.project.id,
|
||||
project_id = %self.project.id,
|
||||
refname,
|
||||
message,
|
||||
"pulling reference"
|
||||
@ -159,7 +160,7 @@ impl Repository {
|
||||
});
|
||||
callbacks.push_transfer_progress(move |one, two, three| {
|
||||
tracing::debug!(
|
||||
project_id = self.project.id,
|
||||
project_id = %self.project.id,
|
||||
"transferred {}/{}/{} objects",
|
||||
one,
|
||||
two,
|
||||
@ -181,7 +182,7 @@ impl Repository {
|
||||
))?;
|
||||
|
||||
tracing::info!(
|
||||
project_id = self.project.id,
|
||||
project_id = %self.project.id,
|
||||
remote = %remote.url()?.unwrap(),
|
||||
"gb repo fetched",
|
||||
);
|
||||
@ -199,7 +200,7 @@ impl Repository {
|
||||
let mut callbacks = git2::RemoteCallbacks::new();
|
||||
callbacks.push_update_reference(move |refname, message| {
|
||||
tracing::debug!(
|
||||
project_id = self.project.id,
|
||||
project_id = %self.project.id,
|
||||
refname,
|
||||
message,
|
||||
"pushing reference"
|
||||
@ -208,7 +209,7 @@ impl Repository {
|
||||
});
|
||||
callbacks.push_transfer_progress(move |one, two, three| {
|
||||
tracing::debug!(
|
||||
project_id = self.project.id,
|
||||
project_id = %self.project.id,
|
||||
"transferred {}/{}/{} objects",
|
||||
one,
|
||||
two,
|
||||
@ -232,7 +233,7 @@ impl Repository {
|
||||
remote.url()?.unwrap()
|
||||
))?;
|
||||
|
||||
tracing::info!(project_id = self.project.id, remote = %remote.url()?.unwrap(), "gb repository pushed");
|
||||
tracing::info!(project_id = %self.project.id, remote = %remote.url()?.unwrap(), "gb repository pushed");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -328,7 +329,7 @@ impl Repository {
|
||||
};
|
||||
|
||||
let session = sessions::Session {
|
||||
id: Uuid::new_v4().to_string(),
|
||||
id: SessionId::generate(),
|
||||
hash: None,
|
||||
meta,
|
||||
};
|
||||
@ -339,8 +340,8 @@ impl Repository {
|
||||
.context("failed to write session")?;
|
||||
|
||||
tracing::info!(
|
||||
project_id = self.project.id,
|
||||
session_id = session.id,
|
||||
project_id = %self.project.id,
|
||||
session_id = %session.id,
|
||||
"created new session"
|
||||
);
|
||||
|
||||
@ -461,8 +462,8 @@ impl Repository {
|
||||
write_gb_commit(tree_id, self, user).context("failed to write gb commit")?;
|
||||
|
||||
tracing::info!(
|
||||
project_id = self.project.id,
|
||||
session_id = session.id,
|
||||
project_id = %self.project.id,
|
||||
session_id = %session.id,
|
||||
%commit_oid,
|
||||
"flushed session"
|
||||
);
|
||||
@ -543,7 +544,7 @@ impl Repository {
|
||||
match reference {
|
||||
Err(git::Error::NotFound(_)) => {
|
||||
tracing::debug!(
|
||||
project_id = project.id,
|
||||
project_id = %project.id,
|
||||
refname,
|
||||
"reference not found, no migration"
|
||||
);
|
||||
@ -596,10 +597,9 @@ impl Repository {
|
||||
}
|
||||
}
|
||||
|
||||
fn flush_gitbutler_file(&self, session_id: &str) -> Result<()> {
|
||||
fn flush_gitbutler_file(&self, session_id: &SessionId) -> Result<()> {
|
||||
let gb_path = self.git_repository.path();
|
||||
let project_id = self.project.id.as_str();
|
||||
|
||||
let project_id = self.project.id.to_string();
|
||||
let gb_file_content = serde_json::json!({
|
||||
"sessionId": session_id,
|
||||
"repositoryId": project_id,
|
||||
@ -651,7 +651,7 @@ fn build_wd_tree(
|
||||
Result::Ok(reader::Content::UTF8(content)) => content,
|
||||
Result::Ok(reader::Content::Large) => {
|
||||
tracing::error!(
|
||||
project_id = gb_repository.project.id,
|
||||
project_id = %gb_repository.project.id,
|
||||
path = %abs_path.display(),
|
||||
"large file in session working directory"
|
||||
);
|
||||
@ -659,7 +659,7 @@ fn build_wd_tree(
|
||||
}
|
||||
Result::Ok(reader::Content::Binary) => {
|
||||
tracing::error!(
|
||||
project_id = gb_repository.project.id,
|
||||
project_id = %gb_repository.project.id,
|
||||
path = %abs_path.display(),
|
||||
"binary file in session working directory"
|
||||
);
|
||||
@ -667,7 +667,7 @@ fn build_wd_tree(
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::error!(
|
||||
project_id = gb_repository.project.id,
|
||||
project_id = %gb_repository.project.id,
|
||||
path = %abs_path.display(),
|
||||
?error,
|
||||
"failed to read file"
|
||||
@ -836,7 +836,7 @@ fn add_wd_path(
|
||||
// TODO: size limit should be configurable
|
||||
let blob = if metadata.len() > 100_000_000 {
|
||||
tracing::warn!(
|
||||
project_id = gb_repository.project.id,
|
||||
project_id = %gb_repository.project.id,
|
||||
path = %file_path.display(),
|
||||
"file too big"
|
||||
);
|
||||
|
@ -5,7 +5,10 @@ use pretty_assertions::assert_eq;
|
||||
use tempfile::tempdir;
|
||||
|
||||
use crate::{
|
||||
deltas, projects, reader, sessions,
|
||||
deltas,
|
||||
projects::{self, ProjectId},
|
||||
reader,
|
||||
sessions::{self, SessionId},
|
||||
test_utils::{Case, Suite},
|
||||
};
|
||||
|
||||
@ -109,13 +112,11 @@ fn test_list_deltas_from_current_session() -> Result<()> {
|
||||
|
||||
assert_eq!(deltas.len(), 1);
|
||||
assert_eq!(
|
||||
deltas.get(&path::PathBuf::from("test.txt")).unwrap()[0]
|
||||
.operations
|
||||
.len(),
|
||||
deltas[&path::PathBuf::from("test.txt")][0].operations.len(),
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
deltas.get(&path::PathBuf::from("test.txt")).unwrap()[0].operations[0],
|
||||
deltas[&path::PathBuf::from("test.txt")][0].operations[0],
|
||||
deltas::Operation::Insert((0, "Hello World".to_string()))
|
||||
);
|
||||
|
||||
@ -146,13 +147,11 @@ fn test_list_deltas_from_flushed_session() -> Result<()> {
|
||||
|
||||
assert_eq!(deltas.len(), 1);
|
||||
assert_eq!(
|
||||
deltas.get(&path::PathBuf::from("test.txt")).unwrap()[0]
|
||||
.operations
|
||||
.len(),
|
||||
deltas[&path::PathBuf::from("test.txt")][0].operations.len(),
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
deltas.get(&path::PathBuf::from("test.txt")).unwrap()[0].operations[0],
|
||||
deltas[&path::PathBuf::from("test.txt")][0].operations[0],
|
||||
deltas::Operation::Insert((0, "Hello World".to_string()))
|
||||
);
|
||||
|
||||
@ -172,8 +171,8 @@ fn test_list_files_from_current_session() -> Result<()> {
|
||||
|
||||
assert_eq!(files.len(), 1);
|
||||
assert_eq!(
|
||||
files.get(&path::PathBuf::from("test.txt")).unwrap(),
|
||||
&reader::Content::UTF8("Hello World".to_string())
|
||||
files[&path::PathBuf::from("test.txt")],
|
||||
reader::Content::UTF8("Hello World".to_string())
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@ -197,8 +196,8 @@ fn test_list_files_from_flushed_session() -> Result<()> {
|
||||
|
||||
assert_eq!(files.len(), 1);
|
||||
assert_eq!(
|
||||
files.get(&path::PathBuf::from("test.txt")).unwrap(),
|
||||
&reader::Content::UTF8("Hello World".to_string())
|
||||
files[&path::PathBuf::from("test.txt")],
|
||||
reader::Content::UTF8("Hello World".to_string())
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@ -213,8 +212,8 @@ fn test_remote_syncronization() -> Result<()> {
|
||||
description: None,
|
||||
repository_id: "123".to_string(),
|
||||
git_url: cloud.path().to_str().unwrap().to_string(),
|
||||
created_at: 0.to_string(),
|
||||
updated_at: 0.to_string(),
|
||||
created_at: 0_i32.to_string(),
|
||||
updated_at: 0_i32.to_string(),
|
||||
sync: true,
|
||||
};
|
||||
|
||||
@ -227,7 +226,7 @@ fn test_remote_syncronization() -> Result<()> {
|
||||
"Hello World",
|
||||
)]));
|
||||
suite.projects.update(&projects::UpdateRequest {
|
||||
id: case_one.project.id.clone(),
|
||||
id: case_one.project.id,
|
||||
api: Some(api_project.clone()),
|
||||
..Default::default()
|
||||
})?;
|
||||
@ -250,7 +249,7 @@ fn test_remote_syncronization() -> Result<()> {
|
||||
// create second local project, fetch it and make sure session is there
|
||||
let case_two = suite.new_case();
|
||||
suite.projects.update(&projects::UpdateRequest {
|
||||
id: case_two.project.id.clone(),
|
||||
id: case_two.project.id,
|
||||
api: Some(api_project.clone()),
|
||||
..Default::default()
|
||||
})?;
|
||||
@ -262,7 +261,7 @@ fn test_remote_syncronization() -> Result<()> {
|
||||
let sessions_two = case_two
|
||||
.gb_repository
|
||||
.get_sessions_iterator()?
|
||||
.map(|s| s.unwrap())
|
||||
.map(Result::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(sessions_two.len(), 1);
|
||||
assert_eq!(sessions_two[0].id, session_one.id);
|
||||
@ -274,12 +273,12 @@ fn test_remote_syncronization() -> Result<()> {
|
||||
assert_eq!(deltas.len(), 1);
|
||||
assert_eq!(files.len(), 1);
|
||||
assert_eq!(
|
||||
files.get(&path::PathBuf::from("test.txt")).unwrap(),
|
||||
&reader::Content::UTF8("Hello World".to_string())
|
||||
files[&path::PathBuf::from("test.txt")],
|
||||
reader::Content::UTF8("Hello World".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
deltas.get(&path::PathBuf::from("test.txt")).unwrap(),
|
||||
&vec![deltas::Delta {
|
||||
deltas[&path::PathBuf::from("test.txt")],
|
||||
vec![deltas::Delta {
|
||||
operations: vec![deltas::Operation::Insert((0, "Hello World".to_string()))],
|
||||
timestamp_ms: 0,
|
||||
}]
|
||||
@ -297,8 +296,8 @@ fn test_remote_sync_order() -> Result<()> {
|
||||
description: None,
|
||||
repository_id: "123".to_string(),
|
||||
git_url: cloud.path().to_str().unwrap().to_string(),
|
||||
created_at: 0.to_string(),
|
||||
updated_at: 0.to_string(),
|
||||
created_at: 0_i32.to_string(),
|
||||
updated_at: 0_i32.to_string(),
|
||||
sync: true,
|
||||
};
|
||||
|
||||
@ -306,7 +305,7 @@ fn test_remote_sync_order() -> Result<()> {
|
||||
|
||||
let case_one = suite.new_case();
|
||||
suite.projects.update(&projects::UpdateRequest {
|
||||
id: case_one.project.id.clone(),
|
||||
id: case_one.project.id,
|
||||
api: Some(api_project.clone()),
|
||||
..Default::default()
|
||||
})?;
|
||||
@ -314,7 +313,7 @@ fn test_remote_sync_order() -> Result<()> {
|
||||
|
||||
let case_two = suite.new_case();
|
||||
suite.projects.update(&projects::UpdateRequest {
|
||||
id: case_two.project.id.clone(),
|
||||
id: case_two.project.id,
|
||||
api: Some(api_project.clone()),
|
||||
..Default::default()
|
||||
})?;
|
||||
@ -364,14 +363,14 @@ fn test_remote_sync_order() -> Result<()> {
|
||||
let sessions_one = case_one
|
||||
.gb_repository
|
||||
.get_sessions_iterator()?
|
||||
.map(|s| s.unwrap())
|
||||
.map(Result::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
case_two.gb_repository.fetch(Some(&user))?;
|
||||
let sessions_two = case_two
|
||||
.gb_repository
|
||||
.get_sessions_iterator()?
|
||||
.map(|s| s.unwrap())
|
||||
.map(Result::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// make sure the sessions are the same on both repos
|
||||
@ -401,12 +400,11 @@ fn test_gitbutler_file() -> Result<()> {
|
||||
|
||||
let file_content: serde_json::Value =
|
||||
serde_json::from_str(&std::fs::read_to_string(&gitbutler_file_path)?)?;
|
||||
let sid: SessionId = file_content["sessionId"].as_str().unwrap().parse()?;
|
||||
assert_eq!(sid, session.id);
|
||||
|
||||
assert_eq!(file_content["sessionId"], session.id);
|
||||
assert_eq!(
|
||||
file_content["repositoryId"],
|
||||
project_repository.project().id
|
||||
);
|
||||
let pid: ProjectId = file_content["repositoryId"].as_str().unwrap().parse()?;
|
||||
assert_eq!(pid, project_repository.project().id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
110
packages/tauri/src/id.rs
Normal file
110
packages/tauri/src/id.rs
Normal file
@ -0,0 +1,110 @@
|
||||
use std::{fmt, hash::Hash, marker::PhantomData, str};
|
||||
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct Id<T>(Uuid, PhantomData<T>);
|
||||
|
||||
impl<T> Hash for Id<T> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialOrd for Id<T> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Ord for Id<T> {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.0.cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Id<T> {
|
||||
pub fn generate() -> Self {
|
||||
Id(Uuid::new_v4(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Id<T> {
|
||||
fn default() -> Self {
|
||||
Self::generate()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> rusqlite::types::FromSql for Id<T> {
|
||||
fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
|
||||
Uuid::parse_str(value.as_str()?)
|
||||
.map(Into::into)
|
||||
.map_err(|error| rusqlite::types::FromSqlError::Other(Box::new(error)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> rusqlite::ToSql for Id<T> {
|
||||
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
|
||||
Ok(rusqlite::types::ToSqlOutput::from(self.0.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for Id<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.eq(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for Id<T> {}
|
||||
|
||||
impl<T> From<Uuid> for Id<T> {
|
||||
fn from(value: Uuid) -> Self {
|
||||
Self(value, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T> Deserialize<'de> for Id<T> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Uuid::deserialize(deserializer).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Serialize for Id<T> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Id<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Display for Id<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Id<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for Id<T> {}
|
||||
|
||||
impl<T> str::FromStr for Id<T> {
|
||||
type Err = uuid::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Uuid::parse_str(s).map(Into::into)
|
||||
}
|
||||
}
|
@ -98,6 +98,7 @@ pub mod fs;
|
||||
pub mod gb_repository;
|
||||
pub mod git;
|
||||
pub mod github;
|
||||
pub mod id;
|
||||
pub mod keys;
|
||||
pub mod lock;
|
||||
pub mod logs;
|
||||
|
@ -257,7 +257,7 @@ impl Repository {
|
||||
) {
|
||||
Ok(()) => {
|
||||
tracing::info!(
|
||||
project_id = self.project.id,
|
||||
project_id = %self.project.id,
|
||||
remote = %branch.remote(),
|
||||
%head,
|
||||
branch = branch.branch(),
|
||||
@ -266,7 +266,7 @@ impl Repository {
|
||||
return Ok(());
|
||||
}
|
||||
Err(git::Error::AuthenticationFailed(error)) => {
|
||||
tracing::error!(project_id = self.project.id, ?error, "git push failed",);
|
||||
tracing::error!(project_id = %self.project.id, ?error, "git push failed",);
|
||||
continue;
|
||||
}
|
||||
Err(error) => return Err(RemoteError::Other(error.into())),
|
||||
@ -285,7 +285,7 @@ impl Repository {
|
||||
remote_callbacks.push_update_reference(|refname, message| {
|
||||
if let Some(msg) = message {
|
||||
tracing::debug!(
|
||||
project_id = self.project.id,
|
||||
project_id = %self.project.id,
|
||||
refname,
|
||||
msg,
|
||||
"push update reference",
|
||||
@ -295,7 +295,7 @@ impl Repository {
|
||||
});
|
||||
remote_callbacks.push_negotiation(|proposals| {
|
||||
tracing::debug!(
|
||||
project_id = self.project.id,
|
||||
project_id = %self.project.id,
|
||||
proposals = proposals
|
||||
.iter()
|
||||
.map(|p| format!(
|
||||
@ -311,7 +311,7 @@ impl Repository {
|
||||
});
|
||||
remote_callbacks.push_transfer_progress(|one, two, three| {
|
||||
tracing::debug!(
|
||||
project_id = self.project.id,
|
||||
project_id = %self.project.id,
|
||||
"push transfer progress: {}/{}/{}",
|
||||
one,
|
||||
two,
|
||||
@ -327,11 +327,11 @@ impl Repository {
|
||||
|
||||
match remote.fetch(&[refspec], Some(&mut fetch_opts)) {
|
||||
Ok(()) => {
|
||||
tracing::info!(project_id = self.project.id, %refspec, "git fetched");
|
||||
tracing::info!(project_id = %self.project.id, %refspec, "git fetched");
|
||||
return Ok(());
|
||||
}
|
||||
Err(git::Error::AuthenticationFailed(error)) => {
|
||||
tracing::error!(project_id = self.project.id, ?error, "fetch failed");
|
||||
tracing::error!(project_id = %self.project.id, ?error, "fetch failed");
|
||||
continue;
|
||||
}
|
||||
Err(error) => return Err(RemoteError::Other(error.into())),
|
||||
@ -373,7 +373,7 @@ impl Repository {
|
||||
)?;
|
||||
|
||||
tracing::info!(
|
||||
project_id = self.project.id,
|
||||
project_id = %self.project.id,
|
||||
%commit_oid,
|
||||
message,
|
||||
"created commit"
|
||||
|
@ -91,7 +91,11 @@ impl From<controller::GetError> for Error {
|
||||
#[tauri::command(async)]
|
||||
#[instrument(skip(handle))]
|
||||
pub async fn get_project(handle: tauri::AppHandle, id: &str) -> Result<projects::Project, Error> {
|
||||
handle.state::<Controller>().get(id).map_err(Into::into)
|
||||
let id = id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".into(),
|
||||
})?;
|
||||
handle.state::<Controller>().get(&id).map_err(Into::into)
|
||||
}
|
||||
|
||||
impl From<controller::ListError> for Error {
|
||||
@ -125,5 +129,9 @@ impl From<controller::DeleteError> for Error {
|
||||
#[tauri::command(async)]
|
||||
#[instrument(skip(handle))]
|
||||
pub async fn delete_project(handle: tauri::AppHandle, id: &str) -> Result<(), Error> {
|
||||
handle.state::<Controller>().delete(id).map_err(Into::into)
|
||||
let id = id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".into(),
|
||||
})?;
|
||||
handle.state::<Controller>().delete(&id).map_err(Into::into)
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use tauri::{AppHandle, Manager};
|
||||
|
||||
use crate::{paths::DataDir, watcher};
|
||||
|
||||
use super::{storage, storage::UpdateRequest, Project};
|
||||
use super::{storage, storage::UpdateRequest, Project, ProjectId};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Controller {
|
||||
@ -65,7 +65,7 @@ impl Controller {
|
||||
.map_or_else(|| id.clone(), |p| p.to_str().unwrap().to_string());
|
||||
|
||||
let project = Project {
|
||||
id: uuid::Uuid::new_v4().to_string(),
|
||||
id: ProjectId::generate(),
|
||||
title,
|
||||
path: path.to_path_buf(),
|
||||
api: None,
|
||||
@ -96,11 +96,11 @@ impl Controller {
|
||||
if let Some(api) = &project.api {
|
||||
if api.sync {
|
||||
if let Err(error) = block_on(watchers.post(watcher::Event::FetchGitbutlerData(
|
||||
project.id.clone(),
|
||||
project.id,
|
||||
time::SystemTime::now(),
|
||||
))) {
|
||||
tracing::error!(
|
||||
project_id = &project.id,
|
||||
project_id = %project.id,
|
||||
?error,
|
||||
"failed to post fetch project event"
|
||||
);
|
||||
@ -108,10 +108,10 @@ impl Controller {
|
||||
}
|
||||
|
||||
if let Err(error) =
|
||||
block_on(watchers.post(watcher::Event::PushGitbutlerData(project.id.clone())))
|
||||
block_on(watchers.post(watcher::Event::PushGitbutlerData(project.id)))
|
||||
{
|
||||
tracing::error!(
|
||||
project_id = &project.id,
|
||||
project_id = %project.id,
|
||||
?error,
|
||||
"failed to post push project event"
|
||||
);
|
||||
@ -122,7 +122,7 @@ impl Controller {
|
||||
Ok(updated)
|
||||
}
|
||||
|
||||
pub fn get(&self, id: &str) -> Result<Project, GetError> {
|
||||
pub fn get(&self, id: &ProjectId) -> Result<Project, GetError> {
|
||||
self.projects_storage.get(id).map_err(|error| match error {
|
||||
super::storage::Error::NotFound => GetError::NotFound,
|
||||
error => GetError::Other(error.into()),
|
||||
@ -135,7 +135,7 @@ impl Controller {
|
||||
.map_err(|error| ListError::Other(error.into()))
|
||||
}
|
||||
|
||||
pub fn delete(&self, id: &str) -> Result<(), DeleteError> {
|
||||
pub fn delete(&self, id: &ProjectId) -> Result<(), DeleteError> {
|
||||
let project = match self.projects_storage.get(id) {
|
||||
Ok(project) => Ok(project),
|
||||
Err(super::storage::Error::NotFound) => return Ok(()),
|
||||
@ -145,7 +145,7 @@ impl Controller {
|
||||
if let Some(watchers) = &self.watchers {
|
||||
if let Err(error) = block_on(watchers.stop(id)) {
|
||||
tracing::error!(
|
||||
project_id = id,
|
||||
project_id = %id,
|
||||
?error,
|
||||
"failed to stop watcher for project",
|
||||
);
|
||||
@ -160,9 +160,9 @@ impl Controller {
|
||||
self.local_data_dir
|
||||
.to_path_buf()
|
||||
.join("projects")
|
||||
.join(&project.id),
|
||||
.join(project.id.to_string()),
|
||||
) {
|
||||
tracing::error!(project_id = id, ?error, "failed to remove project data",);
|
||||
tracing::error!(project_id = %id, ?error, "failed to remove project data",);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -4,5 +4,5 @@ mod project;
|
||||
mod storage;
|
||||
|
||||
pub use controller::*;
|
||||
pub use project::{ApiProject, AuthKey, FetchResult, Project};
|
||||
pub use project::{ApiProject, AuthKey, FetchResult, Project, ProjectId};
|
||||
pub use storage::UpdateRequest;
|
||||
|
@ -3,6 +3,8 @@ use std::{path, time};
|
||||
use anyhow::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::id::Id;
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum AuthKey {
|
||||
@ -78,9 +80,11 @@ impl FetchResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub type ProjectId = Id<Project>;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
|
||||
pub struct Project {
|
||||
pub id: String,
|
||||
pub id: ProjectId,
|
||||
pub title: String,
|
||||
pub description: Option<String>,
|
||||
pub path: path::PathBuf,
|
||||
|
@ -1,7 +1,11 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
use crate::{paths::DataDir, projects::project, storage};
|
||||
use crate::{
|
||||
paths::DataDir,
|
||||
projects::{project, ProjectId},
|
||||
storage,
|
||||
};
|
||||
|
||||
const PROJECTS_FILE: &str = "projects.json";
|
||||
|
||||
@ -32,7 +36,7 @@ impl From<&AppHandle> for Storage {
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||
pub struct UpdateRequest {
|
||||
pub id: String,
|
||||
pub id: ProjectId,
|
||||
pub title: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub api: Option<project::ApiProject>,
|
||||
@ -74,9 +78,9 @@ impl Storage {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, id: &str) -> Result<project::Project, Error> {
|
||||
pub fn get(&self, id: &ProjectId) -> Result<project::Project, Error> {
|
||||
let projects = self.list()?;
|
||||
match projects.into_iter().find(|p| p.id == id) {
|
||||
match projects.into_iter().find(|p| p.id == *id) {
|
||||
Some(project) => Ok(project),
|
||||
None => Err(Error::NotFound),
|
||||
}
|
||||
@ -125,9 +129,9 @@ impl Storage {
|
||||
.clone())
|
||||
}
|
||||
|
||||
pub fn purge(&self, id: &str) -> Result<(), Error> {
|
||||
pub fn purge(&self, id: &ProjectId) -> Result<(), Error> {
|
||||
let mut projects = self.list()?;
|
||||
if let Some(index) = projects.iter().position(|p| p.id == id) {
|
||||
if let Some(index) = projects.iter().position(|p| p.id == *id) {
|
||||
projects.remove(index);
|
||||
self.storage
|
||||
.write(PROJECTS_FILE, &serde_json::to_string_pretty(&projects)?)?;
|
||||
|
@ -3,14 +3,16 @@ use tantivy::{
|
||||
Document,
|
||||
};
|
||||
|
||||
use crate::{projects::ProjectId, sessions::SessionId};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct IndexDocument {
|
||||
pub version: u64,
|
||||
pub timestamp_ms: Option<u64>,
|
||||
pub index: Option<u64>,
|
||||
pub id: String,
|
||||
pub project_id: Option<String>,
|
||||
pub session_id: Option<String>,
|
||||
pub project_id: Option<ProjectId>,
|
||||
pub session_id: Option<SessionId>,
|
||||
pub file_path: Option<String>,
|
||||
pub diff: Option<String>,
|
||||
pub note: Option<String>,
|
||||
@ -67,10 +69,10 @@ impl IndexDocument {
|
||||
.to_string();
|
||||
let project_id = doc
|
||||
.get_first(schema.get_field("project_id").unwrap())
|
||||
.map(|v| v.as_text().unwrap().to_string());
|
||||
.map(|v| v.as_text().unwrap().parse().unwrap());
|
||||
let session_id = doc
|
||||
.get_first(schema.get_field("session_id").unwrap())
|
||||
.map(|v| v.as_text().unwrap().to_string());
|
||||
.map(|v| v.as_text().unwrap().parse().unwrap());
|
||||
let file_path = doc
|
||||
.get_first(schema.get_field("file_path").unwrap())
|
||||
.map(|v| v.as_text().unwrap().to_string());
|
||||
|
@ -2,7 +2,7 @@ use std::path;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::{paths::DataDir, storage};
|
||||
use crate::{paths::DataDir, projects::ProjectId, sessions::SessionId, storage};
|
||||
|
||||
use super::index;
|
||||
|
||||
@ -29,12 +29,12 @@ impl Storage {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get(&self, project_id: &str, session_hash: &str) -> Result<Option<u64>> {
|
||||
pub fn get(&self, project_id: &ProjectId, session_id: &SessionId) -> Result<Option<u64>> {
|
||||
let filepath = path::Path::new("indexes")
|
||||
.join(format!("v{}", index::VERSION))
|
||||
.join("meta")
|
||||
.join(project_id)
|
||||
.join(session_hash);
|
||||
.join(project_id.to_string())
|
||||
.join(session_id.to_string());
|
||||
let meta = match self.storage.read(filepath.to_str().unwrap())? {
|
||||
None => None,
|
||||
Some(meta) => meta.parse::<u64>().ok(),
|
||||
@ -42,12 +42,12 @@ impl Storage {
|
||||
Ok(meta)
|
||||
}
|
||||
|
||||
pub fn set(&self, project_id: &str, session_hash: &str, version: u64) -> Result<()> {
|
||||
pub fn set(&self, project_id: &ProjectId, session_id: &SessionId, version: u64) -> Result<()> {
|
||||
let filepath = path::Path::new("indexes")
|
||||
.join(format!("v{}", index::VERSION))
|
||||
.join("meta")
|
||||
.join(project_id)
|
||||
.join(session_hash);
|
||||
.join(project_id.to_string())
|
||||
.join(session_id.to_string());
|
||||
self.storage.write(filepath, &version.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
fs, path,
|
||||
sync::{Arc, RwLock},
|
||||
time, vec,
|
||||
@ -7,14 +8,19 @@ use std::{
|
||||
use anyhow::{Context, Result};
|
||||
use serde::Serialize;
|
||||
use similar::{ChangeTag, TextDiff};
|
||||
use std::cmp::Ordering;
|
||||
use tantivy::query::TermQuery;
|
||||
use tantivy::{collector, directory::MmapDirectory, IndexWriter};
|
||||
use tantivy::{query::QueryParser, Term};
|
||||
use tantivy::{schema::IndexRecordOption, tokenizer};
|
||||
use tauri::AppHandle;
|
||||
|
||||
use crate::{bookmarks, deltas, gb_repository, paths::DataDir, reader, sessions};
|
||||
use crate::{
|
||||
bookmarks, deltas, gb_repository,
|
||||
paths::DataDir,
|
||||
projects::ProjectId,
|
||||
reader,
|
||||
sessions::{self, SessionId},
|
||||
};
|
||||
|
||||
use super::{index, meta};
|
||||
|
||||
@ -241,7 +247,7 @@ impl SearcherInner {
|
||||
self.reader.reload()?;
|
||||
|
||||
tracing::debug!(
|
||||
project_id = bookmark.project_id,
|
||||
project_id = %bookmark.project_id,
|
||||
timestamp_ms = bookmark.timestamp_ms,
|
||||
"bookmark added to search",
|
||||
);
|
||||
@ -279,8 +285,8 @@ impl SearcherInner {
|
||||
.set(repository.get_project_id(), &session.id, index::VERSION)?;
|
||||
|
||||
tracing::debug!(
|
||||
project_id = repository.get_project_id(),
|
||||
session_id = session.id,
|
||||
project_id = %repository.get_project_id(),
|
||||
session_id = %session.id,
|
||||
"session added to search",
|
||||
);
|
||||
|
||||
@ -288,7 +294,7 @@ impl SearcherInner {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_id(project_id: &str, timestamp_ms: &u128) -> String {
|
||||
fn build_id(project_id: &ProjectId, timestamp_ms: &u128) -> String {
|
||||
format!("{}-{}-{}", index::VERSION, project_id, timestamp_ms)
|
||||
}
|
||||
|
||||
@ -297,8 +303,8 @@ const WRITE_BUFFER_SIZE: usize = 10_000_000; // 10MB
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SearchResult {
|
||||
pub project_id: String,
|
||||
pub session_id: String,
|
||||
pub project_id: ProjectId,
|
||||
pub session_id: SessionId,
|
||||
pub file_path: String,
|
||||
pub index: u64,
|
||||
}
|
||||
@ -389,8 +395,8 @@ fn index_delta(
|
||||
index: &tantivy::Index,
|
||||
writer: &mut IndexWriter,
|
||||
reader: &tantivy::IndexReader,
|
||||
session_id: &str,
|
||||
project_id: &str,
|
||||
session_id: &SessionId,
|
||||
project_id: &ProjectId,
|
||||
file_text: &mut Vec<char>,
|
||||
file_path: &path::Path,
|
||||
i: usize,
|
||||
@ -439,9 +445,9 @@ fn index_delta(
|
||||
.join(" ");
|
||||
|
||||
doc.index = Some(i.try_into()?);
|
||||
doc.session_id = Some(session_id.to_string());
|
||||
doc.session_id = Some(*session_id);
|
||||
doc.file_path = Some(file_path.display().to_string());
|
||||
doc.project_id = Some(project_id.to_string());
|
||||
doc.project_id = Some(*project_id);
|
||||
doc.timestamp_ms = Some(delta.timestamp_ms.try_into()?);
|
||||
doc.diff = Some(changes);
|
||||
|
||||
|
@ -4,6 +4,7 @@ use anyhow::Result;
|
||||
|
||||
use crate::{
|
||||
bookmarks, deltas,
|
||||
projects::ProjectId,
|
||||
test_utils::{Case, Suite},
|
||||
};
|
||||
|
||||
@ -66,7 +67,7 @@ fn search_by_bookmark_note() -> Result<()> {
|
||||
Path::new("test.txt"),
|
||||
&vec![deltas::Delta {
|
||||
operations: vec![deltas::Operation::Insert((0, "Hello".to_string()))],
|
||||
timestamp_ms: 123456,
|
||||
timestamp_ms: 123_456,
|
||||
}],
|
||||
)?;
|
||||
let session = gb_repository.flush(&project_repository, None)?.unwrap();
|
||||
@ -75,8 +76,8 @@ fn search_by_bookmark_note() -> Result<()> {
|
||||
|
||||
// first we index bookmark
|
||||
searcher.index_bookmark(&bookmarks::Bookmark {
|
||||
project_id: gb_repository.get_project_id().to_string(),
|
||||
timestamp_ms: 123456,
|
||||
project_id: *gb_repository.get_project_id(),
|
||||
timestamp_ms: 123_456,
|
||||
created_timestamp_ms: 0,
|
||||
updated_timestamp_ms: time::UNIX_EPOCH.elapsed()?.as_millis(),
|
||||
note: "bookmark note".to_string(),
|
||||
@ -114,8 +115,8 @@ fn search_by_bookmark_note() -> Result<()> {
|
||||
|
||||
// then update the note
|
||||
searcher.index_bookmark(&bookmarks::Bookmark {
|
||||
project_id: gb_repository.get_project_id().to_string(),
|
||||
timestamp_ms: 123456,
|
||||
project_id: *gb_repository.get_project_id(),
|
||||
timestamp_ms: 123_456,
|
||||
created_timestamp_ms: 0,
|
||||
updated_timestamp_ms: time::UNIX_EPOCH.elapsed()?.as_millis(),
|
||||
note: "updated bookmark note".to_string(),
|
||||
@ -218,7 +219,7 @@ fn search_by_diff() -> Result<()> {
|
||||
})?;
|
||||
assert_eq!(result.total, 1);
|
||||
assert_eq!(result.page[0].session_id, session.id);
|
||||
assert_eq!(result.page[0].project_id, gb_repository.get_project_id());
|
||||
assert_eq!(result.page[0].project_id, *gb_repository.get_project_id());
|
||||
assert_eq!(result.page[0].file_path, "test.txt");
|
||||
assert_eq!(result.page[0].index, 1);
|
||||
|
||||
@ -229,11 +230,12 @@ fn search_by_diff() -> Result<()> {
|
||||
fn should_index_bookmark_once() -> Result<()> {
|
||||
let suite = Suite::default();
|
||||
let searcher = super::Searcher::try_from(&suite.local_app_data).unwrap();
|
||||
let project_id = ProjectId::generate();
|
||||
|
||||
// should not index deleted non-existing bookmark
|
||||
assert!(searcher
|
||||
.index_bookmark(&bookmarks::Bookmark {
|
||||
project_id: "test".to_string(),
|
||||
project_id,
|
||||
timestamp_ms: 0,
|
||||
created_timestamp_ms: 0,
|
||||
updated_timestamp_ms: time::UNIX_EPOCH.elapsed()?.as_millis(),
|
||||
@ -245,7 +247,7 @@ fn should_index_bookmark_once() -> Result<()> {
|
||||
// should index new non deleted bookmark
|
||||
assert!(searcher
|
||||
.index_bookmark(&bookmarks::Bookmark {
|
||||
project_id: "test".to_string(),
|
||||
project_id,
|
||||
timestamp_ms: 0,
|
||||
created_timestamp_ms: 0,
|
||||
updated_timestamp_ms: time::UNIX_EPOCH.elapsed()?.as_millis(),
|
||||
@ -257,7 +259,7 @@ fn should_index_bookmark_once() -> Result<()> {
|
||||
// should not index existing non deleted bookmark
|
||||
assert!(searcher
|
||||
.index_bookmark(&bookmarks::Bookmark {
|
||||
project_id: "test".to_string(),
|
||||
project_id,
|
||||
timestamp_ms: 0,
|
||||
created_timestamp_ms: 0,
|
||||
updated_timestamp_ms: time::UNIX_EPOCH.elapsed()?.as_millis(),
|
||||
@ -269,7 +271,7 @@ fn should_index_bookmark_once() -> Result<()> {
|
||||
// should index existing deleted bookmark
|
||||
assert!(searcher
|
||||
.index_bookmark(&bookmarks::Bookmark {
|
||||
project_id: "test".to_string(),
|
||||
project_id,
|
||||
timestamp_ms: 0,
|
||||
created_timestamp_ms: 0,
|
||||
updated_timestamp_ms: time::UNIX_EPOCH.elapsed()?.as_millis(),
|
||||
@ -281,7 +283,7 @@ fn should_index_bookmark_once() -> Result<()> {
|
||||
// should not index existing deleted bookmark
|
||||
assert!(searcher
|
||||
.index_bookmark(&bookmarks::Bookmark {
|
||||
project_id: "test".to_string(),
|
||||
project_id,
|
||||
timestamp_ms: 0,
|
||||
created_timestamp_ms: 0,
|
||||
updated_timestamp_ms: time::UNIX_EPOCH.elapsed()?.as_millis(),
|
||||
@ -361,7 +363,7 @@ fn search_bookmark_by_phrase() -> Result<()> {
|
||||
|
||||
searcher.index_session(&gb_repository, &session)?;
|
||||
searcher.index_bookmark(&bookmarks::Bookmark {
|
||||
project_id: gb_repository.get_project_id().to_string(),
|
||||
project_id: *gb_repository.get_project_id(),
|
||||
timestamp_ms: 0,
|
||||
created_timestamp_ms: 0,
|
||||
updated_timestamp_ms: time::UNIX_EPOCH.elapsed()?.as_millis(),
|
||||
@ -428,7 +430,7 @@ fn search_by_filename() -> Result<()> {
|
||||
.page;
|
||||
assert_eq!(found_result.len(), 2);
|
||||
assert_eq!(found_result[0].session_id, session.id);
|
||||
assert_eq!(found_result[0].project_id, gb_repository.get_project_id());
|
||||
assert_eq!(found_result[0].project_id, *gb_repository.get_project_id());
|
||||
assert_eq!(found_result[0].file_path, "test.txt");
|
||||
|
||||
let not_found_result = searcher.search(&super::Query {
|
||||
|
@ -1,9 +1,9 @@
|
||||
use anyhow::{Context, Result};
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
use crate::database;
|
||||
use crate::{database, projects::ProjectId};
|
||||
|
||||
use super::session;
|
||||
use super::session::{self, SessionId};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Database {
|
||||
@ -25,7 +25,7 @@ impl From<&AppHandle> for Database {
|
||||
}
|
||||
|
||||
impl Database {
|
||||
pub fn insert(&self, project_id: &str, sessions: &[&session::Session]) -> Result<()> {
|
||||
pub fn insert(&self, project_id: &ProjectId, sessions: &[&session::Session]) -> Result<()> {
|
||||
self.database.transaction(|tx| -> Result<()> {
|
||||
let mut stmt = insert_stmt(tx).context("Failed to prepare insert statement")?;
|
||||
for session in sessions {
|
||||
@ -48,7 +48,7 @@ impl Database {
|
||||
|
||||
pub fn list_by_project_id(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
earliest_timestamp_ms: Option<u128>,
|
||||
) -> Result<Vec<session::Session>> {
|
||||
self.database.transaction(|tx| {
|
||||
@ -81,8 +81,8 @@ impl Database {
|
||||
|
||||
pub fn get_by_project_id_id(
|
||||
&self,
|
||||
project_id: &str,
|
||||
id: &str,
|
||||
project_id: &ProjectId,
|
||||
id: &SessionId,
|
||||
) -> Result<Option<session::Session>> {
|
||||
self.database.transaction(|tx| {
|
||||
let mut stmt = get_by_project_id_id_stmt(tx)
|
||||
@ -104,7 +104,7 @@ impl Database {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_by_id(&self, id: &str) -> Result<Option<session::Session>> {
|
||||
pub fn get_by_id(&self, id: &SessionId) -> Result<Option<session::Session>> {
|
||||
self.database.transaction(|tx| {
|
||||
let mut stmt = get_by_id_stmt(tx).context("Failed to prepare get_by_id statement")?;
|
||||
let mut rows = stmt
|
||||
@ -199,9 +199,9 @@ mod tests {
|
||||
let db = test_utils::test_database();
|
||||
let database = Database::from(db);
|
||||
|
||||
let project_id = "project_id";
|
||||
let project_id = ProjectId::generate();
|
||||
let session1 = session::Session {
|
||||
id: "id1".to_string(),
|
||||
id: SessionId::generate(),
|
||||
hash: None,
|
||||
meta: session::Meta {
|
||||
branch: None,
|
||||
@ -211,7 +211,7 @@ mod tests {
|
||||
},
|
||||
};
|
||||
let session2 = session::Session {
|
||||
id: "id2".to_string(),
|
||||
id: SessionId::generate(),
|
||||
hash: Some("hash2".to_string()),
|
||||
meta: session::Meta {
|
||||
branch: Some("branch2".to_string()),
|
||||
@ -222,15 +222,15 @@ mod tests {
|
||||
};
|
||||
let sessions = vec![&session1, &session2];
|
||||
|
||||
database.insert(project_id, &sessions)?;
|
||||
database.insert(&project_id, &sessions)?;
|
||||
|
||||
assert_eq!(
|
||||
database.list_by_project_id(project_id, None)?,
|
||||
database.list_by_project_id(&project_id, None)?,
|
||||
vec![session2.clone(), session1.clone()]
|
||||
);
|
||||
assert_eq!(database.get_by_id("id1")?.unwrap(), session1);
|
||||
assert_eq!(database.get_by_id("id2")?.unwrap(), session2);
|
||||
assert_eq!(database.get_by_id("id3")?, None);
|
||||
assert_eq!(database.get_by_id(&session1.id)?.unwrap(), session1);
|
||||
assert_eq!(database.get_by_id(&session2.id)?.unwrap(), session2);
|
||||
assert_eq!(database.get_by_id(&SessionId::generate())?, None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -240,9 +240,9 @@ mod tests {
|
||||
let db = test_utils::test_database();
|
||||
let database = Database::from(db);
|
||||
|
||||
let project_id = "project_id";
|
||||
let session1 = session::Session {
|
||||
id: "id1".to_string(),
|
||||
let project_id = ProjectId::generate();
|
||||
let session = session::Session {
|
||||
id: SessionId::generate(),
|
||||
hash: None,
|
||||
meta: session::Meta {
|
||||
branch: None,
|
||||
@ -252,7 +252,7 @@ mod tests {
|
||||
},
|
||||
};
|
||||
let session_updated = session::Session {
|
||||
id: "id1".to_string(),
|
||||
id: session.id,
|
||||
hash: Some("hash2".to_string()),
|
||||
meta: session::Meta {
|
||||
branch: Some("branch2".to_string()),
|
||||
@ -261,14 +261,14 @@ mod tests {
|
||||
last_timestamp_ms: 4,
|
||||
},
|
||||
};
|
||||
database.insert(project_id, &[&session1])?;
|
||||
database.insert(project_id, &[&session_updated])?;
|
||||
database.insert(&project_id, &[&session])?;
|
||||
database.insert(&project_id, &[&session_updated])?;
|
||||
|
||||
assert_eq!(
|
||||
database.list_by_project_id(project_id, None)?,
|
||||
database.list_by_project_id(&project_id, None)?,
|
||||
vec![session_updated.clone()]
|
||||
);
|
||||
assert_eq!(database.get_by_id("id1")?.unwrap(), session_updated);
|
||||
assert_eq!(database.get_by_id(&session.id)?.unwrap(), session_updated);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -10,5 +10,5 @@ mod tests;
|
||||
pub use database::Database;
|
||||
pub use iterator::SessionsIterator;
|
||||
pub use reader::SessionReader as Reader;
|
||||
pub use session::{Meta, Session, SessionError};
|
||||
pub use session::{Meta, Session, SessionError, SessionId};
|
||||
pub use writer::SessionWriter as Writer;
|
||||
|
@ -41,7 +41,7 @@ impl<'reader> SessionReader<'reader> {
|
||||
if let Ok(reader::Content::UTF8(current_session_id)) =
|
||||
wd_reader.read(&repository.session_path().join("meta").join("id"))
|
||||
{
|
||||
if current_session_id == session.id {
|
||||
if current_session_id == session.id.to_string() {
|
||||
let head_commit = repository.git_repository.head()?.peel_to_commit()?;
|
||||
return Ok(SessionReader {
|
||||
reader: Box::new(wd_reader),
|
||||
|
@ -4,7 +4,7 @@ use anyhow::{Context, Result};
|
||||
use serde::Serialize;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::reader;
|
||||
use crate::{id::Id, reader};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@ -19,10 +19,12 @@ pub struct Meta {
|
||||
pub commit: Option<String>,
|
||||
}
|
||||
|
||||
pub type SessionId = Id<Session>;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Session {
|
||||
pub id: String,
|
||||
pub id: SessionId,
|
||||
// if hash is not set, the session is not saved aka current
|
||||
pub hash: Option<String>,
|
||||
pub meta: Meta,
|
||||
@ -51,6 +53,11 @@ impl TryFrom<&dyn reader::Reader> for Session {
|
||||
.try_into()
|
||||
.context("failed to parse session id")
|
||||
.map_err(SessionError::Err)?;
|
||||
let id: SessionId = id
|
||||
.parse()
|
||||
.context("failed to parse session id")
|
||||
.map_err(SessionError::Err)?;
|
||||
|
||||
let start_timestamp_ms = reader
|
||||
.read(path::Path::new("session/meta/start"))
|
||||
.context("failed to read session start timestamp")
|
||||
|
@ -1,18 +1,18 @@
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::{
|
||||
sessions,
|
||||
sessions::{self, session::SessionId},
|
||||
test_utils::{Case, Suite},
|
||||
};
|
||||
|
||||
use super::Writer;
|
||||
|
||||
#[test]
|
||||
fn test_should_not_write_session_with_hash() -> Result<()> {
|
||||
fn test_should_not_write_session_with_hash() {
|
||||
let Case { gb_repository, .. } = Suite::default().new_case();
|
||||
|
||||
let session = sessions::Session {
|
||||
id: "session_id".to_string(),
|
||||
id: SessionId::generate(),
|
||||
hash: Some("hash".to_string()),
|
||||
meta: sessions::Meta {
|
||||
start_timestamp_ms: 0,
|
||||
@ -23,8 +23,6 @@ fn test_should_not_write_session_with_hash() -> Result<()> {
|
||||
};
|
||||
|
||||
assert!(Writer::new(&gb_repository).write(&session).is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -32,7 +30,7 @@ fn test_should_write_full_session() -> Result<()> {
|
||||
let Case { gb_repository, .. } = Suite::default().new_case();
|
||||
|
||||
let session = sessions::Session {
|
||||
id: "session_id".to_string(),
|
||||
id: SessionId::generate(),
|
||||
hash: None,
|
||||
meta: sessions::Meta {
|
||||
start_timestamp_ms: 0,
|
||||
@ -46,7 +44,7 @@ fn test_should_write_full_session() -> Result<()> {
|
||||
|
||||
assert_eq!(
|
||||
std::fs::read_to_string(gb_repository.session_path().join("meta/id"))?,
|
||||
"session_id"
|
||||
session.id.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
std::fs::read_to_string(gb_repository.session_path().join("meta/commit"))?,
|
||||
@ -73,7 +71,7 @@ fn test_should_write_partial_session() -> Result<()> {
|
||||
let Case { gb_repository, .. } = Suite::default().new_case();
|
||||
|
||||
let session = sessions::Session {
|
||||
id: "session_id".to_string(),
|
||||
id: SessionId::generate(),
|
||||
hash: None,
|
||||
meta: sessions::Meta {
|
||||
start_timestamp_ms: 0,
|
||||
@ -87,7 +85,7 @@ fn test_should_write_partial_session() -> Result<()> {
|
||||
|
||||
assert_eq!(
|
||||
std::fs::read_to_string(gb_repository.session_path().join("meta/id"))?,
|
||||
"session_id"
|
||||
session.id.to_string()
|
||||
);
|
||||
assert!(!gb_repository.session_path().join("meta/commit").exists());
|
||||
assert!(!gb_repository.session_path().join("meta/branch").exists());
|
||||
|
@ -36,7 +36,9 @@ impl<'writer> SessionWriter<'writer> {
|
||||
None
|
||||
};
|
||||
|
||||
if current_session_id.is_some() && current_session_id.as_ref() != Some(&session.id) {
|
||||
if current_session_id.is_some()
|
||||
&& current_session_id.as_ref() != Some(&session.id.to_string())
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"{}: can not open writer for {} because a writer for {} is still open",
|
||||
self.repository.get_project_id(),
|
||||
@ -57,7 +59,9 @@ impl<'writer> SessionWriter<'writer> {
|
||||
)
|
||||
.context("failed to write last timestamp")?;
|
||||
|
||||
if current_session_id.is_some() && current_session_id.as_ref() == Some(&session.id) {
|
||||
if current_session_id.is_some()
|
||||
&& current_session_id.as_ref() == Some(&session.id.to_string())
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -69,7 +73,7 @@ impl<'writer> SessionWriter<'writer> {
|
||||
.join("id")
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
session.id.as_str(),
|
||||
&session.id.to_string(),
|
||||
)
|
||||
.context("failed to write id")?;
|
||||
|
||||
|
@ -2,7 +2,6 @@ use std::time;
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use serde::Serialize;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
gb_repository,
|
||||
@ -11,7 +10,7 @@ use crate::{
|
||||
reader, sessions, users,
|
||||
};
|
||||
|
||||
use super::{branch, delete_branch, iterator, target, RemoteCommit};
|
||||
use super::{branch, delete_branch, iterator, target, BranchId, RemoteCommit};
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@ -540,9 +539,8 @@ pub fn create_virtual_branch_from_branch(
|
||||
}
|
||||
};
|
||||
|
||||
let branch_id = Uuid::new_v4().to_string();
|
||||
let mut branch = branch::Branch {
|
||||
id: branch_id.clone(),
|
||||
id: BranchId::generate(),
|
||||
name: upstream.branch().to_string(),
|
||||
notes: String::new(),
|
||||
applied: applied.unwrap_or(false),
|
||||
|
@ -16,7 +16,9 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::git;
|
||||
use crate::{git, id::Id};
|
||||
|
||||
pub type BranchId = Id<Branch>;
|
||||
|
||||
// this is the struct for the virtual branch data that is stored in our data
|
||||
// store. it is more or less equivalent to a git branch reference, but it is not
|
||||
@ -24,7 +26,7 @@ use crate::git;
|
||||
// session storage under the branches/ directory.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Branch {
|
||||
pub id: String,
|
||||
pub id: BranchId,
|
||||
pub name: String,
|
||||
pub notes: String,
|
||||
pub applied: bool,
|
||||
@ -43,7 +45,7 @@ pub struct Branch {
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||
pub struct BranchUpdateRequest {
|
||||
pub id: String,
|
||||
pub id: BranchId,
|
||||
pub name: Option<String>,
|
||||
pub notes: Option<String>,
|
||||
pub ownership: Option<Ownership>,
|
||||
@ -63,6 +65,12 @@ impl TryFrom<&dyn crate::reader::Reader> for Branch {
|
||||
|
||||
fn try_from(reader: &dyn crate::reader::Reader) -> Result<Self, Self::Error> {
|
||||
let id: String = reader.read(&path::PathBuf::from("id"))?.try_into()?;
|
||||
let id: BranchId = id.parse().map_err(|e| {
|
||||
crate::reader::Error::Io(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("id: {}", e),
|
||||
))
|
||||
})?;
|
||||
let name: String = reader.read(&path::PathBuf::from("meta/name"))?.try_into()?;
|
||||
|
||||
let notes: String = match reader.read(&path::PathBuf::from("meta/notes")) {
|
||||
|
@ -2,7 +2,7 @@ use std::path;
|
||||
|
||||
use crate::reader::{self, Reader, SubReader};
|
||||
|
||||
use super::Branch;
|
||||
use super::{Branch, BranchId};
|
||||
|
||||
pub struct BranchReader<'reader> {
|
||||
reader: &'reader dyn reader::Reader,
|
||||
@ -17,7 +17,7 @@ impl<'reader> BranchReader<'reader> {
|
||||
self.reader
|
||||
}
|
||||
|
||||
pub fn read(&self, id: &str) -> Result<Branch, reader::Error> {
|
||||
pub fn read(&self, id: &BranchId) -> Result<Branch, reader::Error> {
|
||||
if !self
|
||||
.reader
|
||||
.exists(&path::PathBuf::from(format!("branches/{}", id)))
|
||||
@ -52,7 +52,7 @@ mod tests {
|
||||
TEST_INDEX.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
Branch {
|
||||
id: format!("branch_{}", TEST_INDEX.load(Ordering::Relaxed)),
|
||||
id: BranchId::generate(),
|
||||
name: format!("branch_name_{}", TEST_INDEX.load(Ordering::Relaxed)),
|
||||
notes: "".to_string(),
|
||||
applied: true,
|
||||
@ -103,7 +103,7 @@ mod tests {
|
||||
let session_reader = sessions::Reader::open(&gb_repository, &session)?;
|
||||
|
||||
let reader = BranchReader::new(&session_reader);
|
||||
let result = reader.read("not found");
|
||||
let result = reader.read(&BranchId::generate());
|
||||
assert!(result.is_err());
|
||||
assert_eq!(result.unwrap_err().to_string(), "file not found");
|
||||
|
||||
|
@ -34,7 +34,10 @@ impl<'writer> BranchWriter<'writer> {
|
||||
let _lock = self.repository.lock();
|
||||
|
||||
self.writer
|
||||
.write_string(&format!("branches/{}/id", branch.id), &branch.id)
|
||||
.write_string(
|
||||
&format!("branches/{}/id", branch.id),
|
||||
&branch.id.to_string(),
|
||||
)
|
||||
.context("Failed to write branch id")?;
|
||||
|
||||
self.writer
|
||||
@ -121,6 +124,8 @@ mod tests {
|
||||
virtual_branches::branch,
|
||||
};
|
||||
|
||||
use self::branch::BranchId;
|
||||
|
||||
use super::*;
|
||||
|
||||
static TEST_INDEX: Lazy<AtomicUsize> = Lazy::new(|| AtomicUsize::new(0));
|
||||
@ -129,7 +134,7 @@ mod tests {
|
||||
TEST_INDEX.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
Branch {
|
||||
id: format!("branch_{}", TEST_INDEX.load(Ordering::Relaxed)),
|
||||
id: BranchId::generate(),
|
||||
name: format!("branch_name_{}", TEST_INDEX.load(Ordering::Relaxed)),
|
||||
notes: "".to_string(),
|
||||
applied: true,
|
||||
@ -175,7 +180,10 @@ mod tests {
|
||||
let writer = BranchWriter::new(&gb_repository);
|
||||
writer.write(&branch)?;
|
||||
|
||||
let root = gb_repository.root().join("branches").join(&branch.id);
|
||||
let root = gb_repository
|
||||
.root()
|
||||
.join("branches")
|
||||
.join(branch.id.to_string());
|
||||
|
||||
assert_eq!(
|
||||
fs::read_to_string(root.join("meta").join("name").to_str().unwrap())
|
||||
@ -259,7 +267,10 @@ mod tests {
|
||||
|
||||
writer.write(&updated_branch)?;
|
||||
|
||||
let root = gb_repository.root().join("branches").join(&branch.id);
|
||||
let root = gb_repository
|
||||
.root()
|
||||
.join("branches")
|
||||
.join(branch.id.to_string());
|
||||
|
||||
assert_eq!(
|
||||
fs::read_to_string(root.join("meta").join("name").to_str().unwrap())
|
||||
|
@ -2,10 +2,14 @@ use anyhow::Context;
|
||||
use tauri::{AppHandle, Manager};
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::{assets, error::Error, git};
|
||||
use crate::{
|
||||
assets,
|
||||
error::{Code, Error},
|
||||
git,
|
||||
};
|
||||
|
||||
use super::{
|
||||
branch::Ownership,
|
||||
branch::{BranchId, Ownership},
|
||||
controller::{self, Controller},
|
||||
RemoteBranchFile,
|
||||
};
|
||||
@ -38,9 +42,17 @@ pub async fn commit_virtual_branch(
|
||||
message: &str,
|
||||
ownership: Option<Ownership>,
|
||||
) -> Result<git::Oid, Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let branch_id = branch.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Branches,
|
||||
message: "Malformed branch id".to_string(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.create_commit(project_id, branch, message, ownership.as_ref())
|
||||
.create_commit(&project_id, &branch_id, message, ownership.as_ref())
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -51,9 +63,13 @@ pub async fn list_virtual_branches(
|
||||
handle: AppHandle,
|
||||
project_id: &str,
|
||||
) -> Result<Vec<super::VirtualBranch>, Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.list_virtual_branches(project_id)
|
||||
.list_virtual_branches(&project_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -64,10 +80,14 @@ pub async fn create_virtual_branch(
|
||||
handle: AppHandle,
|
||||
project_id: &str,
|
||||
branch: super::branch::BranchCreateRequest,
|
||||
) -> Result<String, Error> {
|
||||
) -> Result<BranchId, Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.create_virtual_branch(project_id, &branch)
|
||||
.create_virtual_branch(&project_id, &branch)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -78,10 +98,14 @@ pub async fn create_virtual_branch_from_branch(
|
||||
handle: AppHandle,
|
||||
project_id: &str,
|
||||
branch: git::BranchName,
|
||||
) -> Result<String, Error> {
|
||||
) -> Result<BranchId, Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.create_virtual_branch_from_branch(project_id, &branch)
|
||||
.create_virtual_branch_from_branch(&project_id, &branch)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -93,9 +117,17 @@ pub async fn merge_virtual_branch_upstream(
|
||||
project_id: &str,
|
||||
branch: &str,
|
||||
) -> Result<(), Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let branch_id = branch.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Branches,
|
||||
message: "Malformed branch id".to_string(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.merge_virtual_branch_upstream(project_id, branch)
|
||||
.merge_virtual_branch_upstream(&project_id, &branch_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -106,9 +138,13 @@ pub async fn get_base_branch_data(
|
||||
handle: AppHandle,
|
||||
project_id: &str,
|
||||
) -> Result<Option<super::BaseBranch>, Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
if let Some(base_branch) = handle
|
||||
.state::<Controller>()
|
||||
.get_base_branch_data(project_id)?
|
||||
.get_base_branch_data(&project_id)?
|
||||
{
|
||||
let proxy = handle.state::<assets::Proxy>();
|
||||
let base_branch = proxy.proxy_base_branch(&base_branch).await;
|
||||
@ -125,12 +161,16 @@ pub async fn set_base_branch(
|
||||
project_id: &str,
|
||||
branch: &str,
|
||||
) -> Result<super::BaseBranch, Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let branch_name = format!("refs/remotes/{}", branch)
|
||||
.parse()
|
||||
.context("Invalid branch name")?;
|
||||
let base_branch = handle
|
||||
.state::<Controller>()
|
||||
.set_base_branch(project_id, &branch_name)?;
|
||||
.set_base_branch(&project_id, &branch_name)?;
|
||||
let base_branch = handle
|
||||
.state::<assets::Proxy>()
|
||||
.proxy_base_branch(&base_branch)
|
||||
@ -141,9 +181,13 @@ pub async fn set_base_branch(
|
||||
#[tauri::command(async)]
|
||||
#[instrument(skip(handle))]
|
||||
pub async fn update_base_branch(handle: AppHandle, project_id: &str) -> Result<(), Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".into(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.update_base_branch(project_id)
|
||||
.update_base_branch(&project_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -155,9 +199,13 @@ pub async fn update_virtual_branch(
|
||||
project_id: &str,
|
||||
branch: super::branch::BranchUpdateRequest,
|
||||
) -> Result<(), Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.update_virtual_branch(project_id, branch)
|
||||
.update_virtual_branch(&project_id, branch)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -169,9 +217,17 @@ pub async fn delete_virtual_branch(
|
||||
project_id: &str,
|
||||
branch_id: &str,
|
||||
) -> Result<(), Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let branch_id = branch_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Branches,
|
||||
message: "Malformed branch id".to_string(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.delete_virtual_branch(project_id, branch_id)
|
||||
.delete_virtual_branch(&project_id, &branch_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -179,9 +235,17 @@ pub async fn delete_virtual_branch(
|
||||
#[tauri::command(async)]
|
||||
#[instrument(skip(handle))]
|
||||
pub async fn apply_branch(handle: AppHandle, project_id: &str, branch: &str) -> Result<(), Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let branch_id = branch.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Branches,
|
||||
message: "Malformed branch id".to_string(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.apply_virtual_branch(project_id, branch)
|
||||
.apply_virtual_branch(&project_id, &branch_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -193,9 +257,17 @@ pub async fn unapply_branch(
|
||||
project_id: &str,
|
||||
branch: &str,
|
||||
) -> Result<(), Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let branch_id = branch.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Branches,
|
||||
message: "Malformed branch id".to_string(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.unapply_virtual_branch(project_id, branch)
|
||||
.unapply_virtual_branch(&project_id, &branch_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -207,9 +279,13 @@ pub async fn unapply_ownership(
|
||||
project_id: &str,
|
||||
ownership: Ownership,
|
||||
) -> Result<(), Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.unapply_ownership(project_id, &ownership)
|
||||
.unapply_ownership(&project_id, &ownership)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -222,9 +298,17 @@ pub async fn push_virtual_branch(
|
||||
branch_id: &str,
|
||||
with_force: bool,
|
||||
) -> Result<(), Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let branch_id = branch_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Branches,
|
||||
message: "Malformed branch id".to_string(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.push_virtual_branch(project_id, branch_id, with_force)
|
||||
.push_virtual_branch(&project_id, &branch_id, with_force)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -236,9 +320,17 @@ pub async fn can_apply_virtual_branch(
|
||||
project_id: &str,
|
||||
branch_id: &str,
|
||||
) -> Result<bool, Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let branch_id = branch_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Branches,
|
||||
message: "Malformed branch id".to_string(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.can_apply_virtual_branch(project_id, branch_id)
|
||||
.can_apply_virtual_branch(&project_id, &branch_id)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
@ -249,9 +341,13 @@ pub async fn can_apply_remote_branch(
|
||||
project_id: &str,
|
||||
branch: git::BranchName,
|
||||
) -> Result<bool, Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.can_apply_remote_branch(project_id, &branch)
|
||||
.can_apply_remote_branch(&project_id, &branch)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
@ -262,9 +358,13 @@ pub async fn list_remote_commit_files(
|
||||
project_id: &str,
|
||||
commit_oid: git::Oid,
|
||||
) -> Result<Vec<RemoteBranchFile>, Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.list_remote_commit_files(project_id, commit_oid)
|
||||
.list_remote_commit_files(&project_id, commit_oid)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
@ -276,9 +376,17 @@ pub async fn reset_virtual_branch(
|
||||
branch_id: &str,
|
||||
target_commit_oid: git::Oid,
|
||||
) -> Result<(), Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let branch_id = branch_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Branches,
|
||||
message: "Malformed branch id".to_string(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.reset_virtual_branch(project_id, branch_id, target_commit_oid)
|
||||
.reset_virtual_branch(&project_id, &branch_id, target_commit_oid)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
@ -8,10 +8,14 @@ use crate::{
|
||||
gb_repository, git, keys,
|
||||
paths::DataDir,
|
||||
project_repository::{self, conflicts},
|
||||
projects, users,
|
||||
projects::{self, ProjectId},
|
||||
users,
|
||||
};
|
||||
|
||||
use super::{branch::Ownership, RemoteBranchFile};
|
||||
use super::{
|
||||
branch::{BranchId, Ownership},
|
||||
RemoteBranchFile,
|
||||
};
|
||||
|
||||
pub struct Controller {
|
||||
local_data_dir: DataDir,
|
||||
@ -84,8 +88,8 @@ impl Controller {
|
||||
|
||||
pub async fn create_commit(
|
||||
&self,
|
||||
project_id: &str,
|
||||
branch: &str,
|
||||
project_id: &ProjectId,
|
||||
branch_id: &BranchId,
|
||||
message: &str,
|
||||
ownership: Option<&Ownership>,
|
||||
) -> Result<git::Oid, Error> {
|
||||
@ -107,7 +111,7 @@ impl Controller {
|
||||
super::commit(
|
||||
gb_repository,
|
||||
project_repository,
|
||||
branch,
|
||||
branch_id,
|
||||
message,
|
||||
ownership,
|
||||
signing_key.as_ref(),
|
||||
@ -124,7 +128,7 @@ impl Controller {
|
||||
|
||||
pub fn can_apply_remote_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
branch_name: &git::BranchName,
|
||||
) -> Result<bool, Error> {
|
||||
let project = self.projects.get(project_id)?;
|
||||
@ -142,8 +146,8 @@ impl Controller {
|
||||
|
||||
pub fn can_apply_virtual_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
branch_id: &str,
|
||||
project_id: &ProjectId,
|
||||
branch_id: &BranchId,
|
||||
) -> Result<bool, Error> {
|
||||
let project = self.projects.get(project_id)?;
|
||||
let project_repository = project_repository::Repository::try_from(&project)?;
|
||||
@ -160,7 +164,7 @@ impl Controller {
|
||||
|
||||
pub async fn list_virtual_branches(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
) -> Result<Vec<super::VirtualBranch>, Error> {
|
||||
self.with_lock(project_id, || {
|
||||
self.with_verify_branch(project_id, |gb_repository, project_repository, _| {
|
||||
@ -173,9 +177,9 @@ impl Controller {
|
||||
|
||||
pub async fn create_virtual_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
create: &super::branch::BranchCreateRequest,
|
||||
) -> Result<String, Error> {
|
||||
) -> Result<BranchId, Error> {
|
||||
self.with_lock(project_id, || {
|
||||
self.with_verify_branch(project_id, |gb_repository, project_repository, _| {
|
||||
if conflicts::is_resolving(project_repository) {
|
||||
@ -192,10 +196,10 @@ impl Controller {
|
||||
|
||||
pub async fn create_virtual_branch_from_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
branch: &git::BranchName,
|
||||
) -> Result<String, Error> {
|
||||
self.with_lock::<Result<String, Error>>(project_id, || {
|
||||
) -> Result<BranchId, Error> {
|
||||
self.with_lock(project_id, || {
|
||||
self.with_verify_branch(project_id, |gb_repository, project_repository, user| {
|
||||
let branch = super::create_virtual_branch_from_branch(
|
||||
gb_repository,
|
||||
@ -237,7 +241,7 @@ impl Controller {
|
||||
|
||||
pub fn get_base_branch_data(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
) -> Result<Option<super::BaseBranch>, Error> {
|
||||
let project = self.projects.get(project_id)?;
|
||||
let project_repository = project_repository::Repository::try_from(&project)?;
|
||||
@ -255,7 +259,7 @@ impl Controller {
|
||||
|
||||
pub fn list_remote_commit_files(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
commit_oid: git::Oid,
|
||||
) -> Result<Vec<RemoteBranchFile>, Error> {
|
||||
let project = self.projects.get(project_id)?;
|
||||
@ -270,7 +274,7 @@ impl Controller {
|
||||
|
||||
pub fn set_base_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
target_branch: &git::RemoteBranchName,
|
||||
) -> Result<super::BaseBranch, Error> {
|
||||
let project = self.projects.get(project_id)?;
|
||||
@ -298,8 +302,8 @@ impl Controller {
|
||||
|
||||
pub async fn merge_virtual_branch_upstream(
|
||||
&self,
|
||||
project_id: &str,
|
||||
branch: &str,
|
||||
project_id: &ProjectId,
|
||||
branch_id: &BranchId,
|
||||
) -> Result<(), Error> {
|
||||
self.with_lock(project_id, || {
|
||||
self.with_verify_branch(project_id, |gb_repository, project_repository, user| {
|
||||
@ -325,7 +329,7 @@ impl Controller {
|
||||
super::merge_virtual_branch_upstream(
|
||||
gb_repository,
|
||||
project_repository,
|
||||
branch,
|
||||
branch_id,
|
||||
signing_key.as_ref(),
|
||||
user,
|
||||
)
|
||||
@ -335,7 +339,7 @@ impl Controller {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn update_base_branch(&self, project_id: &str) -> Result<(), Error> {
|
||||
pub async fn update_base_branch(&self, project_id: &ProjectId) -> Result<(), Error> {
|
||||
self.with_lock(project_id, || {
|
||||
self.with_verify_branch(project_id, |gb_repository, project_repository, user| {
|
||||
super::update_base_branch(gb_repository, project_repository, user)
|
||||
@ -347,7 +351,7 @@ impl Controller {
|
||||
|
||||
pub async fn update_virtual_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
branch_update: super::branch::BranchUpdateRequest,
|
||||
) -> Result<(), Error> {
|
||||
self.with_lock(project_id, || {
|
||||
@ -361,8 +365,8 @@ impl Controller {
|
||||
|
||||
pub async fn delete_virtual_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
branch_id: &str,
|
||||
project_id: &ProjectId,
|
||||
branch_id: &BranchId,
|
||||
) -> Result<(), Error> {
|
||||
self.with_lock(project_id, || {
|
||||
self.with_verify_branch(project_id, |gb_repository, project_repository, _| {
|
||||
@ -375,8 +379,8 @@ impl Controller {
|
||||
|
||||
pub async fn apply_virtual_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
branch_id: &str,
|
||||
project_id: &ProjectId,
|
||||
branch_id: &BranchId,
|
||||
) -> Result<(), Error> {
|
||||
self.with_lock(project_id, || {
|
||||
self.with_verify_branch(project_id, |gb_repository, project_repository, user| {
|
||||
@ -408,7 +412,7 @@ impl Controller {
|
||||
|
||||
pub async fn unapply_ownership(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
ownership: &Ownership,
|
||||
) -> Result<(), Error> {
|
||||
self.with_lock(project_id, || {
|
||||
@ -422,8 +426,8 @@ impl Controller {
|
||||
|
||||
pub async fn reset_virtual_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
branch_id: &str,
|
||||
project_id: &ProjectId,
|
||||
branch_id: &BranchId,
|
||||
target_commit_oid: git::Oid,
|
||||
) -> Result<(), Error> {
|
||||
self.with_lock(project_id, || {
|
||||
@ -442,8 +446,8 @@ impl Controller {
|
||||
|
||||
pub async fn unapply_virtual_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
branch_id: &str,
|
||||
project_id: &ProjectId,
|
||||
branch_id: &BranchId,
|
||||
) -> Result<(), Error> {
|
||||
self.with_lock(project_id, || {
|
||||
self.with_verify_branch(project_id, |gb_repository, project_repository, _| {
|
||||
@ -456,8 +460,8 @@ impl Controller {
|
||||
|
||||
pub async fn push_virtual_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
branch_id: &str,
|
||||
project_id: &ProjectId,
|
||||
branch_id: &BranchId,
|
||||
with_force: bool,
|
||||
) -> Result<(), Error> {
|
||||
self.with_lock(project_id, || {
|
||||
@ -497,7 +501,7 @@ impl Controller {
|
||||
|
||||
fn with_verify_branch<T>(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
action: impl FnOnce(
|
||||
&gb_repository::Repository,
|
||||
&project_repository::Repository,
|
||||
@ -518,7 +522,7 @@ impl Controller {
|
||||
action(&gb_repository, &project_repository, user.as_ref())
|
||||
}
|
||||
|
||||
async fn with_lock<T>(&self, project_id: &str, action: impl FnOnce() -> T) -> T {
|
||||
async fn with_lock<T>(&self, project_id: &ProjectId, action: impl FnOnce() -> T) -> T {
|
||||
let mut semaphores = self.semaphores.lock().await;
|
||||
let semaphore = semaphores
|
||||
.entry(project_id.to_string())
|
||||
|
@ -4,11 +4,11 @@ use anyhow::Result;
|
||||
|
||||
use crate::reader;
|
||||
|
||||
use super::branch;
|
||||
use super::branch::{self, BranchId};
|
||||
|
||||
pub struct BranchIterator<'iterator> {
|
||||
branch_reader: branch::Reader<'iterator>,
|
||||
ids: Vec<String>,
|
||||
ids: Vec<BranchId>,
|
||||
}
|
||||
|
||||
impl<'iterator> BranchIterator<'iterator> {
|
||||
@ -28,7 +28,11 @@ impl<'iterator> BranchIterator<'iterator> {
|
||||
.filter(|file_path| file_path != "selected")
|
||||
.filter(|file_path| file_path != "target");
|
||||
let unique_ids: HashSet<String> = ids_itarator.collect();
|
||||
let mut ids: Vec<String> = unique_ids.into_iter().collect();
|
||||
let mut ids: Vec<BranchId> = unique_ids
|
||||
.into_iter()
|
||||
.map(|id| id.parse())
|
||||
.filter_map(Result::ok)
|
||||
.collect();
|
||||
ids.sort();
|
||||
Ok(Self {
|
||||
branch_reader: branch::Reader::new(reader),
|
||||
@ -72,9 +76,9 @@ mod tests {
|
||||
TEST_INDEX.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
branch::Branch {
|
||||
id: format!("branch_{}", TEST_INDEX.load(Ordering::Relaxed)),
|
||||
id: BranchId::generate(),
|
||||
name: format!("branch_name_{}", TEST_INDEX.load(Ordering::Relaxed)),
|
||||
notes: "".to_string(),
|
||||
notes: String::new(),
|
||||
applied: true,
|
||||
upstream: Some(
|
||||
format!(
|
||||
@ -157,10 +161,12 @@ mod tests {
|
||||
let session = gb_repository.get_current_session()?.unwrap();
|
||||
let session_reader = sessions::Reader::open(&gb_repository, &session)?;
|
||||
|
||||
let mut iter = BranchIterator::new(&session_reader)?;
|
||||
assert_eq!(iter.next().unwrap().unwrap(), branch_1);
|
||||
assert_eq!(iter.next().unwrap().unwrap(), branch_2);
|
||||
assert_eq!(iter.next().unwrap().unwrap(), branch_3);
|
||||
let iter =
|
||||
BranchIterator::new(&session_reader)?.collect::<Result<Vec<_>, reader::Error>>()?;
|
||||
assert_eq!(iter.len(), 3);
|
||||
assert!(iter.contains(&branch_1));
|
||||
assert!(iter.contains(&branch_2));
|
||||
assert!(iter.contains(&branch_3));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
pub mod branch;
|
||||
pub use branch::Branch;
|
||||
pub use branch::{Branch, BranchId};
|
||||
pub mod target;
|
||||
|
||||
mod files;
|
||||
|
@ -1,6 +1,9 @@
|
||||
use std::path;
|
||||
|
||||
use crate::reader::{self, SubReader};
|
||||
use crate::{
|
||||
reader::{self, SubReader},
|
||||
virtual_branches::BranchId,
|
||||
};
|
||||
|
||||
use super::Target;
|
||||
|
||||
@ -22,7 +25,7 @@ impl<'reader> TargetReader<'reader> {
|
||||
Target::try_from(reader)
|
||||
}
|
||||
|
||||
pub fn read(&self, id: &str) -> Result<Target, reader::Error> {
|
||||
pub fn read(&self, id: &BranchId) -> Result<Target, reader::Error> {
|
||||
if !self
|
||||
.reader
|
||||
.exists(&path::PathBuf::from(format!("branches/{}/target", id)))
|
||||
@ -58,7 +61,7 @@ mod tests {
|
||||
TEST_INDEX.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
branch::Branch {
|
||||
id: format!("branch_{}", TEST_INDEX.load(Ordering::Relaxed)),
|
||||
id: BranchId::generate(),
|
||||
name: format!("branch_name_{}", TEST_INDEX.load(Ordering::Relaxed)),
|
||||
notes: "".to_string(),
|
||||
applied: true,
|
||||
@ -103,7 +106,7 @@ mod tests {
|
||||
let session_reader = sessions::Reader::open(&gb_repository, &session)?;
|
||||
|
||||
let reader = TargetReader::new(&session_reader);
|
||||
let result = reader.read("not found");
|
||||
let result = reader.read(&BranchId::generate());
|
||||
assert!(result.is_err());
|
||||
assert_eq!(result.unwrap_err().to_string(), "file not found");
|
||||
|
||||
|
@ -2,6 +2,7 @@ use anyhow::{Context, Result};
|
||||
|
||||
use crate::{
|
||||
gb_repository,
|
||||
virtual_branches::BranchId,
|
||||
writer::{self, Writer},
|
||||
};
|
||||
|
||||
@ -43,7 +44,7 @@ impl<'writer> TargetWriter<'writer> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write(&self, id: &str, target: &Target) -> Result<()> {
|
||||
pub fn write(&self, id: &BranchId, target: &Target) -> Result<()> {
|
||||
self.repository
|
||||
.mark_active_session()
|
||||
.context("Failed to get or create current session")?;
|
||||
@ -101,7 +102,7 @@ mod tests {
|
||||
TEST_INDEX.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
branch::Branch {
|
||||
id: format!("branch_{}", TEST_INDEX.load(Ordering::Relaxed)),
|
||||
id: BranchId::generate(),
|
||||
name: format!("branch_name_{}", TEST_INDEX.load(Ordering::Relaxed)),
|
||||
notes: format!("branch_notes_{}", TEST_INDEX.load(Ordering::Relaxed)),
|
||||
applied: true,
|
||||
@ -155,7 +156,10 @@ mod tests {
|
||||
let target_writer = TargetWriter::new(&gb_repository);
|
||||
target_writer.write(&branch.id, &target)?;
|
||||
|
||||
let root = gb_repository.root().join("branches").join(&branch.id);
|
||||
let root = gb_repository
|
||||
.root()
|
||||
.join("branches")
|
||||
.join(branch.id.to_string());
|
||||
|
||||
assert_eq!(
|
||||
fs::read_to_string(root.join("meta").join("name").to_str().unwrap())
|
||||
@ -247,7 +251,10 @@ mod tests {
|
||||
|
||||
target_writer.write(&branch.id, &updated_target)?;
|
||||
|
||||
let root = gb_repository.root().join("branches").join(&branch.id);
|
||||
let root = gb_repository
|
||||
.root()
|
||||
.join("branches")
|
||||
.join(branch.id.to_string());
|
||||
|
||||
assert_eq!(
|
||||
fs::read_to_string(root.join("target").join("branch_name").to_str().unwrap())
|
||||
|
@ -8,8 +8,6 @@ use anyhow::{anyhow, bail, Context, Result};
|
||||
use diffy::{apply_bytes, Patch};
|
||||
use serde::Serialize;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
dedup::{dedup, dedup_fmt},
|
||||
gb_repository,
|
||||
@ -20,7 +18,7 @@ use crate::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
branch::{self, Branch, BranchCreateRequest, FileOwnership, Hunk, Ownership},
|
||||
branch::{self, Branch, BranchCreateRequest, BranchId, FileOwnership, Hunk, Ownership},
|
||||
target, Iterator,
|
||||
};
|
||||
|
||||
@ -37,7 +35,7 @@ type AppliedStatuses = Vec<(branch::Branch, Vec<VirtualBranchFile>)>;
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct VirtualBranch {
|
||||
pub id: String,
|
||||
pub id: BranchId,
|
||||
pub name: String,
|
||||
pub notes: String,
|
||||
pub active: bool,
|
||||
@ -154,7 +152,7 @@ pub fn get_default_target(
|
||||
pub fn apply_branch(
|
||||
gb_repository: &gb_repository::Repository,
|
||||
project_repository: &project_repository::Repository,
|
||||
branch_id: &str,
|
||||
branch_id: &BranchId,
|
||||
signing_key: Option<&keys::PrivateKey>,
|
||||
user: Option<&users::User>,
|
||||
) -> Result<()> {
|
||||
@ -413,7 +411,7 @@ pub fn unapply_ownership(
|
||||
pub fn unapply_branch(
|
||||
gb_repository: &gb_repository::Repository,
|
||||
project_repository: &project_repository::Repository,
|
||||
branch_id: &str,
|
||||
branch_id: &BranchId,
|
||||
) -> Result<()> {
|
||||
if conflicts::is_resolving(project_repository) {
|
||||
bail!("cannot unapply, project is in a conflicted state");
|
||||
@ -440,7 +438,7 @@ pub fn unapply_branch(
|
||||
// then check that out into the working directory
|
||||
let final_tree = applied_statuses
|
||||
.into_iter()
|
||||
.filter(|(branch, _)| branch.id != branch_id)
|
||||
.filter(|(branch, _)| &branch.id != branch_id)
|
||||
.fold(
|
||||
target_commit.tree().context("failed to get target tree"),
|
||||
|final_tree, status| {
|
||||
@ -468,7 +466,7 @@ pub fn unapply_branch(
|
||||
pub fn flush_vbranch_as_tree(
|
||||
gb_repository: &gb_repository::Repository,
|
||||
project_repository: &project_repository::Repository,
|
||||
branch_id: &str,
|
||||
branch_id: &BranchId,
|
||||
applied: bool,
|
||||
) -> Result<Option<(target::Target, AppliedStatuses)>> {
|
||||
let session = &gb_repository
|
||||
@ -514,7 +512,7 @@ pub fn flush_vbranch_as_tree(
|
||||
|
||||
let status = applied_statuses
|
||||
.iter()
|
||||
.find(|(s, _)| s.id == branch_id)
|
||||
.find(|(s, _)| s.id == *branch_id)
|
||||
.context("failed to find status for branch");
|
||||
|
||||
if let Ok((_, files)) = status {
|
||||
@ -663,7 +661,7 @@ pub fn list_virtual_branches(
|
||||
let requires_force = is_requires_force(project_repository, branch)?;
|
||||
|
||||
let branch = VirtualBranch {
|
||||
id: branch.id.clone(),
|
||||
id: branch.id,
|
||||
name: branch.name.clone(),
|
||||
notes: branch.notes.clone(),
|
||||
active: branch.applied,
|
||||
@ -958,7 +956,7 @@ pub fn create_virtual_branch(
|
||||
);
|
||||
|
||||
let mut branch = Branch {
|
||||
id: Uuid::new_v4().to_string(),
|
||||
id: BranchId::generate(),
|
||||
name,
|
||||
notes: String::new(),
|
||||
applied: true,
|
||||
@ -988,7 +986,7 @@ pub fn create_virtual_branch(
|
||||
pub fn merge_virtual_branch_upstream(
|
||||
gb_repository: &gb_repository::Repository,
|
||||
project_repository: &project_repository::Repository,
|
||||
branch_id: &str,
|
||||
branch_id: &BranchId,
|
||||
signing_key: Option<&keys::PrivateKey>,
|
||||
user: Option<&users::User>,
|
||||
) -> Result<()> {
|
||||
@ -1044,7 +1042,7 @@ pub fn merge_virtual_branch_upstream(
|
||||
.context("failed to read virtual branches")?
|
||||
.into_iter()
|
||||
.filter(|b| b.applied)
|
||||
.filter(|b| b.id != branch_id)
|
||||
.filter(|b| b.id != *branch_id)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// unapply all other branches
|
||||
@ -1222,7 +1220,7 @@ pub fn update_branch(
|
||||
pub fn delete_branch(
|
||||
gb_repository: &gb_repository::Repository,
|
||||
project_repository: &project_repository::Repository,
|
||||
branch_id: &str,
|
||||
branch_id: &BranchId,
|
||||
) -> Result<branch::Branch> {
|
||||
let current_session = gb_repository
|
||||
.get_or_create_current_session()
|
||||
@ -1512,9 +1510,9 @@ fn get_applied_status(
|
||||
// - update shifted hunks
|
||||
// - remove non existent hunks
|
||||
|
||||
let mut hunks_by_branch_id: HashMap<String, Vec<VirtualBranchHunk>> = virtual_branches
|
||||
let mut hunks_by_branch_id: HashMap<BranchId, Vec<VirtualBranchHunk>> = virtual_branches
|
||||
.iter()
|
||||
.map(|branch| (branch.id.clone(), vec![]))
|
||||
.map(|branch| (branch.id, vec![]))
|
||||
.collect();
|
||||
|
||||
for branch in &mut virtual_branches {
|
||||
@ -1556,14 +1554,13 @@ fn get_applied_status(
|
||||
.unwrap_or(current_hunk.modified_at);
|
||||
|
||||
// push hunk to the end of the list, preserving the order
|
||||
hunks_by_branch_id
|
||||
.entry(branch.id.clone())
|
||||
.or_default()
|
||||
.push(VirtualBranchHunk {
|
||||
hunks_by_branch_id.entry(branch.id).or_default().push(
|
||||
VirtualBranchHunk {
|
||||
id: ch.with_timestamp(timestamp).to_string(),
|
||||
modified_at: timestamp,
|
||||
..current_hunk.clone()
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// remove the hunk from the current hunks because each hunk can
|
||||
// only be owned once
|
||||
@ -1573,16 +1570,13 @@ fn get_applied_status(
|
||||
} else if owned_hunk.intersects(&ch) {
|
||||
// if it's an intersection, push the hunk to the beginning,
|
||||
// indicating the the hunk has been updated
|
||||
hunks_by_branch_id
|
||||
.entry(branch.id.clone())
|
||||
.or_default()
|
||||
.insert(
|
||||
0,
|
||||
VirtualBranchHunk {
|
||||
id: ch.to_string(),
|
||||
..current_hunk.clone()
|
||||
},
|
||||
);
|
||||
hunks_by_branch_id.entry(branch.id).or_default().insert(
|
||||
0,
|
||||
VirtualBranchHunk {
|
||||
id: ch.to_string(),
|
||||
..current_hunk.clone()
|
||||
},
|
||||
);
|
||||
|
||||
// track updated hunks to bubble them up later
|
||||
updated.push(FileOwnership {
|
||||
@ -1636,7 +1630,7 @@ fn get_applied_status(
|
||||
.unwrap(),
|
||||
);
|
||||
hunks_by_branch_id
|
||||
.entry(virtual_branches[0].id.clone())
|
||||
.entry(virtual_branches[0].id)
|
||||
.or_default()
|
||||
.push(hunk.clone());
|
||||
}
|
||||
@ -1701,7 +1695,7 @@ fn virtual_hunks_to_virtual_files(
|
||||
pub fn reset_branch(
|
||||
gb_repository: &gb_repository::Repository,
|
||||
project_repository: &project_repository::Repository,
|
||||
branch_id: &str,
|
||||
branch_id: &BranchId,
|
||||
target_commit_oid: git::Oid,
|
||||
) -> Result<()> {
|
||||
let current_session = gb_repository.get_or_create_current_session()?;
|
||||
@ -1905,7 +1899,7 @@ fn _print_tree(repo: &git2::Repository, tree: &git2::Tree) -> Result<()> {
|
||||
pub fn commit(
|
||||
gb_repository: &gb_repository::Repository,
|
||||
project_repository: &project_repository::Repository,
|
||||
branch_id: &str,
|
||||
branch_id: &BranchId,
|
||||
message: &str,
|
||||
ownership: Option<&branch::Ownership>,
|
||||
signing_key: Option<&keys::PrivateKey>,
|
||||
@ -1922,7 +1916,7 @@ pub fn commit(
|
||||
|
||||
let (branch, files) = statuses
|
||||
.iter()
|
||||
.find(|(branch, _)| branch.id == branch_id)
|
||||
.find(|(branch, _)| branch.id == *branch_id)
|
||||
.ok_or_else(|| anyhow!("branch {} not found", branch_id))?;
|
||||
|
||||
let files = calculate_non_commited_files(project_repository, branch, &default_target, files)?;
|
||||
@ -2054,7 +2048,7 @@ pub enum PushError {
|
||||
pub fn push(
|
||||
project_repository: &project_repository::Repository,
|
||||
gb_repository: &gb_repository::Repository,
|
||||
branch_id: &str,
|
||||
branch_id: &BranchId,
|
||||
with_force: bool,
|
||||
key: &Key,
|
||||
) -> Result<(), PushError> {
|
||||
@ -2241,7 +2235,7 @@ pub fn is_remote_branch_mergeable(
|
||||
pub fn is_virtual_branch_mergeable(
|
||||
gb_repository: &gb_repository::Repository,
|
||||
project_repository: &project_repository::Repository,
|
||||
branch_id: &str,
|
||||
branch_id: &BranchId,
|
||||
) -> Result<bool> {
|
||||
let current_session = gb_repository
|
||||
.get_or_create_current_session()
|
||||
|
@ -11,7 +11,7 @@ use tokio::{
|
||||
task,
|
||||
};
|
||||
|
||||
use crate::{git, watcher::events};
|
||||
use crate::{git, projects::ProjectId, watcher::events};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Dispatcher {
|
||||
@ -29,7 +29,7 @@ impl Dispatcher {
|
||||
self.watcher.lock().unwrap().take();
|
||||
}
|
||||
|
||||
pub fn run(self, project_id: &str, path: &path::Path) -> Result<Receiver<events::Event>> {
|
||||
pub fn run(self, project_id: &ProjectId, path: &path::Path) -> Result<Receiver<events::Event>> {
|
||||
let repo = git::Repository::open(path)
|
||||
.with_context(|| format!("failed to open project repository: {}", path.display()))?;
|
||||
|
||||
@ -64,27 +64,26 @@ impl Dispatcher {
|
||||
.with_context(|| format!("failed to watch project path: {}", path.display()))?;
|
||||
self.watcher.lock().unwrap().replace(watcher);
|
||||
|
||||
tracing::debug!(project_id, "file watcher started");
|
||||
tracing::debug!(%project_id, "file watcher started");
|
||||
|
||||
let (tx, rx) = channel(1);
|
||||
let project_id = project_id.to_string();
|
||||
task::Builder::new()
|
||||
.name(&format!("{} file watcher", project_id))
|
||||
.spawn({
|
||||
let path = path.to_path_buf();
|
||||
let project_id = project_id.clone();
|
||||
let project_id = *project_id;
|
||||
async move {
|
||||
while let Some(file_path) = notify_rx.recv().await {
|
||||
match file_path.strip_prefix(&path) {
|
||||
Ok(relative_file_path) => {
|
||||
let event = if relative_file_path.starts_with(".git") {
|
||||
tracing::info!(
|
||||
project_id,
|
||||
%project_id,
|
||||
file_path = %relative_file_path.display(),
|
||||
"git file change",
|
||||
);
|
||||
events::Event::GitFileChange(
|
||||
project_id.clone(),
|
||||
project_id,
|
||||
relative_file_path
|
||||
.strip_prefix(".git")
|
||||
.unwrap()
|
||||
@ -92,29 +91,29 @@ impl Dispatcher {
|
||||
)
|
||||
} else {
|
||||
tracing::info!(
|
||||
project_id,
|
||||
%project_id,
|
||||
file_path = %relative_file_path.display(),
|
||||
"project file change",
|
||||
);
|
||||
events::Event::ProjectFileChange(
|
||||
project_id.clone(),
|
||||
project_id,
|
||||
relative_file_path.to_path_buf(),
|
||||
)
|
||||
};
|
||||
if let Err(error) = tx.send(event).await {
|
||||
tracing::error!(
|
||||
project_id,
|
||||
%project_id,
|
||||
?error,
|
||||
"failed to send file change event",
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::error!(project_id, ?error, "failed to strip prefix");
|
||||
tracing::error!(%project_id, ?error, "failed to strip prefix");
|
||||
}
|
||||
}
|
||||
}
|
||||
tracing::debug!(project_id, "file watcher stopped");
|
||||
tracing::debug!(%project_id, "file watcher stopped");
|
||||
}
|
||||
})?;
|
||||
|
||||
|
@ -11,6 +11,8 @@ use tokio::{
|
||||
};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::projects::ProjectId;
|
||||
|
||||
use super::events;
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -36,7 +38,7 @@ impl Dispatcher {
|
||||
|
||||
pub fn run<P: AsRef<path::Path>>(
|
||||
self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
path: P,
|
||||
) -> Result<Receiver<events::Event>> {
|
||||
let path = path.as_ref();
|
||||
@ -55,7 +57,7 @@ impl Dispatcher {
|
||||
))?;
|
||||
|
||||
let (tx, rx) = channel(1);
|
||||
let project_id = project_id.to_owned();
|
||||
let project_id = *project_id;
|
||||
task::Builder::new()
|
||||
.name(&format!("{} dispatcher", project_id))
|
||||
.spawn(async move {
|
||||
@ -66,17 +68,17 @@ impl Dispatcher {
|
||||
}
|
||||
Some(event) = tick_rx.recv() => {
|
||||
if let Err(error) = tx.send(event).await {
|
||||
tracing::error!(project_id, ?error,"failed to send tick");
|
||||
tracing::error!(%project_id, ?error,"failed to send tick");
|
||||
}
|
||||
}
|
||||
Some(event) = file_change_rx.recv() => {
|
||||
if let Err(error) = tx.send(event).await {
|
||||
tracing::error!( project_id, ?error,"failed to send file change");
|
||||
tracing::error!(%project_id, ?error,"failed to send file change");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tracing::debug!(project_id, "dispatcher stopped");
|
||||
tracing::debug!(%project_id, "dispatcher stopped");
|
||||
})?;
|
||||
|
||||
Ok(rx)
|
||||
|
@ -7,7 +7,7 @@ use tokio::{
|
||||
};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::watcher::events;
|
||||
use crate::{projects::ProjectId, watcher::events};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Dispatcher {
|
||||
@ -27,7 +27,7 @@ impl Dispatcher {
|
||||
|
||||
pub fn run(
|
||||
self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
interval: time::Duration,
|
||||
) -> Result<Receiver<events::Event>> {
|
||||
let (tx, rx) = channel(1);
|
||||
@ -36,25 +36,22 @@ impl Dispatcher {
|
||||
task::Builder::new()
|
||||
.name(&format!("{} ticker", project_id))
|
||||
.spawn({
|
||||
let project_id = project_id.to_string();
|
||||
let project_id = *project_id;
|
||||
async move {
|
||||
tracing::debug!(project_id, "ticker started");
|
||||
tracing::debug!(%project_id, "ticker started");
|
||||
loop {
|
||||
ticker.tick().await;
|
||||
if self.cancellation_token.is_cancelled() {
|
||||
break;
|
||||
}
|
||||
if let Err(error) = tx
|
||||
.send(events::Event::Tick(
|
||||
project_id.clone(),
|
||||
time::SystemTime::now(),
|
||||
))
|
||||
.send(events::Event::Tick(project_id, time::SystemTime::now()))
|
||||
.await
|
||||
{
|
||||
tracing::error!(project_id, ?error, "failed to send tick");
|
||||
tracing::error!(%project_id, ?error, "failed to send tick");
|
||||
}
|
||||
}
|
||||
tracing::debug!(project_id, "ticker stopped");
|
||||
tracing::debug!(%project_id, "ticker stopped");
|
||||
}
|
||||
})?;
|
||||
|
||||
@ -64,14 +61,17 @@ impl Dispatcher {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::time::Duration;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_ticker() {
|
||||
let dispatcher = Dispatcher::new();
|
||||
let dispatcher2 = dispatcher.clone();
|
||||
let mut rx = dispatcher2.run("test", Duration::from_millis(10)).unwrap();
|
||||
let mut rx = dispatcher2
|
||||
.run(&ProjectId::generate(), Duration::from_millis(10))
|
||||
.unwrap();
|
||||
|
||||
tokio::spawn(async move {
|
||||
tokio::time::sleep(Duration::from_millis(50)).await;
|
||||
|
@ -1,33 +1,38 @@
|
||||
use std::{fmt::Display, path, time};
|
||||
|
||||
use crate::{analytics, bookmarks, deltas, events, reader, sessions};
|
||||
use crate::{
|
||||
analytics, bookmarks, deltas, events,
|
||||
projects::ProjectId,
|
||||
reader,
|
||||
sessions::{self, SessionId},
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Event {
|
||||
Tick(String, time::SystemTime),
|
||||
Flush(String, sessions::Session),
|
||||
Tick(ProjectId, time::SystemTime),
|
||||
Flush(ProjectId, sessions::Session),
|
||||
|
||||
FetchGitbutlerData(String, time::SystemTime),
|
||||
PushGitbutlerData(String),
|
||||
FetchProjectData(String, time::SystemTime),
|
||||
FetchGitbutlerData(ProjectId, time::SystemTime),
|
||||
PushGitbutlerData(ProjectId),
|
||||
FetchProjectData(ProjectId, time::SystemTime),
|
||||
|
||||
GitFileChange(String, path::PathBuf),
|
||||
GitFileChange(ProjectId, path::PathBuf),
|
||||
|
||||
ProjectFileChange(String, path::PathBuf),
|
||||
ProjectFileChange(ProjectId, path::PathBuf),
|
||||
|
||||
Session(String, sessions::Session),
|
||||
SessionFile((String, String, path::PathBuf, Option<reader::Content>)),
|
||||
SessionDelta((String, String, path::PathBuf, deltas::Delta)),
|
||||
Session(ProjectId, sessions::Session),
|
||||
SessionFile((ProjectId, SessionId, path::PathBuf, Option<reader::Content>)),
|
||||
SessionDelta((ProjectId, SessionId, path::PathBuf, deltas::Delta)),
|
||||
Bookmark(bookmarks::Bookmark),
|
||||
|
||||
IndexAll(String),
|
||||
IndexAll(ProjectId),
|
||||
|
||||
Emit(events::Event),
|
||||
Analytics(analytics::Event),
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub fn project_id(&self) -> &str {
|
||||
pub fn project_id(&self) -> &ProjectId {
|
||||
match self {
|
||||
Event::Analytics(event) => event.project_id(),
|
||||
Event::Emit(event) => event.project_id(),
|
||||
|
@ -6,8 +6,8 @@ use std::{
|
||||
use anyhow::{Context, Result};
|
||||
use tauri::AppHandle;
|
||||
|
||||
use crate::paths::DataDir;
|
||||
use crate::{gb_repository, project_repository, projects, users};
|
||||
use crate::{paths::DataDir, projects::ProjectId};
|
||||
|
||||
use super::events;
|
||||
|
||||
@ -28,7 +28,11 @@ impl TryFrom<&AppHandle> for Handler {
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
pub fn handle(&self, project_id: &str, now: &time::SystemTime) -> Result<Vec<events::Event>> {
|
||||
pub fn handle(
|
||||
&self,
|
||||
project_id: &ProjectId,
|
||||
now: &time::SystemTime,
|
||||
) -> Result<Vec<events::Event>> {
|
||||
self.inner.handle(project_id, now)
|
||||
}
|
||||
}
|
||||
@ -59,7 +63,11 @@ impl TryFrom<&AppHandle> for HandlerInner {
|
||||
}
|
||||
|
||||
impl HandlerInner {
|
||||
pub fn handle(&self, project_id: &str, now: &time::SystemTime) -> Result<Vec<events::Event>> {
|
||||
pub fn handle(
|
||||
&self,
|
||||
project_id: &ProjectId,
|
||||
now: &time::SystemTime,
|
||||
) -> Result<Vec<events::Event>> {
|
||||
let _lock = match self.mutex.try_lock() {
|
||||
Ok(lock) => lock,
|
||||
Err(TryLockError::Poisoned(_)) => return Err(anyhow::anyhow!("mutex poisoned")),
|
||||
@ -81,7 +89,7 @@ impl HandlerInner {
|
||||
// mark fetching
|
||||
self.projects
|
||||
.update(&projects::UpdateRequest {
|
||||
id: project_id.to_string(),
|
||||
id: *project_id,
|
||||
gitbutler_data_last_fetched: Some(projects::FetchResult::Fetching {
|
||||
timestamp_ms: now.duration_since(time::UNIX_EPOCH)?.as_millis(),
|
||||
}),
|
||||
@ -104,7 +112,7 @@ impl HandlerInner {
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let fetch_result = if let Err(error) = gb_repo.fetch(user.as_ref()) {
|
||||
tracing::error!(project_id, ?error, "failed to fetch gitbutler data");
|
||||
tracing::error!(%project_id, ?error, "failed to fetch gitbutler data");
|
||||
projects::FetchResult::Error {
|
||||
attempt: project
|
||||
.gitbutler_data_last_fetched
|
||||
@ -125,7 +133,7 @@ impl HandlerInner {
|
||||
|
||||
self.projects
|
||||
.update(&projects::UpdateRequest {
|
||||
id: project_id.to_string(),
|
||||
id: *project_id,
|
||||
gitbutler_data_last_fetched: Some(fetch_result),
|
||||
..Default::default()
|
||||
})
|
||||
@ -144,7 +152,7 @@ impl HandlerInner {
|
||||
let events = new_sessions
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.map(|session| events::Event::Session(project_id.to_string(), session))
|
||||
.map(|session| events::Event::Session(*project_id, session))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(events)
|
||||
|
@ -6,7 +6,13 @@ use std::{
|
||||
use anyhow::{Context, Result};
|
||||
use tauri::AppHandle;
|
||||
|
||||
use crate::{gb_repository, keys, paths::DataDir, project_repository, projects, users};
|
||||
use crate::{
|
||||
gb_repository, keys,
|
||||
paths::DataDir,
|
||||
project_repository,
|
||||
projects::{self, ProjectId},
|
||||
users,
|
||||
};
|
||||
|
||||
use super::events;
|
||||
|
||||
@ -27,7 +33,11 @@ impl TryFrom<&AppHandle> for Handler {
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
pub fn handle(&self, project_id: &str, now: &time::SystemTime) -> Result<Vec<events::Event>> {
|
||||
pub fn handle(
|
||||
&self,
|
||||
project_id: &ProjectId,
|
||||
now: &time::SystemTime,
|
||||
) -> Result<Vec<events::Event>> {
|
||||
self.inner.handle(project_id, now)
|
||||
}
|
||||
}
|
||||
@ -59,7 +69,11 @@ impl TryFrom<&AppHandle> for HandlerInner {
|
||||
}
|
||||
|
||||
impl HandlerInner {
|
||||
pub fn handle(&self, project_id: &str, now: &time::SystemTime) -> Result<Vec<events::Event>> {
|
||||
pub fn handle(
|
||||
&self,
|
||||
project_id: &ProjectId,
|
||||
now: &time::SystemTime,
|
||||
) -> Result<Vec<events::Event>> {
|
||||
let _lock = match self.mutex.try_lock() {
|
||||
Ok(lock) => lock,
|
||||
Err(TryLockError::Poisoned(_)) => return Err(anyhow::anyhow!("mutex poisoned")),
|
||||
@ -71,7 +85,7 @@ impl HandlerInner {
|
||||
// mark fetching
|
||||
self.projects
|
||||
.update(&projects::UpdateRequest {
|
||||
id: project_id.to_string(),
|
||||
id: *project_id,
|
||||
project_data_last_fetched: Some(projects::FetchResult::Fetching {
|
||||
timestamp_ms: now.duration_since(time::UNIX_EPOCH)?.as_millis(),
|
||||
}),
|
||||
@ -109,7 +123,7 @@ impl HandlerInner {
|
||||
|
||||
let fetch_result =
|
||||
if let Err(error) = project_repository.fetch(default_target.branch.remote(), &key) {
|
||||
tracing::error!(project_id, ?error, "failed to fetch project data");
|
||||
tracing::error!(%project_id, ?error, "failed to fetch project data");
|
||||
projects::FetchResult::Error {
|
||||
attempt: project
|
||||
.project_data_last_fetched
|
||||
@ -130,7 +144,7 @@ impl HandlerInner {
|
||||
|
||||
self.projects
|
||||
.update(&projects::UpdateRequest {
|
||||
id: project_id.to_string(),
|
||||
id: *project_id,
|
||||
project_data_last_fetched: Some(fetch_result),
|
||||
..Default::default()
|
||||
})
|
||||
|
@ -2,7 +2,11 @@ use anyhow::{Context, Result};
|
||||
use tauri::AppHandle;
|
||||
|
||||
use crate::{
|
||||
gb_repository, paths::DataDir, project_repository, projects, sessions, users, virtual_branches,
|
||||
gb_repository,
|
||||
paths::DataDir,
|
||||
project_repository,
|
||||
projects::{self, ProjectId},
|
||||
sessions, users,
|
||||
};
|
||||
|
||||
use super::events;
|
||||
@ -29,7 +33,7 @@ impl TryFrom<&AppHandle> for Handler {
|
||||
impl Handler {
|
||||
pub fn handle(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
session: &sessions::Session,
|
||||
) -> Result<Vec<events::Event>> {
|
||||
let project = self
|
||||
@ -47,22 +51,23 @@ impl Handler {
|
||||
)
|
||||
.context("failed to open repository")?;
|
||||
|
||||
if let Some(branch_id) = &session.meta.branch {
|
||||
virtual_branches::flush_vbranch_as_tree(
|
||||
&gb_repo,
|
||||
&project_repository,
|
||||
branch_id,
|
||||
true,
|
||||
)?;
|
||||
}
|
||||
// TODO: fixme
|
||||
// if let Some(branch_id) = &session.meta.branch {
|
||||
// virtual_branches::flush_vbranch_as_tree(
|
||||
// &gb_repo,
|
||||
// &project_repository,
|
||||
// branch_id,
|
||||
// true,
|
||||
// )?;
|
||||
// }
|
||||
|
||||
let session = gb_repo
|
||||
.flush_session(&project_repository, session, user.as_ref())
|
||||
.context("failed to flush session")?;
|
||||
|
||||
Ok(vec![
|
||||
events::Event::Session(project_id.to_string(), session),
|
||||
events::Event::PushGitbutlerData(project_id.to_string()),
|
||||
events::Event::Session(*project_id, session),
|
||||
events::Event::PushGitbutlerData(*project_id),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,10 @@ use anyhow::{Context, Result};
|
||||
use tauri::AppHandle;
|
||||
|
||||
use crate::{
|
||||
analytics, events as app_events, gb_repository, paths::DataDir, project_repository, projects,
|
||||
analytics, events as app_events, gb_repository,
|
||||
paths::DataDir,
|
||||
project_repository,
|
||||
projects::{self, ProjectId},
|
||||
users,
|
||||
};
|
||||
|
||||
@ -31,7 +34,7 @@ impl Handler {
|
||||
pub fn handle<P: AsRef<std::path::Path>>(
|
||||
&self,
|
||||
path: P,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
) -> Result<Vec<events::Event>> {
|
||||
let project = self
|
||||
.projects
|
||||
@ -61,17 +64,14 @@ impl Handler {
|
||||
|
||||
if file_path.exists() {
|
||||
if let Err(e) = std::fs::remove_file(&file_path) {
|
||||
tracing::error!(project_id, path = %file_path.display(), "GB_FLUSH file delete error: {}", e);
|
||||
tracing::error!(%project_id, path = %file_path.display(), "GB_FLUSH file delete error: {}", e);
|
||||
}
|
||||
|
||||
if let Some(current_session) = gb_repo
|
||||
.get_current_session()
|
||||
.context("failed to get current session")?
|
||||
{
|
||||
return Ok(vec![events::Event::Flush(
|
||||
project.id.clone(),
|
||||
current_session,
|
||||
)]);
|
||||
return Ok(vec![events::Event::Flush(project.id, current_session)]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ impl Handler {
|
||||
if let Some(head) = head_ref.name() {
|
||||
Ok(vec![
|
||||
events::Event::Analytics(analytics::Event::HeadChange {
|
||||
project_id: project.id.clone(),
|
||||
project_id: project.id,
|
||||
reference_name: head_ref_name.to_string(),
|
||||
}),
|
||||
events::Event::Emit(app_events::Event::git_head(&project.id, head)),
|
||||
|
@ -4,8 +4,13 @@ use anyhow::{Context, Result};
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
use crate::{
|
||||
bookmarks, deltas, events as app_events, gb_repository, paths::DataDir, project_repository,
|
||||
projects, search, sessions, users,
|
||||
bookmarks, deltas, events as app_events, gb_repository,
|
||||
paths::DataDir,
|
||||
project_repository,
|
||||
projects::{self, ProjectId},
|
||||
search,
|
||||
sessions::{self, SessionId},
|
||||
users,
|
||||
};
|
||||
|
||||
use super::events;
|
||||
@ -40,8 +45,8 @@ impl TryFrom<&AppHandle> for Handler {
|
||||
impl Handler {
|
||||
pub fn index_deltas(
|
||||
&self,
|
||||
project_id: &str,
|
||||
session_id: &str,
|
||||
project_id: &ProjectId,
|
||||
session_id: &SessionId,
|
||||
file_path: &path::Path,
|
||||
deltas: &Vec<deltas::Delta>,
|
||||
) -> Result<Vec<events::Event>> {
|
||||
@ -53,7 +58,7 @@ impl Handler {
|
||||
|
||||
pub fn index_bookmark(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
bookmark: &bookmarks::Bookmark,
|
||||
) -> Result<Vec<events::Event>> {
|
||||
let updated = self.bookmarks_database.upsert(bookmark)?;
|
||||
@ -67,7 +72,7 @@ impl Handler {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reindex(&self, project_id: &str) -> Result<Vec<events::Event>> {
|
||||
pub fn reindex(&self, project_id: &ProjectId) -> Result<Vec<events::Event>> {
|
||||
let user = self.users.get_user()?;
|
||||
let project = self.projects.get(project_id)?;
|
||||
let project_repository = project_repository::Repository::try_from(&project)
|
||||
@ -89,7 +94,7 @@ impl Handler {
|
||||
|
||||
pub fn index_session(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
session: &sessions::Session,
|
||||
) -> Result<Vec<events::Event>> {
|
||||
let user = self.users.get_user()?;
|
||||
|
@ -6,7 +6,8 @@ use tauri::AppHandle;
|
||||
use crate::{
|
||||
deltas, gb_repository,
|
||||
paths::DataDir,
|
||||
project_repository, projects,
|
||||
project_repository,
|
||||
projects::{self, ProjectId},
|
||||
reader::{self, Reader},
|
||||
sessions, users,
|
||||
};
|
||||
@ -79,7 +80,7 @@ impl Handler {
|
||||
pub fn handle<P: AsRef<std::path::Path>>(
|
||||
&self,
|
||||
path: P,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
) -> Result<Vec<events::Event>> {
|
||||
let project = self
|
||||
.projects
|
||||
@ -146,7 +147,7 @@ impl Handler {
|
||||
.context("failed to calculate new deltas")?;
|
||||
|
||||
if new_delta.is_none() {
|
||||
tracing::debug!(project_id, path = %path.display(), "no new deltas, ignoring");
|
||||
tracing::debug!(%project_id, path = %path.display(), "no new deltas, ignoring");
|
||||
return Ok(vec![]);
|
||||
}
|
||||
let new_delta = new_delta.as_ref().unwrap();
|
||||
@ -166,15 +167,15 @@ impl Handler {
|
||||
|
||||
Ok(vec![
|
||||
events::Event::SessionFile((
|
||||
project_id.to_string(),
|
||||
current_session.id.clone(),
|
||||
*project_id,
|
||||
current_session.id,
|
||||
path.to_path_buf(),
|
||||
latest_file_content,
|
||||
)),
|
||||
events::Event::Session(project_id.to_string(), current_session.clone()),
|
||||
events::Event::Session(*project_id, current_session.clone()),
|
||||
events::Event::SessionDelta((
|
||||
project_id.to_string(),
|
||||
current_session.id.clone(),
|
||||
*project_id,
|
||||
current_session.id,
|
||||
path.to_path_buf(),
|
||||
new_delta.clone(),
|
||||
)),
|
||||
@ -197,6 +198,8 @@ mod test {
|
||||
virtual_branches::{self, branch},
|
||||
};
|
||||
|
||||
use self::branch::BranchId;
|
||||
|
||||
use super::*;
|
||||
|
||||
static TEST_TARGET_INDEX: Lazy<AtomicUsize> = Lazy::new(|| AtomicUsize::new(0));
|
||||
@ -226,7 +229,7 @@ mod test {
|
||||
TEST_INDEX.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
virtual_branches::branch::Branch {
|
||||
id: format!("branch_{}", TEST_INDEX.load(Ordering::Relaxed)),
|
||||
id: BranchId::generate(),
|
||||
name: format!("branch_name_{}", TEST_INDEX.load(Ordering::Relaxed)),
|
||||
notes: format!("branch_notes_{}", TEST_INDEX.load(Ordering::Relaxed)),
|
||||
applied: true,
|
||||
@ -518,7 +521,7 @@ mod test {
|
||||
// get all the created sessions
|
||||
let mut sessions: Vec<sessions::Session> = gb_repository
|
||||
.get_sessions_iterator()?
|
||||
.map(|s| s.unwrap())
|
||||
.map(Result::unwrap)
|
||||
.collect();
|
||||
assert_eq!(sessions.len(), size);
|
||||
// verify sessions order is correct
|
||||
@ -533,23 +536,23 @@ mod test {
|
||||
|
||||
sessions.reverse();
|
||||
// try to reconstruct file state from operations for every session slice
|
||||
for i in 0..=sessions.len() - 1 {
|
||||
for i in 0..sessions.len() {
|
||||
let sessions_slice = &mut sessions[i..];
|
||||
|
||||
// collect all operations from sessions in the reverse order
|
||||
let mut operations: Vec<deltas::Operation> = vec![];
|
||||
sessions_slice.iter().for_each(|session| {
|
||||
for session in &mut *sessions_slice {
|
||||
let session_reader = sessions::Reader::open(&gb_repository, session).unwrap();
|
||||
let deltas_reader = deltas::Reader::new(&session_reader);
|
||||
let deltas_by_filepath = deltas_reader.read(&None).unwrap();
|
||||
for deltas in deltas_by_filepath.values() {
|
||||
deltas.iter().for_each(|delta| {
|
||||
for delta in deltas {
|
||||
delta.operations.iter().for_each(|operation| {
|
||||
operations.push(operation.clone());
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let reader =
|
||||
sessions::Reader::open(&gb_repository, sessions_slice.first().unwrap()).unwrap();
|
||||
@ -604,7 +607,7 @@ mod test {
|
||||
// get all the created sessions
|
||||
let mut sessions: Vec<sessions::Session> = gb_repository
|
||||
.get_sessions_iterator()?
|
||||
.map(|s| s.unwrap())
|
||||
.map(Result::unwrap)
|
||||
.collect();
|
||||
assert_eq!(sessions.len(), size);
|
||||
// verify sessions order is correct
|
||||
@ -619,23 +622,23 @@ mod test {
|
||||
|
||||
sessions.reverse();
|
||||
// try to reconstruct file state from operations for every session slice
|
||||
for i in 0..=sessions.len() - 1 {
|
||||
for i in 0..sessions.len() {
|
||||
let sessions_slice = &mut sessions[i..];
|
||||
|
||||
// collect all operations from sessions in the reverse order
|
||||
let mut operations: Vec<deltas::Operation> = vec![];
|
||||
sessions_slice.iter().for_each(|session| {
|
||||
for session in &mut *sessions_slice {
|
||||
let session_reader = sessions::Reader::open(&gb_repository, session).unwrap();
|
||||
let deltas_reader = deltas::Reader::new(&session_reader);
|
||||
let deltas_by_filepath = deltas_reader.read(&None).unwrap();
|
||||
for deltas in deltas_by_filepath.values() {
|
||||
deltas.iter().for_each(|delta| {
|
||||
for delta in deltas {
|
||||
delta.operations.iter().for_each(|operation| {
|
||||
operations.push(operation.clone());
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let reader =
|
||||
sessions::Reader::open(&gb_repository, sessions_slice.first().unwrap()).unwrap();
|
||||
@ -672,9 +675,9 @@ mod test {
|
||||
} = suite.new_case();
|
||||
let listener = Handler::from(&suite.local_app_data);
|
||||
|
||||
let size = 10;
|
||||
let size = 10_i32;
|
||||
let relative_file_path = std::path::Path::new("one/two/test.txt");
|
||||
for i in 1..=size {
|
||||
for i in 1_i32..=size {
|
||||
std::fs::create_dir_all(std::path::Path::new(&project.path).join("one/two"))?;
|
||||
// create a session with a single file change and flush it
|
||||
std::fs::write(
|
||||
@ -692,11 +695,11 @@ mod test {
|
||||
let deltas_reader = deltas::Reader::new(&session_reader);
|
||||
let deltas_by_filepath = deltas_reader.read(&None).unwrap();
|
||||
for deltas in deltas_by_filepath.values() {
|
||||
deltas.iter().for_each(|delta| {
|
||||
for delta in deltas {
|
||||
delta.operations.iter().for_each(|operation| {
|
||||
operations.push(operation.clone());
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let reader = sessions::Reader::open(&gb_repository, &session).unwrap();
|
||||
@ -760,8 +763,9 @@ mod test {
|
||||
.into_iter()
|
||||
.collect::<Vec<virtual_branches::Branch>>();
|
||||
assert_eq!(branches.len(), 2);
|
||||
assert_eq!(branches[0].id, vbranch0.id);
|
||||
assert_eq!(branches[1].id, vbranch1.id);
|
||||
let branch_ids = branches.iter().map(|b| b.id).collect::<Vec<_>>();
|
||||
assert!(branch_ids.contains(&vbranch0.id));
|
||||
assert!(branch_ids.contains(&vbranch1.id));
|
||||
|
||||
let target_reader = virtual_branches::target::Reader::new(&session_reader);
|
||||
assert_eq!(target_reader.read_default().unwrap(), default_target);
|
||||
@ -818,8 +822,9 @@ mod test {
|
||||
.into_iter()
|
||||
.collect::<Vec<virtual_branches::Branch>>();
|
||||
assert_eq!(branches.len(), 2);
|
||||
assert_eq!(branches[0].id, vbranch0.id);
|
||||
assert_eq!(branches[1].id, vbranch1.id);
|
||||
let branch_ids = branches.iter().map(|b| b.id).collect::<Vec<_>>();
|
||||
assert!(branch_ids.contains(&vbranch0.id));
|
||||
assert!(branch_ids.contains(&vbranch1.id));
|
||||
|
||||
let target_reader = virtual_branches::target::Reader::new(&session_reader);
|
||||
assert_eq!(target_reader.read_default().unwrap(), default_target);
|
||||
|
@ -4,6 +4,7 @@ use anyhow::{Context, Result};
|
||||
use tauri::AppHandle;
|
||||
|
||||
use crate::paths::DataDir;
|
||||
use crate::projects::ProjectId;
|
||||
use crate::{gb_repository, project_repository, projects, users};
|
||||
|
||||
use super::events;
|
||||
@ -24,7 +25,7 @@ impl TryFrom<&AppHandle> for Handler {
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
pub fn handle(&self, project_id: &str) -> Result<Vec<events::Event>> {
|
||||
pub fn handle(&self, project_id: &ProjectId) -> Result<Vec<events::Event>> {
|
||||
self.inner.handle(project_id)
|
||||
}
|
||||
}
|
||||
@ -66,7 +67,7 @@ impl HandlerInner {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle(&self, project_id: &str) -> Result<Vec<events::Event>> {
|
||||
pub fn handle(&self, project_id: &ProjectId) -> Result<Vec<events::Event>> {
|
||||
let _lock = match self.mutex.try_lock() {
|
||||
Ok(lock) => lock,
|
||||
Err(TryLockError::Poisoned(_)) => return Err(anyhow::anyhow!("mutex poisoned")),
|
||||
|
@ -3,7 +3,13 @@ use std::time;
|
||||
use anyhow::{Context, Result};
|
||||
use tauri::AppHandle;
|
||||
|
||||
use crate::{gb_repository, paths::DataDir, project_repository, projects, sessions, users};
|
||||
use crate::{
|
||||
gb_repository,
|
||||
paths::DataDir,
|
||||
project_repository,
|
||||
projects::{self, ProjectId},
|
||||
sessions, users,
|
||||
};
|
||||
|
||||
use super::events;
|
||||
|
||||
@ -27,7 +33,11 @@ impl TryFrom<&AppHandle> for Handler {
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
pub fn handle(&self, project_id: &str, now: &time::SystemTime) -> Result<Vec<events::Event>> {
|
||||
pub fn handle(
|
||||
&self,
|
||||
project_id: &ProjectId,
|
||||
now: &time::SystemTime,
|
||||
) -> Result<Vec<events::Event>> {
|
||||
let user = self.users.get_user()?;
|
||||
|
||||
let project = self.projects.get(project_id)?;
|
||||
@ -51,10 +61,7 @@ impl Handler {
|
||||
.map_or(Ok(true), |f| f.should_fetch(now))
|
||||
.context("failed to check if gitbutler data should be fetched")?
|
||||
{
|
||||
events.push(events::Event::FetchGitbutlerData(
|
||||
project_id.to_string(),
|
||||
*now,
|
||||
));
|
||||
events.push(events::Event::FetchGitbutlerData(*project_id, *now));
|
||||
}
|
||||
|
||||
if project
|
||||
@ -63,10 +70,7 @@ impl Handler {
|
||||
.map_or(Ok(true), |f| f.should_fetch(now))
|
||||
.context("failed to check if project data should be fetched")?
|
||||
{
|
||||
events.push(events::Event::FetchProjectData(
|
||||
project_id.to_string(),
|
||||
*now,
|
||||
));
|
||||
events.push(events::Event::FetchProjectData(*project_id, *now));
|
||||
}
|
||||
|
||||
if let Some(current_session) = gb_repo
|
||||
@ -74,10 +78,7 @@ impl Handler {
|
||||
.context("failed to get current session")?
|
||||
{
|
||||
if should_flush(now, ¤t_session)? {
|
||||
events.push(events::Event::Flush(
|
||||
project_id.to_string(),
|
||||
current_session,
|
||||
));
|
||||
events.push(events::Event::Flush(*project_id, current_session));
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,6 +108,8 @@ fn is_session_active(now: &time::SystemTime, session: &sessions::Session) -> Res
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::sessions::SessionId;
|
||||
|
||||
use super::*;
|
||||
|
||||
const ONE_MILLISECOND: time::Duration = time::Duration::from_millis(1);
|
||||
@ -133,7 +136,7 @@ mod tests {
|
||||
.into_iter()
|
||||
.for_each(|(start, last, expected)| {
|
||||
let session = sessions::Session {
|
||||
id: "session-id".to_string(),
|
||||
id: SessionId::generate(),
|
||||
hash: None,
|
||||
meta: sessions::Meta {
|
||||
start_timestamp_ms: start.duration_since(time::UNIX_EPOCH).unwrap().as_millis(),
|
||||
|
@ -17,12 +17,12 @@ use tokio::{
|
||||
};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::projects;
|
||||
use crate::projects::{self, ProjectId};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Watchers {
|
||||
app_handle: AppHandle,
|
||||
watchers: Arc<Mutex<HashMap<String, Watcher>>>,
|
||||
watchers: Arc<Mutex<HashMap<ProjectId, Watcher>>>,
|
||||
}
|
||||
|
||||
impl TryFrom<&AppHandle> for Watchers {
|
||||
@ -41,22 +41,22 @@ impl Watchers {
|
||||
let watcher = Watcher::try_from(&self.app_handle)?;
|
||||
|
||||
let c_watcher = watcher.clone();
|
||||
let project_id = project.id.clone();
|
||||
let project_id = project.id;
|
||||
let project_path = project.path.clone();
|
||||
|
||||
task::Builder::new()
|
||||
.name(&format!("{} watcher", project_id))
|
||||
.spawn(async move {
|
||||
if let Err(error) = c_watcher.run(&project_path, &project_id).await {
|
||||
tracing::error!(?error, project_id, "watcher error");
|
||||
tracing::error!(?error, %project_id, "watcher error");
|
||||
}
|
||||
tracing::debug!(project_id, "watcher stopped");
|
||||
tracing::debug!(%project_id, "watcher stopped");
|
||||
})?;
|
||||
|
||||
self.watchers
|
||||
.lock()
|
||||
.await
|
||||
.insert(project.id.clone(), watcher.clone());
|
||||
.insert(project.id, watcher.clone());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -73,7 +73,7 @@ impl Watchers {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn stop(&self, project_id: &str) -> Result<()> {
|
||||
pub async fn stop(&self, project_id: &ProjectId) -> Result<()> {
|
||||
if let Some((_, watcher)) = self.watchers.lock().await.remove_entry(project_id) {
|
||||
watcher.stop();
|
||||
};
|
||||
@ -105,7 +105,7 @@ impl Watcher {
|
||||
self.inner.post(event).await
|
||||
}
|
||||
|
||||
pub async fn run<P: AsRef<path::Path>>(&self, path: P, project_id: &str) -> Result<()> {
|
||||
pub async fn run<P: AsRef<path::Path>>(&self, path: P, project_id: &ProjectId) -> Result<()> {
|
||||
self.inner.run(path, project_id).await
|
||||
}
|
||||
}
|
||||
@ -149,7 +149,7 @@ impl WatcherInner {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run<P: AsRef<path::Path>>(&self, path: P, project_id: &str) -> Result<()> {
|
||||
pub async fn run<P: AsRef<path::Path>>(&self, path: P, project_id: &ProjectId) -> Result<()> {
|
||||
let (proxy_tx, mut proxy_rx) = unbounded_channel();
|
||||
self.proxy_tx.lock().await.replace(proxy_tx.clone());
|
||||
|
||||
@ -159,7 +159,7 @@ impl WatcherInner {
|
||||
.context("failed to run dispatcher")?;
|
||||
|
||||
proxy_tx
|
||||
.send(Event::IndexAll(project_id.to_string()))
|
||||
.send(Event::IndexAll(*project_id))
|
||||
.context("failed to send event")?;
|
||||
|
||||
let handle_event = |event: &Event| -> Result<()> {
|
||||
|
@ -3,7 +3,7 @@ use std::path;
|
||||
use tauri::{AppHandle, Manager};
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::error::{Code, Error};
|
||||
|
||||
use super::controller;
|
||||
|
||||
@ -25,9 +25,13 @@ pub async fn get_project_archive_path(
|
||||
handle: AppHandle,
|
||||
project_id: &str,
|
||||
) -> Result<path::PathBuf, Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".into(),
|
||||
})?;
|
||||
handle
|
||||
.state::<controller::Controller>()
|
||||
.archive(project_id)
|
||||
.archive(&project_id)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
@ -49,9 +53,13 @@ pub async fn get_project_data_archive_path(
|
||||
handle: AppHandle,
|
||||
project_id: &str,
|
||||
) -> Result<path::PathBuf, Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Projects,
|
||||
message: "Malformed project id".into(),
|
||||
})?;
|
||||
handle
|
||||
.state::<controller::Controller>()
|
||||
.data_archive(project_id)
|
||||
.data_archive(&project_id)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ use tauri::AppHandle;
|
||||
|
||||
use crate::{
|
||||
paths::{DataDir, LogsDir},
|
||||
projects,
|
||||
projects::{self, ProjectId},
|
||||
};
|
||||
|
||||
use super::Zipper;
|
||||
@ -32,19 +32,19 @@ impl TryFrom<&AppHandle> for Controller {
|
||||
}
|
||||
|
||||
impl Controller {
|
||||
pub fn archive(&self, project_id: &str) -> Result<path::PathBuf, ArchiveError> {
|
||||
pub fn archive(&self, project_id: &ProjectId) -> Result<path::PathBuf, ArchiveError> {
|
||||
let project = self.projects_controller.get(project_id)?;
|
||||
self.zipper.zip(project.path).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn data_archive(&self, project_id: &str) -> Result<path::PathBuf, DataArchiveError> {
|
||||
pub fn data_archive(&self, project_id: &ProjectId) -> Result<path::PathBuf, DataArchiveError> {
|
||||
let project = self.projects_controller.get(project_id)?;
|
||||
self.zipper
|
||||
.zip(
|
||||
self.local_data_dir
|
||||
.to_path_buf()
|
||||
.join("projects")
|
||||
.join(project.id),
|
||||
.join(project.id.to_string()),
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ mod add {
|
||||
let project = controller.add(path).unwrap();
|
||||
assert_eq!(project.path, path);
|
||||
assert_eq!(project.title, path.iter().last().unwrap().to_str().unwrap());
|
||||
assert_eq!(project.id.len(), 36);
|
||||
}
|
||||
|
||||
mod error {
|
||||
|
@ -1,7 +1,9 @@
|
||||
use std::{fs, str::FromStr};
|
||||
|
||||
use gitbutler::{
|
||||
git, keys, projects, users,
|
||||
git, keys,
|
||||
projects::{self, ProjectId},
|
||||
users,
|
||||
virtual_branches::{Controller, ControllerError},
|
||||
};
|
||||
|
||||
@ -9,7 +11,7 @@ use crate::{common::TestProject, paths};
|
||||
|
||||
struct Test {
|
||||
repository: TestProject,
|
||||
project_id: String,
|
||||
project_id: ProjectId,
|
||||
controller: Controller,
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user