mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-04 11:53:18 +03:00
typed project id
This commit is contained in:
parent
6cac02fccc
commit
276e2d5b4f
@ -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,8 @@ use crate::{
|
||||
keys,
|
||||
paths::DataDir,
|
||||
project_repository::{self, conflicts},
|
||||
projects, reader, search,
|
||||
projects::{self, ProjectId},
|
||||
reader, search,
|
||||
sessions::{self, SessionId},
|
||||
users,
|
||||
virtual_branches::{self, target},
|
||||
@ -75,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
|
||||
@ -96,7 +97,7 @@ impl App {
|
||||
|
||||
pub fn list_session_files(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
session_id: &SessionId,
|
||||
paths: &Option<Vec<path::PathBuf>>,
|
||||
) -> Result<HashMap<path::PathBuf, reader::Content>, Error> {
|
||||
@ -122,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
|
||||
@ -130,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")?;
|
||||
@ -199,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
|
||||
@ -209,7 +210,7 @@ impl App {
|
||||
|
||||
pub fn list_session_deltas(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
session_id: &SessionId,
|
||||
paths: &Option<Vec<&str>>,
|
||||
) -> Result<HashMap<String, Vec<deltas::Delta>>, Error> {
|
||||
@ -220,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)?;
|
||||
@ -257,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)?;
|
||||
@ -268,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)?;
|
||||
@ -283,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
|
||||
@ -313,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",
|
||||
);
|
||||
|
@ -54,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)
|
||||
}
|
||||
|
||||
@ -71,7 +75,11 @@ pub async fn list_session_files(
|
||||
message: "Malformed session id".to_string(),
|
||||
code: Code::Sessions,
|
||||
})?;
|
||||
let files = app.list_session_files(project_id, &session_id, &paths)?;
|
||||
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)
|
||||
}
|
||||
|
||||
@ -88,7 +96,11 @@ pub async fn list_deltas(
|
||||
message: "Malformed session id".to_string(),
|
||||
code: Code::Sessions,
|
||||
})?;
|
||||
let deltas = app.list_session_deltas(project_id, &session_id, &paths)?;
|
||||
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)
|
||||
}
|
||||
|
||||
@ -100,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)
|
||||
}
|
||||
|
||||
@ -111,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)
|
||||
}
|
||||
|
||||
@ -122,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)
|
||||
@ -134,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)
|
||||
}
|
||||
|
||||
@ -160,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
|
||||
@ -182,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)
|
||||
}
|
||||
|
||||
@ -190,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(())
|
||||
}
|
||||
|
||||
@ -202,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, sessions::SessionId};
|
||||
use crate::{database, projects::ProjectId, sessions::SessionId};
|
||||
|
||||
use super::{delta, operations};
|
||||
|
||||
@ -27,7 +27,7 @@ impl From<&AppHandle> for Database {
|
||||
impl Database {
|
||||
pub fn insert(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
session_id: &SessionId,
|
||||
file_path: &path::Path,
|
||||
deltas: &Vec<delta::Delta>,
|
||||
@ -55,7 +55,7 @@ impl Database {
|
||||
|
||||
pub fn list_by_project_id_session_id(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
session_id: &SessionId,
|
||||
file_path_filter: &Option<Vec<&str>>,
|
||||
) -> Result<HashMap<String, Vec<delta::Delta>>> {
|
||||
@ -141,7 +141,7 @@ mod tests {
|
||||
let db = test_utils::test_database();
|
||||
let database = Database::from(db);
|
||||
|
||||
let project_id = "project_id";
|
||||
let project_id = ProjectId::generate();
|
||||
let session_id = SessionId::generate();
|
||||
let file_path = path::PathBuf::from("file_path");
|
||||
let delta1 = delta::Delta {
|
||||
@ -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,7 +167,7 @@ mod tests {
|
||||
let db = test_utils::test_database();
|
||||
let database = Database::from(db);
|
||||
|
||||
let project_id = "project_id";
|
||||
let project_id = ProjectId::generate();
|
||||
let session_id = SessionId::generate();
|
||||
let file_path = path::PathBuf::from("file_path");
|
||||
let delta1 = delta::Delta {
|
||||
@ -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,7 +200,7 @@ mod tests {
|
||||
let db = test_utils::test_database();
|
||||
let database = Database::from(db);
|
||||
|
||||
let project_id = "project_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");
|
||||
@ -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"
|
||||
);
|
||||
|
@ -2,7 +2,9 @@ use anyhow::{Context, Result};
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
use crate::{
|
||||
bookmarks, deltas, reader,
|
||||
bookmarks, deltas,
|
||||
projects::ProjectId,
|
||||
reader,
|
||||
sessions::{self, SessionId},
|
||||
};
|
||||
|
||||
@ -33,7 +35,7 @@ impl Sender {
|
||||
pub struct Event {
|
||||
name: String,
|
||||
payload: serde_json::Value,
|
||||
project_id: String,
|
||||
project_id: ProjectId,
|
||||
}
|
||||
|
||||
impl Event {
|
||||
@ -41,44 +43,44 @@ 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,
|
||||
project_id: &ProjectId,
|
||||
session_id: &SessionId,
|
||||
file_path: &str,
|
||||
contents: Option<&reader::Content>,
|
||||
@ -89,28 +91,28 @@ 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,
|
||||
project_id: &ProjectId,
|
||||
session_id: &SessionId,
|
||||
deltas: &Vec<deltas::Delta>,
|
||||
relative_file_path: &std::path::Path,
|
||||
@ -121,7 +123,7 @@ impl Event {
|
||||
"deltas": deltas,
|
||||
"filePath": relative_file_path,
|
||||
}),
|
||||
project_id: project_id.to_string(),
|
||||
project_id: *project_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ use sha2::{Digest, Sha256};
|
||||
use crate::{
|
||||
fs, git, lock,
|
||||
paths::DataDir,
|
||||
projects,
|
||||
projects::{self, ProjectId},
|
||||
sessions::SessionId,
|
||||
users,
|
||||
virtual_branches::{self, target},
|
||||
@ -57,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())
|
||||
@ -96,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);
|
||||
}
|
||||
|
||||
@ -112,7 +112,7 @@ impl Repository {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_project_id(&self) -> &str {
|
||||
pub fn get_project_id(&self) -> &ProjectId {
|
||||
&self.project.id
|
||||
}
|
||||
|
||||
@ -151,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"
|
||||
@ -160,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,
|
||||
@ -182,7 +182,7 @@ impl Repository {
|
||||
))?;
|
||||
|
||||
tracing::info!(
|
||||
project_id = self.project.id,
|
||||
project_id = %self.project.id,
|
||||
remote = %remote.url()?.unwrap(),
|
||||
"gb repo fetched",
|
||||
);
|
||||
@ -200,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"
|
||||
@ -209,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,
|
||||
@ -233,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(())
|
||||
}
|
||||
@ -340,7 +340,7 @@ impl Repository {
|
||||
.context("failed to write session")?;
|
||||
|
||||
tracing::info!(
|
||||
project_id = self.project.id,
|
||||
project_id = %self.project.id,
|
||||
session_id = %session.id,
|
||||
"created new session"
|
||||
);
|
||||
@ -462,7 +462,7 @@ impl Repository {
|
||||
write_gb_commit(tree_id, self, user).context("failed to write gb commit")?;
|
||||
|
||||
tracing::info!(
|
||||
project_id = self.project.id,
|
||||
project_id = %self.project.id,
|
||||
session_id = %session.id,
|
||||
%commit_oid,
|
||||
"flushed session"
|
||||
@ -544,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"
|
||||
);
|
||||
@ -599,8 +599,7 @@ impl Repository {
|
||||
|
||||
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,
|
||||
@ -652,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"
|
||||
);
|
||||
@ -660,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"
|
||||
);
|
||||
@ -668,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"
|
||||
@ -837,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,9 @@ use pretty_assertions::assert_eq;
|
||||
use tempfile::tempdir;
|
||||
|
||||
use crate::{
|
||||
deltas, projects, reader,
|
||||
deltas,
|
||||
projects::{self, ProjectId},
|
||||
reader,
|
||||
sessions::{self, SessionId},
|
||||
test_utils::{Case, Suite},
|
||||
};
|
||||
@ -224,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()
|
||||
})?;
|
||||
@ -247,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()
|
||||
})?;
|
||||
@ -303,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()
|
||||
})?;
|
||||
@ -311,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()
|
||||
})?;
|
||||
@ -400,10 +402,9 @@ fn test_gitbutler_file() -> Result<()> {
|
||||
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["repositoryId"],
|
||||
project_repository.project().id
|
||||
);
|
||||
|
||||
let pid: ProjectId = file_content["repositoryId"].as_str().unwrap().parse()?;
|
||||
assert_eq!(pid, project_repository.project().id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,16 +1,28 @@
|
||||
use std::{fmt, marker::PhantomData, str};
|
||||
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> 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()?)
|
||||
@ -31,6 +43,8 @@ impl<T> PartialEq for Id<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for Id<T> {}
|
||||
|
||||
impl<T> From<Uuid> for Id<T> {
|
||||
fn from(value: Uuid) -> Self {
|
||||
Self(value, PhantomData)
|
||||
|
@ -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,
|
||||
@ -98,7 +98,7 @@ impl Controller {
|
||||
time::SystemTime::now(),
|
||||
))) {
|
||||
tracing::error!(
|
||||
project_id = &project.id,
|
||||
project_id = %project.id,
|
||||
?error,
|
||||
"failed to post fetch project event"
|
||||
);
|
||||
@ -109,7 +109,7 @@ impl Controller {
|
||||
block_on(watchers.post(watcher::Event::PushGitbutlerData(project.id.clone())))
|
||||
{
|
||||
tracing::error!(
|
||||
project_id = &project.id,
|
||||
project_id = %project.id,
|
||||
?error,
|
||||
"failed to post push project event"
|
||||
);
|
||||
@ -120,7 +120,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()),
|
||||
@ -133,7 +133,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(()),
|
||||
@ -143,7 +143,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",
|
||||
);
|
||||
@ -158,9 +158,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 {
|
||||
@ -79,9 +81,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,7 +3,7 @@ use tantivy::{
|
||||
Document,
|
||||
};
|
||||
|
||||
use crate::sessions::SessionId;
|
||||
use crate::{projects::ProjectId, sessions::SessionId};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct IndexDocument {
|
||||
@ -11,7 +11,7 @@ pub struct IndexDocument {
|
||||
pub timestamp_ms: Option<u64>,
|
||||
pub index: Option<u64>,
|
||||
pub id: String,
|
||||
pub project_id: Option<String>,
|
||||
pub project_id: Option<ProjectId>,
|
||||
pub session_id: Option<SessionId>,
|
||||
pub file_path: Option<String>,
|
||||
pub diff: Option<String>,
|
||||
@ -69,7 +69,7 @@ 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().parse().unwrap());
|
||||
|
@ -2,7 +2,7 @@ use std::path;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::{paths::DataDir, sessions::SessionId, storage};
|
||||
use crate::{paths::DataDir, projects::ProjectId, sessions::SessionId, storage};
|
||||
|
||||
use super::index;
|
||||
|
||||
@ -29,11 +29,11 @@ impl Storage {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get(&self, project_id: &str, session_id: &SessionId) -> 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(project_id.to_string())
|
||||
.join(session_id.to_string());
|
||||
let meta = match self.storage.read(filepath.to_str().unwrap())? {
|
||||
None => None,
|
||||
@ -42,11 +42,11 @@ impl Storage {
|
||||
Ok(meta)
|
||||
}
|
||||
|
||||
pub fn set(&self, project_id: &str, session_id: &SessionId, 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(project_id.to_string())
|
||||
.join(session_id.to_string());
|
||||
self.storage.write(filepath, &version.to_string())?;
|
||||
Ok(())
|
||||
|
@ -17,6 +17,7 @@ use tauri::AppHandle;
|
||||
use crate::{
|
||||
bookmarks, deltas, gb_repository,
|
||||
paths::DataDir,
|
||||
projects::ProjectId,
|
||||
reader,
|
||||
sessions::{self, SessionId},
|
||||
};
|
||||
@ -246,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",
|
||||
);
|
||||
@ -284,7 +285,7 @@ impl SearcherInner {
|
||||
.set(repository.get_project_id(), &session.id, index::VERSION)?;
|
||||
|
||||
tracing::debug!(
|
||||
project_id = repository.get_project_id(),
|
||||
project_id = %repository.get_project_id(),
|
||||
session_id = %session.id,
|
||||
"session added to search",
|
||||
);
|
||||
@ -293,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)
|
||||
}
|
||||
|
||||
@ -302,7 +303,7 @@ const WRITE_BUFFER_SIZE: usize = 10_000_000; // 10MB
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SearchResult {
|
||||
pub project_id: String,
|
||||
pub project_id: ProjectId,
|
||||
pub session_id: SessionId,
|
||||
pub file_path: String,
|
||||
pub index: u64,
|
||||
@ -395,7 +396,7 @@ fn index_delta(
|
||||
writer: &mut IndexWriter,
|
||||
reader: &tantivy::IndexReader,
|
||||
session_id: &SessionId,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
file_text: &mut Vec<char>,
|
||||
file_path: &path::Path,
|
||||
i: usize,
|
||||
@ -446,7 +447,7 @@ fn index_delta(
|
||||
doc.index = Some(i.try_into()?);
|
||||
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},
|
||||
};
|
||||
|
||||
@ -75,7 +76,7 @@ fn search_by_bookmark_note() -> Result<()> {
|
||||
|
||||
// first we index bookmark
|
||||
searcher.index_bookmark(&bookmarks::Bookmark {
|
||||
project_id: gb_repository.get_project_id().to_string(),
|
||||
project_id: *gb_repository.get_project_id(),
|
||||
timestamp_ms: 123_456,
|
||||
created_timestamp_ms: 0,
|
||||
updated_timestamp_ms: time::UNIX_EPOCH.elapsed()?.as_millis(),
|
||||
@ -114,7 +115,7 @@ fn search_by_bookmark_note() -> Result<()> {
|
||||
|
||||
// then update the note
|
||||
searcher.index_bookmark(&bookmarks::Bookmark {
|
||||
project_id: gb_repository.get_project_id().to_string(),
|
||||
project_id: *gb_repository.get_project_id(),
|
||||
timestamp_ms: 123_456,
|
||||
created_timestamp_ms: 0,
|
||||
updated_timestamp_ms: time::UNIX_EPOCH.elapsed()?.as_millis(),
|
||||
@ -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,7 +1,7 @@
|
||||
use anyhow::{Context, Result};
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
use crate::database;
|
||||
use crate::{database, projects::ProjectId};
|
||||
|
||||
use super::session::{self, SessionId};
|
||||
|
||||
@ -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,7 +81,7 @@ impl Database {
|
||||
|
||||
pub fn get_by_project_id_id(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
id: &SessionId,
|
||||
) -> Result<Option<session::Session>> {
|
||||
self.database.transaction(|tx| {
|
||||
@ -199,7 +199,7 @@ 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: SessionId::generate(),
|
||||
hash: None,
|
||||
@ -222,10 +222,10 @@ 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(&session1.id)?.unwrap(), session1);
|
||||
@ -240,7 +240,7 @@ mod tests {
|
||||
let db = test_utils::test_database();
|
||||
let database = Database::from(db);
|
||||
|
||||
let project_id = "project_id";
|
||||
let project_id = ProjectId::generate();
|
||||
let session = session::Session {
|
||||
id: SessionId::generate(),
|
||||
hash: None,
|
||||
@ -261,11 +261,11 @@ mod tests {
|
||||
last_timestamp_ms: 4,
|
||||
},
|
||||
};
|
||||
database.insert(project_id, &[&session])?;
|
||||
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(&session.id)?.unwrap(), session_updated);
|
||||
|
@ -2,7 +2,11 @@ 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,
|
||||
@ -38,9 +42,13 @@ 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(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.create_commit(project_id, branch, message, ownership.as_ref())
|
||||
.create_commit(&project_id, branch, message, ownership.as_ref())
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -51,9 +59,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)
|
||||
}
|
||||
@ -65,9 +77,13 @@ pub async fn create_virtual_branch(
|
||||
project_id: &str,
|
||||
branch: super::branch::BranchCreateRequest,
|
||||
) -> Result<String, 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)
|
||||
}
|
||||
@ -79,9 +95,13 @@ pub async fn create_virtual_branch_from_branch(
|
||||
project_id: &str,
|
||||
branch: git::BranchName,
|
||||
) -> Result<String, 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 +113,13 @@ 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(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.merge_virtual_branch_upstream(project_id, branch)
|
||||
.merge_virtual_branch_upstream(&project_id, branch)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -106,9 +130,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 +153,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 +173,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 +191,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 +209,13 @@ 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(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.delete_virtual_branch(project_id, branch_id)
|
||||
.delete_virtual_branch(&project_id, branch_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -179,9 +223,13 @@ 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(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.apply_virtual_branch(project_id, branch)
|
||||
.apply_virtual_branch(&project_id, branch)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -193,9 +241,13 @@ 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(),
|
||||
})?;
|
||||
handle
|
||||
.state::<Controller>()
|
||||
.unapply_virtual_branch(project_id, branch)
|
||||
.unapply_virtual_branch(&project_id, branch)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -207,9 +259,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 +278,13 @@ 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(),
|
||||
})?;
|
||||
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 +296,13 @@ 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(),
|
||||
})?;
|
||||
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 +313,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 +330,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 +348,13 @@ 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(),
|
||||
})?;
|
||||
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,7 +8,8 @@ use crate::{
|
||||
gb_repository, git, keys,
|
||||
paths::DataDir,
|
||||
project_repository::{self, conflicts},
|
||||
projects, users,
|
||||
projects::{self, ProjectId},
|
||||
users,
|
||||
};
|
||||
|
||||
use super::{branch::Ownership, RemoteBranchFile};
|
||||
@ -84,7 +85,7 @@ impl Controller {
|
||||
|
||||
pub async fn create_commit(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
branch: &str,
|
||||
message: &str,
|
||||
ownership: Option<&Ownership>,
|
||||
@ -124,7 +125,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,7 +143,7 @@ impl Controller {
|
||||
|
||||
pub fn can_apply_virtual_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
branch_id: &str,
|
||||
) -> Result<bool, Error> {
|
||||
let project = self.projects.get(project_id)?;
|
||||
@ -160,7 +161,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,7 +174,7 @@ impl Controller {
|
||||
|
||||
pub async fn create_virtual_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
create: &super::branch::BranchCreateRequest,
|
||||
) -> Result<String, Error> {
|
||||
self.with_lock(project_id, || {
|
||||
@ -192,7 +193,7 @@ 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, || {
|
||||
@ -237,7 +238,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 +256,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 +271,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,7 +299,7 @@ impl Controller {
|
||||
|
||||
pub async fn merge_virtual_branch_upstream(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
branch: &str,
|
||||
) -> Result<(), Error> {
|
||||
self.with_lock(project_id, || {
|
||||
@ -335,7 +336,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 +348,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,7 +362,7 @@ impl Controller {
|
||||
|
||||
pub async fn delete_virtual_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
branch_id: &str,
|
||||
) -> Result<(), Error> {
|
||||
self.with_lock(project_id, || {
|
||||
@ -375,7 +376,7 @@ impl Controller {
|
||||
|
||||
pub async fn apply_virtual_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
branch_id: &str,
|
||||
) -> Result<(), Error> {
|
||||
self.with_lock(project_id, || {
|
||||
@ -408,7 +409,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,7 +423,7 @@ impl Controller {
|
||||
|
||||
pub async fn reset_virtual_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
branch_id: &str,
|
||||
target_commit_oid: git::Oid,
|
||||
) -> Result<(), Error> {
|
||||
@ -442,7 +443,7 @@ impl Controller {
|
||||
|
||||
pub async fn unapply_virtual_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
branch_id: &str,
|
||||
) -> Result<(), Error> {
|
||||
self.with_lock(project_id, || {
|
||||
@ -456,7 +457,7 @@ impl Controller {
|
||||
|
||||
pub async fn push_virtual_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
branch_id: &str,
|
||||
with_force: bool,
|
||||
) -> Result<(), Error> {
|
||||
@ -497,7 +498,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 +519,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())
|
||||
|
@ -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,10 +64,9 @@ 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({
|
||||
@ -79,12 +78,12 @@ impl Dispatcher {
|
||||
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();
|
||||
@ -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,36 +1,38 @@
|
||||
use std::{fmt::Display, path, time};
|
||||
|
||||
use crate::{
|
||||
analytics, bookmarks, deltas, events, reader,
|
||||
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, SessionId, path::PathBuf, Option<reader::Content>)),
|
||||
SessionDelta((String, SessionId, 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")),
|
||||
@ -71,7 +79,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(),
|
||||
}),
|
||||
@ -98,7 +106,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
|
||||
@ -119,7 +127,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()
|
||||
})
|
||||
@ -138,7 +146,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()
|
||||
})
|
||||
|
@ -1,7 +1,13 @@
|
||||
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,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
|
||||
@ -50,8 +56,8 @@ impl Handler {
|
||||
.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,7 +64,7 @@ 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
|
||||
@ -85,7 +88,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)),
|
||||
|
@ -6,7 +6,9 @@ use tauri::{AppHandle, Manager};
|
||||
use crate::{
|
||||
bookmarks, deltas, events as app_events, gb_repository,
|
||||
paths::DataDir,
|
||||
project_repository, projects, search,
|
||||
project_repository,
|
||||
projects::{self, ProjectId},
|
||||
search,
|
||||
sessions::{self, SessionId},
|
||||
users,
|
||||
};
|
||||
@ -43,7 +45,7 @@ impl TryFrom<&AppHandle> for Handler {
|
||||
impl Handler {
|
||||
pub fn index_deltas(
|
||||
&self,
|
||||
project_id: &str,
|
||||
project_id: &ProjectId,
|
||||
session_id: &SessionId,
|
||||
file_path: &path::Path,
|
||||
deltas: &Vec<deltas::Delta>,
|
||||
@ -56,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)?;
|
||||
@ -70,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)
|
||||
@ -92,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,14 +167,14 @@ impl Handler {
|
||||
|
||||
Ok(vec![
|
||||
events::Event::SessionFile((
|
||||
project_id.to_string(),
|
||||
*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(),
|
||||
*project_id,
|
||||
current_session.id,
|
||||
path.to_path_buf(),
|
||||
new_delta.clone(),
|
||||
|
@ -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)?;
|
||||
@ -48,10 +58,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
|
||||
@ -60,10 +67,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
|
||||
@ -71,10 +75,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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
@ -48,15 +48,15 @@ impl Watchers {
|
||||
.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