display errors when adding project

This commit is contained in:
Nikita Galaiko 2023-02-24 16:27:28 +01:00
parent a6a3df025e
commit 1ff419a8c8
7 changed files with 168 additions and 209 deletions

1
src-tauri/Cargo.lock generated
View File

@ -1016,6 +1016,7 @@ dependencies = [
"tauri-plugin-log",
"tauri-plugin-window-state",
"tempfile",
"thiserror",
"urlencoding",
"uuid 1.3.0",
"walkdir",

View File

@ -34,6 +34,7 @@ tempfile = "3.3.0"
reqwest = "0.11.14"
md5 = "0.7.0"
urlencoding = "2.1.2"
thiserror = "1.0.38"
[features]
# by default Tauri runs in production mode

View File

@ -11,7 +11,7 @@ mod watchers;
use anyhow::{Context, Result};
use deltas::Delta;
use log;
use serde::{Deserialize, Serialize};
use serde::{ser::SerializeMap, Serialize};
use std::{collections::HashMap, sync::mpsc, thread};
use storage::Storage;
use tauri::{generate_context, Manager};
@ -19,10 +19,40 @@ use tauri_plugin_log::{
fern::colors::{Color, ColoredLevelConfig},
LogTarget,
};
use thiserror::Error;
#[derive(Debug, Serialize, Deserialize)]
pub struct Error {
pub message: String,
#[derive(Debug, Error)]
pub enum Error {
#[error("{0}")]
ProjectError(projects::CreateError),
#[error("Project already exists")]
ProjectAlreadyExists,
#[error("Something went wrong")]
Unknown,
}
impl Serialize for Error {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry("message", &self.to_string())?;
map.end()
}
}
impl From<projects::CreateError> for Error {
fn from(e: projects::CreateError) -> Self {
Error::ProjectError(e)
}
}
impl From<anyhow::Error> for Error {
fn from(e: anyhow::Error) -> Self {
log::error!("{:#}", e);
Error::Unknown
}
}
const IS_DEV: bool = cfg!(debug_assertions);
@ -85,19 +115,11 @@ fn list_sessions(
let users_storage = users::Storage::new(storage);
let repo = repositories::Repository::open(&projects_storage, &users_storage, project_id)
.map_err(|e| {
log::error!("{:#}", e);
Error {
message: "Failed to open project".to_string(),
}
})?;
.with_context(|| format!("Failed to open repository for project {}", project_id))?;
let sessions = repo.sessions().map_err(|e| {
log::error!("{:#}", e);
Error {
message: "Failed to list sessions".to_string(),
}
})?;
let sessions = repo
.sessions()
.with_context(|| format!("Failed to list sessions for project {}", project_id))?;
Ok(sessions)
}
@ -108,12 +130,10 @@ fn get_user(handle: tauri::AppHandle) -> Result<Option<users::User>, Error> {
let storage = storage::Storage::from_path_resolver(&path_resolver);
let users_storage = users::Storage::new(storage);
match users_storage.get().map_err(|e| {
log::error!("{:#}", e);
Error {
message: "Failed to get user".to_string(),
}
})? {
match users_storage
.get()
.with_context(|| "Failed to get user".to_string())?
{
Some(user) => {
let local_picture = match proxy_image(handle, &user.picture) {
Ok(picture) => picture,
@ -140,12 +160,9 @@ fn set_user(handle: tauri::AppHandle, user: users::User) -> Result<(), Error> {
let storage = storage::Storage::from_path_resolver(&path_resolver);
let users_storage = users::Storage::new(storage);
users_storage.set(&user).map_err(|e| {
log::error!("{:#}", e);
Error {
message: "Failed to save user".to_string(),
}
})?;
users_storage
.set(&user)
.with_context(|| "Failed to set user".to_string())?;
sentry::configure_scope(|scope| scope.set_user(Some(user.clone().into())));
@ -158,12 +175,9 @@ fn delete_user(handle: tauri::AppHandle) -> Result<(), Error> {
let storage = storage::Storage::from_path_resolver(&path_resolver);
let users_storage = users::Storage::new(storage);
users_storage.delete().map_err(|e| {
log::error!("{:#}", e);
Error {
message: "Failed to delete user".to_string(),
}
})?;
users_storage
.delete()
.with_context(|| "Failed to delete user".to_string())?;
sentry::configure_scope(|scope| scope.set_user(None));
@ -179,12 +193,11 @@ fn update_project(
let storage = storage::Storage::from_path_resolver(&path_resolver);
let projects_storage = projects::Storage::new(storage);
projects_storage.update_project(&project).map_err(|e| {
log::error!("{:#}", e);
Error {
message: "Failed to update project".to_string(),
}
})
let project = projects_storage
.update_project(&project)
.with_context(|| format!("Failed to update project {}", project.id))?;
Ok(project)
}
#[tauri::command]
@ -200,63 +213,32 @@ fn add_project(handle: tauri::AppHandle, path: &str) -> Result<projects::Project
users_storage.clone(),
);
for project in projects_storage.list_projects().map_err(|e| {
log::error!("{:#}", e);
Error {
message: "Failed to list projects".to_string(),
}
})? {
for project in projects_storage
.list_projects()
.with_context(|| "Failed to list projects".to_string())?
{
if project.path == path {
if !project.deleted {
return Err(Error {
message: "Project already exists".to_string(),
});
return Err(Error::ProjectAlreadyExists);
} else {
projects_storage
.update_project(&projects::UpdateRequest {
projects_storage.update_project(&projects::UpdateRequest {
id: project.id.clone(),
deleted: Some(false),
..Default::default()
})
.map_err(|e| {
log::error!("{:#}", e);
Error {
message: "Failed to undelete project".to_string(),
}
})?;
return Ok(project);
}
}
}
let project = projects::Project::from_path(path.to_string());
if project.is_ok() {
let project = project.unwrap();
projects_storage.add_project(&project).map_err(|e| {
log::error!("{:#}", e);
Error {
message: "Failed to add project".to_string(),
}
})?;
let (tx, rx): (mpsc::Sender<events::Event>, mpsc::Receiver<events::Event>) =
mpsc::channel();
watchers.watch(tx, &project).map_err(|e| {
log::error!("{:#}", e);
Error {
message: "Failed to watch project".to_string(),
}
})?;
let project = projects::Project::from_path(path.to_string())?;
projects_storage.add_project(&project)?;
let (tx, rx): (mpsc::Sender<events::Event>, mpsc::Receiver<events::Event>) = mpsc::channel();
watchers.watch(tx, &project)?;
watch_events(handle, rx);
return Ok(project);
} else {
return Err(Error {
message: "Failed to add project".to_string(),
});
}
Ok(project)
}
#[tauri::command]
@ -265,12 +247,9 @@ fn list_projects(handle: tauri::AppHandle) -> Result<Vec<projects::Project>, Err
let storage = storage::Storage::from_path_resolver(&path_resolver);
let projects_storage = projects::Storage::new(storage);
projects_storage.list_projects().map_err(|e| {
log::error!("{:#}", e);
Error {
message: "Failed to list projects".to_string(),
}
})
let projects = projects_storage.list_projects()?;
Ok(projects)
}
#[tauri::command]
@ -286,37 +265,19 @@ fn delete_project(handle: tauri::AppHandle, id: &str) -> Result<(), Error> {
users_storage.clone(),
);
match projects_storage.get_project(id) {
Ok(Some(project)) => {
watchers.unwatch(project).map_err(|e| {
log::error!("{:#}", e);
Error {
message: "Failed to unwatch project".to_string(),
}
})?;
match projects_storage.get_project(id)? {
Some(project) => {
watchers.unwatch(project)?;
projects_storage
.update_project(&projects::UpdateRequest {
projects_storage.update_project(&projects::UpdateRequest {
id: id.to_string(),
deleted: Some(true),
..Default::default()
})
.map_err(|e| {
log::error!("{:#}", e);
Error {
message: "Failed to delete project".to_string(),
}
})?;
Ok(())
}
Ok(None) => Ok(()),
Err(e) => {
log::error!("{:#}", e);
Err(Error {
message: "Failed to get project".to_string(),
})
}
None => Ok(()),
}
}
@ -332,20 +293,9 @@ fn list_session_files(
let projects_storage = projects::Storage::new(storage.clone());
let users_storage = users::Storage::new(storage);
let repo = repositories::Repository::open(&projects_storage, &users_storage, project_id)
.map_err(|e| {
log::error!("{:#}", e);
Error {
message: "Failed to open project".to_string(),
}
})?;
let repo = repositories::Repository::open(&projects_storage, &users_storage, project_id)?;
let files = repo.files(session_id, paths).map_err(|e| {
log::error!("{:#}", e);
Error {
message: "Failed to list files".to_string(),
}
})?;
let files = repo.files(session_id, paths)?;
Ok(files)
}
@ -361,20 +311,9 @@ fn list_deltas(
let projects_storage = projects::Storage::new(storage.clone());
let users_storage = users::Storage::new(storage);
let repo = repositories::Repository::open(&projects_storage, &users_storage, project_id)
.map_err(|e| {
log::error!("{:#}", e);
Error {
message: "Failed to open project".to_string(),
}
})?;
let repo = repositories::Repository::open(&projects_storage, &users_storage, project_id)?;
let deltas = repo.deltas(session_id).map_err(|e| {
log::error!("{:#}", e);
Error {
message: "Failed to list deltas".to_string(),
}
})?;
let deltas = repo.deltas(session_id)?;
Ok(deltas)
}

View File

@ -1,6 +1,5 @@
mod project;
mod storage;
pub use project::Project;
pub use storage::Storage;
pub use storage::UpdateRequest;
pub use project::{CreateError, Project};
pub use storage::{Storage, UpdateRequest};

View File

@ -1,7 +1,7 @@
use std::path::PathBuf;
use anyhow::{anyhow, Result};
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ApiProject {
@ -31,6 +31,16 @@ impl AsRef<Project> for Project {
}
}
#[derive(Error, Debug)]
pub enum CreateError {
#[error("{0} does not exist")]
PathNotFound(String),
#[error("{0} is not a directory")]
NotADirectory(String),
#[error("{0} is not a git repository")]
NotAGitRepository(String),
}
impl Project {
pub fn refname(&self) -> String {
format!("refs/gitbutler-{}/current", self.id)
@ -47,34 +57,40 @@ impl Project {
self.session_path().join("deltas")
}
pub fn from_path(path: String) -> Result<Self> {
pub fn from_path(fpath: String) -> Result<Self, CreateError> {
// make sure path exists
let path = std::path::Path::new(&path);
let path = std::path::Path::new(&fpath);
if !path.exists() {
return Err(anyhow!("path {} does not exist", path.display()));
return Err(CreateError::PathNotFound(fpath).into());
}
// make sure path is a directory
if !path.is_dir() {
return Err(anyhow!("path {} is not a directory", path.display()));
return Err(CreateError::NotADirectory(fpath).into());
}
// make sure it's a git repository
if !path.join(".git").exists() {
return Err(anyhow!("path {} is not a git repository", path.display()));
return Err(CreateError::NotAGitRepository(fpath).into());
};
let id = uuid::Uuid::new_v4().to_string();
// title is the base name of the file
path.into_iter()
let title = path
.into_iter()
.last()
.map(|p| p.to_str().unwrap().to_string())
.map(|title| Self {
.unwrap_or_else(|| id.clone());
let project = Project {
id: uuid::Uuid::new_v4().to_string(),
deleted: false,
title,
path: path.to_str().unwrap().to_string(),
api: None,
})
.ok_or_else(|| anyhow!("failed to get title from path"))
};
Ok(project)
}
}

View File

@ -1,6 +1,8 @@
<script lang="ts">
import { open } from '@tauri-apps/api/dialog';
import type { LayoutData } from './$types';
import { toasts } from '$lib';
export let data: LayoutData;
const { projects } = data;
@ -14,10 +16,11 @@
if (Array.isArray(selectedPath) && selectedPath.length !== 1) return;
const projectPath = Array.isArray(selectedPath) ? selectedPath[0] : selectedPath;
const projectExists = $projects.some((p) => p.path === projectPath);
if (projectExists) return;
try {
await projects.add({ path: projectPath });
} catch (e: any) {
toasts.error(e.message);
}
};
</script>