移除Unknown错误码
This commit is contained in:
@@ -6,7 +6,6 @@ use crate::db::error::DbError;
|
|||||||
#[derive(Debug, Clone, Copy, Serialize)]
|
#[derive(Debug, Clone, Copy, Serialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum AppErrorCode {
|
pub enum AppErrorCode {
|
||||||
Unknown,
|
|
||||||
InvalidInput,
|
InvalidInput,
|
||||||
ConfigurationMissing,
|
ConfigurationMissing,
|
||||||
ConfigurationInvalid,
|
ConfigurationInvalid,
|
||||||
@@ -55,6 +54,10 @@ impl AppCommandError {
|
|||||||
Self::new(AppErrorCode::InvalidInput, message)
|
Self::new(AppErrorCode::InvalidInput, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn configuration_missing(message: impl Into<String>) -> Self {
|
||||||
|
Self::new(AppErrorCode::ConfigurationMissing, message)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn configuration_invalid(message: impl Into<String>) -> Self {
|
pub fn configuration_invalid(message: impl Into<String>) -> Self {
|
||||||
Self::new(AppErrorCode::ConfigurationInvalid, message)
|
Self::new(AppErrorCode::ConfigurationInvalid, message)
|
||||||
}
|
}
|
||||||
@@ -63,10 +66,34 @@ impl AppCommandError {
|
|||||||
Self::new(AppErrorCode::NotFound, message)
|
Self::new(AppErrorCode::NotFound, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn already_exists(message: impl Into<String>) -> Self {
|
||||||
|
Self::new(AppErrorCode::AlreadyExists, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn permission_denied(message: impl Into<String>) -> Self {
|
||||||
|
Self::new(AppErrorCode::PermissionDenied, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dependency_missing(message: impl Into<String>) -> Self {
|
||||||
|
Self::new(AppErrorCode::DependencyMissing, message)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn network(message: impl Into<String>) -> Self {
|
pub fn network(message: impl Into<String>) -> Self {
|
||||||
Self::new(AppErrorCode::NetworkError, message)
|
Self::new(AppErrorCode::NetworkError, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn authentication_failed(message: impl Into<String>) -> Self {
|
||||||
|
Self::new(AppErrorCode::AuthenticationFailed, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn database_error(message: impl Into<String>) -> Self {
|
||||||
|
Self::new(AppErrorCode::DatabaseError, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn io_error(message: impl Into<String>) -> Self {
|
||||||
|
Self::new(AppErrorCode::IoError, message)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn task_execution_failed(message: impl Into<String>) -> Self {
|
pub fn task_execution_failed(message: impl Into<String>) -> Self {
|
||||||
Self::new(AppErrorCode::TaskExecutionFailed, message)
|
Self::new(AppErrorCode::TaskExecutionFailed, message)
|
||||||
}
|
}
|
||||||
@@ -104,15 +131,3 @@ impl From<DbError> for AppCommandError {
|
|||||||
Self::db(value)
|
Self::db(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<String> for AppCommandError {
|
|
||||||
fn from(value: String) -> Self {
|
|
||||||
Self::new(AppErrorCode::Unknown, "Operation failed").with_detail(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for AppCommandError {
|
|
||||||
fn from(value: &str) -> Self {
|
|
||||||
Self::from(value.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use tauri::Emitter;
|
|||||||
use tokio::sync::Semaphore;
|
use tokio::sync::Semaphore;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::app_error::{AppCommandError, AppErrorCode};
|
use crate::app_error::AppCommandError;
|
||||||
use crate::db::error::DbError;
|
use crate::db::error::DbError;
|
||||||
use crate::db::service::folder_service;
|
use crate::db::service::folder_service;
|
||||||
use crate::db::AppDatabase;
|
use crate::db::AppDatabase;
|
||||||
@@ -315,8 +315,7 @@ pub async fn set_folder_parent_branch(
|
|||||||
.one(&db.conn)
|
.one(&db.conn)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
AppCommandError::new(AppErrorCode::DatabaseError, "Failed to query folder")
|
AppCommandError::database_error("Failed to query folder").with_detail(e.to_string())
|
||||||
.with_detail(e.to_string())
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if let Some(folder_model) = row {
|
if let Some(folder_model) = row {
|
||||||
@@ -354,8 +353,7 @@ pub async fn create_folder_directory(path: String) -> Result<(), AppCommandError
|
|||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn clone_repository(url: String, target_dir: String) -> Result<(), AppCommandError> {
|
pub async fn clone_repository(url: String, target_dir: String) -> Result<(), AppCommandError> {
|
||||||
if url.trim().is_empty() || target_dir.trim().is_empty() {
|
if url.trim().is_empty() || target_dir.trim().is_empty() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input(
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Repository URL and target directory are required",
|
"Repository URL and target directory are required",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -366,8 +364,7 @@ pub async fn clone_repository(url: String, target_dir: String) -> Result<(), App
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
if e.kind() == std::io::ErrorKind::NotFound {
|
if e.kind() == std::io::ErrorKind::NotFound {
|
||||||
AppCommandError::new(
|
AppCommandError::dependency_missing(
|
||||||
AppErrorCode::DependencyMissing,
|
|
||||||
"Git is not installed. Please install Git first.",
|
"Git is not installed. Please install Git first.",
|
||||||
)
|
)
|
||||||
.with_detail("https://git-scm.com")
|
.with_detail("https://git-scm.com")
|
||||||
@@ -387,16 +384,12 @@ fn classify_git_clone_error(stderr: &str) -> AppCommandError {
|
|||||||
let normalized = stderr.to_lowercase();
|
let normalized = stderr.to_lowercase();
|
||||||
|
|
||||||
if normalized.contains("already exists and is not an empty directory") {
|
if normalized.contains("already exists and is not an empty directory") {
|
||||||
return AppCommandError::new(
|
return AppCommandError::already_exists("Target directory already exists and is not empty")
|
||||||
AppErrorCode::AlreadyExists,
|
.with_detail(stderr.to_string());
|
||||||
"Target directory already exists and is not empty",
|
|
||||||
)
|
|
||||||
.with_detail(stderr.to_string());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if normalized.contains("repository not found") {
|
if normalized.contains("repository not found") {
|
||||||
return AppCommandError::new(
|
return AppCommandError::not_found(
|
||||||
AppErrorCode::NotFound,
|
|
||||||
"Repository not found. Check URL and access permissions.",
|
"Repository not found. Check URL and access permissions.",
|
||||||
)
|
)
|
||||||
.with_detail(stderr.to_string());
|
.with_detail(stderr.to_string());
|
||||||
@@ -407,30 +400,23 @@ fn classify_git_clone_error(stderr: &str) -> AppCommandError {
|
|||||||
|| normalized.contains("connection timed out")
|
|| normalized.contains("connection timed out")
|
||||||
|| normalized.contains("failed to connect")
|
|| normalized.contains("failed to connect")
|
||||||
{
|
{
|
||||||
return AppCommandError::new(
|
return AppCommandError::network("Network is unavailable while cloning repository")
|
||||||
AppErrorCode::NetworkError,
|
.with_detail(stderr.to_string());
|
||||||
"Network is unavailable while cloning repository",
|
|
||||||
)
|
|
||||||
.with_detail(stderr.to_string());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if normalized.contains("authentication failed")
|
if normalized.contains("authentication failed")
|
||||||
|| normalized.contains("could not read username")
|
|| normalized.contains("could not read username")
|
||||||
|| normalized.contains("permission denied (publickey)")
|
|| normalized.contains("permission denied (publickey)")
|
||||||
{
|
{
|
||||||
return AppCommandError::new(
|
return AppCommandError::authentication_failed(
|
||||||
AppErrorCode::AuthenticationFailed,
|
|
||||||
"Authentication failed while cloning repository",
|
"Authentication failed while cloning repository",
|
||||||
)
|
)
|
||||||
.with_detail(stderr.to_string());
|
.with_detail(stderr.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
if normalized.contains("permission denied") {
|
if normalized.contains("permission denied") {
|
||||||
return AppCommandError::new(
|
return AppCommandError::permission_denied("Permission denied while cloning repository")
|
||||||
AppErrorCode::PermissionDenied,
|
.with_detail(stderr.to_string());
|
||||||
"Permission denied while cloning repository",
|
|
||||||
)
|
|
||||||
.with_detail(stderr.to_string());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AppCommandError::external_command("Git clone failed", stderr.to_string())
|
AppCommandError::external_command("Git clone failed", stderr.to_string())
|
||||||
@@ -610,18 +596,16 @@ pub async fn git_worktree_add(
|
|||||||
.map_err(AppCommandError::io)?;
|
.map_err(AppCommandError::io)?;
|
||||||
if check.status.success() {
|
if check.status.success() {
|
||||||
return Err(
|
return Err(
|
||||||
AppCommandError::new(AppErrorCode::AlreadyExists, "Branch already exists")
|
AppCommandError::already_exists("Branch already exists").with_detail(branch_name)
|
||||||
.with_detail(branch_name),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验目录是否已存在
|
// 校验目录是否已存在
|
||||||
if std::path::Path::new(&worktree_path).exists() {
|
if std::path::Path::new(&worktree_path).exists() {
|
||||||
return Err(AppCommandError::new(
|
return Err(
|
||||||
AppErrorCode::AlreadyExists,
|
AppCommandError::already_exists("Worktree directory already exists")
|
||||||
"Worktree directory already exists",
|
.with_detail(worktree_path),
|
||||||
)
|
);
|
||||||
.with_detail(worktree_path));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行 git worktree add -b <branch> <path>
|
// 执行 git worktree add -b <branch> <path>
|
||||||
@@ -786,8 +770,7 @@ pub async fn git_diff_with_branch(
|
|||||||
) -> Result<String, AppCommandError> {
|
) -> Result<String, AppCommandError> {
|
||||||
let target_branch = branch.trim();
|
let target_branch = branch.trim();
|
||||||
if target_branch.is_empty() {
|
if target_branch.is_empty() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input(
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Branch name cannot be empty",
|
"Branch name cannot be empty",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -876,11 +859,9 @@ pub async fn git_show_file(
|
|||||||
|
|
||||||
let bytes = &output.stdout;
|
let bytes = &output.stdout;
|
||||||
if bytes.iter().take(2048).any(|b| *b == 0) {
|
if bytes.iter().take(2048).any(|b| *b == 0) {
|
||||||
return Err(AppCommandError::new(
|
return Err(
|
||||||
AppErrorCode::InvalidInput,
|
AppCommandError::invalid_input("Binary files are not supported").with_detail(file_spec),
|
||||||
"Binary files are not supported",
|
);
|
||||||
)
|
|
||||||
.with_detail(file_spec));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(String::from_utf8_lossy(bytes).to_string())
|
Ok(String::from_utf8_lossy(bytes).to_string())
|
||||||
@@ -946,10 +927,7 @@ pub async fn git_commit(
|
|||||||
pub async fn git_rollback_file(path: String, file: String) -> Result<(), AppCommandError> {
|
pub async fn git_rollback_file(path: String, file: String) -> Result<(), AppCommandError> {
|
||||||
let target = file.trim();
|
let target = file.trim();
|
||||||
if target.is_empty() {
|
if target.is_empty() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input("File path cannot be empty"));
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"File path cannot be empty",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let literal_file = to_git_literal_pathspec(target);
|
let literal_file = to_git_literal_pathspec(target);
|
||||||
@@ -1294,8 +1272,7 @@ fn should_refresh_git_status_for_paths(root_display: &str, changed_paths: &[Stri
|
|||||||
|
|
||||||
fn canonicalize_watch_root(root: &Path) -> Result<(PathBuf, String), AppCommandError> {
|
fn canonicalize_watch_root(root: &Path) -> Result<(PathBuf, String), AppCommandError> {
|
||||||
let canonical = std::fs::canonicalize(root).map_err(|e| {
|
let canonical = std::fs::canonicalize(root).map_err(|e| {
|
||||||
AppCommandError::new(AppErrorCode::NotFound, "Unable to resolve workspace root")
|
AppCommandError::not_found("Unable to resolve workspace root").with_detail(e.to_string())
|
||||||
.with_detail(e.to_string())
|
|
||||||
})?;
|
})?;
|
||||||
let key = normalize_slash_path(&canonical);
|
let key = normalize_slash_path(&canonical);
|
||||||
Ok((canonical, key))
|
Ok((canonical, key))
|
||||||
@@ -1532,26 +1509,17 @@ fn run_file_watch_event_loop(
|
|||||||
fn resolve_tree_path(root: &Path, rel_path: &str) -> Result<PathBuf, AppCommandError> {
|
fn resolve_tree_path(root: &Path, rel_path: &str) -> Result<PathBuf, AppCommandError> {
|
||||||
let rel = Path::new(rel_path);
|
let rel = Path::new(rel_path);
|
||||||
if rel.is_absolute() {
|
if rel.is_absolute() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input("Path must be relative"));
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Path must be relative",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for component in rel.components() {
|
for component in rel.components() {
|
||||||
match component {
|
match component {
|
||||||
Component::Normal(_) | Component::CurDir => {}
|
Component::Normal(_) | Component::CurDir => {}
|
||||||
Component::ParentDir => {
|
Component::ParentDir => {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input("Path cannot contain '..'"));
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Path cannot contain '..'",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
Component::RootDir | Component::Prefix(_) => {
|
Component::RootDir | Component::Prefix(_) => {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input("Invalid path component"));
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Invalid path component",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1562,20 +1530,13 @@ fn resolve_tree_path(root: &Path, rel_path: &str) -> Result<PathBuf, AppCommandE
|
|||||||
fn validate_new_name(new_name: &str) -> Result<&str, AppCommandError> {
|
fn validate_new_name(new_name: &str) -> Result<&str, AppCommandError> {
|
||||||
let trimmed = new_name.trim();
|
let trimmed = new_name.trim();
|
||||||
if trimmed.is_empty() {
|
if trimmed.is_empty() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input("New name cannot be empty"));
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"New name cannot be empty",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if trimmed == "." || trimmed == ".." {
|
if trimmed == "." || trimmed == ".." {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input("Invalid file name"));
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Invalid file name",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if trimmed.contains('/') || trimmed.contains('\\') {
|
if trimmed.contains('/') || trimmed.contains('\\') {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input(
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"New name cannot contain path separators",
|
"New name cannot contain path separators",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -1589,10 +1550,7 @@ pub async fn start_file_tree_watch(
|
|||||||
) -> Result<(), AppCommandError> {
|
) -> Result<(), AppCommandError> {
|
||||||
let root = PathBuf::from(&root_path);
|
let root = PathBuf::from(&root_path);
|
||||||
if !root.exists() || !root.is_dir() {
|
if !root.exists() || !root.is_dir() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::not_found("Folder does not exist"));
|
||||||
AppErrorCode::NotFound,
|
|
||||||
"Folder does not exist",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let (root_canonical, key) = canonicalize_watch_root(&root)?;
|
let (root_canonical, key) = canonicalize_watch_root(&root)?;
|
||||||
@@ -1636,8 +1594,7 @@ pub async fn start_file_tree_watch(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
AppCommandError::new(AppErrorCode::IoError, "Failed to create file watcher")
|
AppCommandError::io_error("Failed to create file watcher").with_detail(e.to_string())
|
||||||
.with_detail(e.to_string())
|
|
||||||
})?,
|
})?,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1646,8 +1603,7 @@ pub async fn start_file_tree_watch(
|
|||||||
.ok_or_else(|| AppCommandError::task_execution_failed("Failed to create file watcher"))?
|
.ok_or_else(|| AppCommandError::task_execution_failed("Failed to create file watcher"))?
|
||||||
.watch(&root_canonical, RecursiveMode::Recursive)
|
.watch(&root_canonical, RecursiveMode::Recursive)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
AppCommandError::new(AppErrorCode::IoError, "Failed to start file watcher")
|
AppCommandError::io_error("Failed to start file watcher").with_detail(e.to_string())
|
||||||
.with_detail(e.to_string())
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let should_cleanup_new_watcher = {
|
let should_cleanup_new_watcher = {
|
||||||
@@ -1789,8 +1745,7 @@ fn ensure_path_in_workspace(root: &Path, target: &Path) -> Result<(), AppCommand
|
|||||||
let canonical_root = std::fs::canonicalize(root).map_err(AppCommandError::io)?;
|
let canonical_root = std::fs::canonicalize(root).map_err(AppCommandError::io)?;
|
||||||
let canonical_target = std::fs::canonicalize(target).map_err(AppCommandError::io)?;
|
let canonical_target = std::fs::canonicalize(target).map_err(AppCommandError::io)?;
|
||||||
if !canonical_target.starts_with(&canonical_root) {
|
if !canonical_target.starts_with(&canonical_root) {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input(
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Path is outside workspace root",
|
"Path is outside workspace root",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -1807,8 +1762,7 @@ fn read_text_preview(target: &Path, limit: usize) -> Result<(String, bool), AppC
|
|||||||
.map_err(AppCommandError::io)?;
|
.map_err(AppCommandError::io)?;
|
||||||
|
|
||||||
if bytes.iter().take(2_048).any(|b| *b == 0) {
|
if bytes.iter().take(2_048).any(|b| *b == 0) {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input(
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Binary files are not supported in preview",
|
"Binary files are not supported in preview",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -1822,18 +1776,14 @@ fn read_text_preview(target: &Path, limit: usize) -> Result<(String, bool), AppC
|
|||||||
|
|
||||||
fn atomic_write_text(path: &Path, bytes: &[u8]) -> Result<(), AppCommandError> {
|
fn atomic_write_text(path: &Path, bytes: &[u8]) -> Result<(), AppCommandError> {
|
||||||
let parent = path.parent().ok_or_else(|| {
|
let parent = path.parent().ok_or_else(|| {
|
||||||
AppCommandError::new(
|
AppCommandError::invalid_input("Cannot determine parent directory for target file")
|
||||||
AppErrorCode::InvalidInput,
|
.with_detail(path.display().to_string())
|
||||||
"Cannot determine parent directory for target file",
|
|
||||||
)
|
|
||||||
.with_detail(path.display().to_string())
|
|
||||||
})?;
|
})?;
|
||||||
if !parent.exists() {
|
if !parent.exists() {
|
||||||
return Err(AppCommandError::new(
|
return Err(
|
||||||
AppErrorCode::NotFound,
|
AppCommandError::not_found("Parent directory does not exist")
|
||||||
"Parent directory does not exist",
|
.with_detail(parent.display().to_string()),
|
||||||
)
|
);
|
||||||
.with_detail(parent.display().to_string()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let temp_path = parent.join(format!(
|
let temp_path = parent.join(format!(
|
||||||
@@ -1902,11 +1852,10 @@ fn replace_file(temp_path: &Path, target_path: &Path) -> Result<(), AppCommandEr
|
|||||||
};
|
};
|
||||||
|
|
||||||
if ok == 0 {
|
if ok == 0 {
|
||||||
return Err(AppCommandError::new(
|
return Err(
|
||||||
AppErrorCode::IoError,
|
AppCommandError::io_error("Failed to atomically replace file")
|
||||||
"Failed to atomically replace file",
|
.with_detail(std::io::Error::last_os_error().to_string()),
|
||||||
)
|
);
|
||||||
.with_detail(std::io::Error::last_os_error().to_string()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1970,8 +1919,7 @@ pub async fn get_file_tree(
|
|||||||
})
|
})
|
||||||
{
|
{
|
||||||
let entry = entry.map_err(|e| {
|
let entry = entry.map_err(|e| {
|
||||||
AppCommandError::new(AppErrorCode::IoError, "Failed to walk file tree")
|
AppCommandError::io_error("Failed to walk file tree").with_detail(e.to_string())
|
||||||
.with_detail(e.to_string())
|
|
||||||
})?;
|
})?;
|
||||||
let entry_path = entry.path().to_path_buf();
|
let entry_path = entry.path().to_path_buf();
|
||||||
|
|
||||||
@@ -2088,24 +2036,15 @@ pub async fn read_file_preview(
|
|||||||
) -> Result<FilePreviewContent, AppCommandError> {
|
) -> Result<FilePreviewContent, AppCommandError> {
|
||||||
let root = PathBuf::from(&root_path);
|
let root = PathBuf::from(&root_path);
|
||||||
if !root.exists() || !root.is_dir() {
|
if !root.exists() || !root.is_dir() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::not_found("Folder does not exist"));
|
||||||
AppErrorCode::NotFound,
|
|
||||||
"Folder does not exist",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = resolve_tree_path(&root, &path)?;
|
let target = resolve_tree_path(&root, &path)?;
|
||||||
if !target.exists() {
|
if !target.exists() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::not_found("File does not exist"));
|
||||||
AppErrorCode::NotFound,
|
|
||||||
"File does not exist",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if !target.is_file() {
|
if !target.is_file() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input("Path is not a file"));
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Path is not a file",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
let limit = max_bytes
|
let limit = max_bytes
|
||||||
.unwrap_or(FILE_PREVIEW_DEFAULT_MAX_BYTES)
|
.unwrap_or(FILE_PREVIEW_DEFAULT_MAX_BYTES)
|
||||||
@@ -2132,24 +2071,15 @@ pub async fn read_file_for_edit(
|
|||||||
) -> Result<FileEditContent, AppCommandError> {
|
) -> Result<FileEditContent, AppCommandError> {
|
||||||
let root = PathBuf::from(&root_path);
|
let root = PathBuf::from(&root_path);
|
||||||
if !root.exists() || !root.is_dir() {
|
if !root.exists() || !root.is_dir() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::not_found("Folder does not exist"));
|
||||||
AppErrorCode::NotFound,
|
|
||||||
"Folder does not exist",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = resolve_tree_path(&root, &path)?;
|
let target = resolve_tree_path(&root, &path)?;
|
||||||
if !target.exists() {
|
if !target.exists() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::not_found("File does not exist"));
|
||||||
AppErrorCode::NotFound,
|
|
||||||
"File does not exist",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if !target.is_file() {
|
if !target.is_file() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input("Path is not a file"));
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Path is not a file",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let limit = max_bytes
|
let limit = max_bytes
|
||||||
@@ -2193,31 +2123,21 @@ pub async fn save_file_content(
|
|||||||
) -> Result<FileSaveResult, AppCommandError> {
|
) -> Result<FileSaveResult, AppCommandError> {
|
||||||
let root = PathBuf::from(&root_path);
|
let root = PathBuf::from(&root_path);
|
||||||
if !root.exists() || !root.is_dir() {
|
if !root.exists() || !root.is_dir() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::not_found("Folder does not exist"));
|
||||||
AppErrorCode::NotFound,
|
|
||||||
"Folder does not exist",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if content.len() > FILE_EDIT_MAX_BYTES {
|
if content.len() > FILE_EDIT_MAX_BYTES {
|
||||||
return Err(AppCommandError::new(
|
return Err(
|
||||||
AppErrorCode::InvalidInput,
|
AppCommandError::invalid_input("File is too large to save in editor")
|
||||||
"File is too large to save in editor",
|
.with_detail(format!("max_bytes={FILE_EDIT_MAX_BYTES}")),
|
||||||
)
|
);
|
||||||
.with_detail(format!("max_bytes={FILE_EDIT_MAX_BYTES}")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = resolve_tree_path(&root, &path)?;
|
let target = resolve_tree_path(&root, &path)?;
|
||||||
if !target.exists() {
|
if !target.exists() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::not_found("File does not exist"));
|
||||||
AppErrorCode::NotFound,
|
|
||||||
"File does not exist",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if !target.is_file() {
|
if !target.is_file() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input("Path is not a file"));
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Path is not a file",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
let path_for_response = path.clone();
|
let path_for_response = path.clone();
|
||||||
|
|
||||||
@@ -2226,32 +2146,26 @@ pub async fn save_file_content(
|
|||||||
|
|
||||||
let link_meta = std::fs::symlink_metadata(&target).map_err(AppCommandError::io)?;
|
let link_meta = std::fs::symlink_metadata(&target).map_err(AppCommandError::io)?;
|
||||||
if link_meta.file_type().is_symlink() {
|
if link_meta.file_type().is_symlink() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input(
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Saving symlink targets is not supported",
|
"Saving symlink targets is not supported",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let before_meta = std::fs::metadata(&target).map_err(AppCommandError::io)?;
|
let before_meta = std::fs::metadata(&target).map_err(AppCommandError::io)?;
|
||||||
if before_meta.permissions().readonly() {
|
if before_meta.permissions().readonly() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::permission_denied("File is read-only"));
|
||||||
AppErrorCode::PermissionDenied,
|
|
||||||
"File is read-only",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let current_bytes = std::fs::read(&target).map_err(AppCommandError::io)?;
|
let current_bytes = std::fs::read(&target).map_err(AppCommandError::io)?;
|
||||||
if current_bytes.iter().take(2_048).any(|b| *b == 0) {
|
if current_bytes.iter().take(2_048).any(|b| *b == 0) {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input(
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Binary files are not supported in editor",
|
"Binary files are not supported in editor",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let current_etag = compute_etag(¤t_bytes, &before_meta);
|
let current_etag = compute_etag(¤t_bytes, &before_meta);
|
||||||
if let Some(expected) = expected_etag {
|
if let Some(expected) = expected_etag {
|
||||||
if expected != current_etag {
|
if expected != current_etag {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input(
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"File has changed on disk. Reload the file before saving.",
|
"File has changed on disk. Reload the file before saving.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -2308,31 +2222,21 @@ pub async fn save_file_copy(
|
|||||||
) -> Result<FileSaveResult, AppCommandError> {
|
) -> Result<FileSaveResult, AppCommandError> {
|
||||||
let root = PathBuf::from(&root_path);
|
let root = PathBuf::from(&root_path);
|
||||||
if !root.exists() || !root.is_dir() {
|
if !root.exists() || !root.is_dir() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::not_found("Folder does not exist"));
|
||||||
AppErrorCode::NotFound,
|
|
||||||
"Folder does not exist",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if content.len() > FILE_EDIT_MAX_BYTES {
|
if content.len() > FILE_EDIT_MAX_BYTES {
|
||||||
return Err(AppCommandError::new(
|
return Err(
|
||||||
AppErrorCode::InvalidInput,
|
AppCommandError::invalid_input("File is too large to save in editor")
|
||||||
"File is too large to save in editor",
|
.with_detail(format!("max_bytes={FILE_EDIT_MAX_BYTES}")),
|
||||||
)
|
);
|
||||||
.with_detail(format!("max_bytes={FILE_EDIT_MAX_BYTES}")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let source = resolve_tree_path(&root, &path)?;
|
let source = resolve_tree_path(&root, &path)?;
|
||||||
if !source.exists() {
|
if !source.exists() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::not_found("File does not exist"));
|
||||||
AppErrorCode::NotFound,
|
|
||||||
"File does not exist",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if !source.is_file() {
|
if !source.is_file() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input("Path is not a file"));
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Path is not a file",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
run_file_io(move || {
|
run_file_io(move || {
|
||||||
@@ -2340,8 +2244,7 @@ pub async fn save_file_copy(
|
|||||||
|
|
||||||
let source_meta = std::fs::symlink_metadata(&source).map_err(AppCommandError::io)?;
|
let source_meta = std::fs::symlink_metadata(&source).map_err(AppCommandError::io)?;
|
||||||
if source_meta.file_type().is_symlink() {
|
if source_meta.file_type().is_symlink() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input(
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Saving symlink targets is not supported",
|
"Saving symlink targets is not supported",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -2349,10 +2252,7 @@ pub async fn save_file_copy(
|
|||||||
let parent = source
|
let parent = source
|
||||||
.parent()
|
.parent()
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
AppCommandError::new(
|
AppCommandError::invalid_input("Cannot determine parent directory for source file")
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Cannot determine parent directory for source file",
|
|
||||||
)
|
|
||||||
})?
|
})?
|
||||||
.to_path_buf();
|
.to_path_buf();
|
||||||
ensure_path_in_workspace(&root, &parent)?;
|
ensure_path_in_workspace(&root, &parent)?;
|
||||||
@@ -2360,12 +2260,7 @@ pub async fn save_file_copy(
|
|||||||
let source_name = source
|
let source_name = source
|
||||||
.file_name()
|
.file_name()
|
||||||
.map(|value| value.to_string_lossy().to_string())
|
.map(|value| value.to_string_lossy().to_string())
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| AppCommandError::invalid_input("Cannot determine source file name"))?;
|
||||||
AppCommandError::new(
|
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Cannot determine source file name",
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let mut created_path: Option<PathBuf> = None;
|
let mut created_path: Option<PathBuf> = None;
|
||||||
for attempt in 1..=9_999 {
|
for attempt in 1..=9_999 {
|
||||||
@@ -2379,8 +2274,7 @@ pub async fn save_file_copy(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let created_path = created_path.ok_or_else(|| {
|
let created_path = created_path.ok_or_else(|| {
|
||||||
AppCommandError::new(
|
AppCommandError::already_exists(
|
||||||
AppErrorCode::AlreadyExists,
|
|
||||||
"Unable to create copy file: too many existing local copies",
|
"Unable to create copy file: too many existing local copies",
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
@@ -2394,11 +2288,8 @@ pub async fn save_file_copy(
|
|||||||
let rel_path = created_path
|
let rel_path = created_path
|
||||||
.strip_prefix(&root)
|
.strip_prefix(&root)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
AppCommandError::new(
|
AppCommandError::invalid_input("Failed to compute relative path for copy")
|
||||||
AppErrorCode::InvalidInput,
|
.with_detail(e.to_string())
|
||||||
"Failed to compute relative path for copy",
|
|
||||||
)
|
|
||||||
.with_detail(e.to_string())
|
|
||||||
})?
|
})?
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.replace('\\', "/");
|
.replace('\\', "/");
|
||||||
@@ -2422,32 +2313,22 @@ pub async fn rename_file_tree_entry(
|
|||||||
) -> Result<String, AppCommandError> {
|
) -> Result<String, AppCommandError> {
|
||||||
let root = PathBuf::from(&root_path);
|
let root = PathBuf::from(&root_path);
|
||||||
if !root.exists() || !root.is_dir() {
|
if !root.exists() || !root.is_dir() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::not_found("Folder does not exist"));
|
||||||
AppErrorCode::NotFound,
|
|
||||||
"Folder does not exist",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = resolve_tree_path(&root, &path)?;
|
let target = resolve_tree_path(&root, &path)?;
|
||||||
if !target.exists() {
|
if !target.exists() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::not_found("Target file does not exist"));
|
||||||
AppErrorCode::NotFound,
|
|
||||||
"Target file does not exist",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if target == root {
|
if target == root {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input(
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Cannot rename workspace root",
|
"Cannot rename workspace root",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let parent = target.parent().ok_or_else(|| {
|
let parent = target
|
||||||
AppCommandError::new(
|
.parent()
|
||||||
AppErrorCode::InvalidInput,
|
.ok_or_else(|| AppCommandError::invalid_input("Cannot rename path without parent"))?;
|
||||||
"Cannot rename path without parent",
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
let validated_name = validate_new_name(&new_name)?;
|
let validated_name = validate_new_name(&new_name)?;
|
||||||
let next_path = parent.join(validated_name);
|
let next_path = parent.join(validated_name);
|
||||||
|
|
||||||
@@ -2455,8 +2336,7 @@ pub async fn rename_file_tree_entry(
|
|||||||
return Ok(path);
|
return Ok(path);
|
||||||
}
|
}
|
||||||
if next_path.exists() {
|
if next_path.exists() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::already_exists(
|
||||||
AppErrorCode::AlreadyExists,
|
|
||||||
"A file with this name already exists",
|
"A file with this name already exists",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -2466,11 +2346,8 @@ pub async fn rename_file_tree_entry(
|
|||||||
let rel = next_path
|
let rel = next_path
|
||||||
.strip_prefix(&root)
|
.strip_prefix(&root)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
AppCommandError::new(
|
AppCommandError::invalid_input("Failed to compute relative path")
|
||||||
AppErrorCode::InvalidInput,
|
.with_detail(e.to_string())
|
||||||
"Failed to compute relative path",
|
|
||||||
)
|
|
||||||
.with_detail(e.to_string())
|
|
||||||
})?
|
})?
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.to_string();
|
.to_string();
|
||||||
@@ -2484,22 +2361,15 @@ pub async fn delete_file_tree_entry(
|
|||||||
) -> Result<(), AppCommandError> {
|
) -> Result<(), AppCommandError> {
|
||||||
let root = PathBuf::from(&root_path);
|
let root = PathBuf::from(&root_path);
|
||||||
if !root.exists() || !root.is_dir() {
|
if !root.exists() || !root.is_dir() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::not_found("Folder does not exist"));
|
||||||
AppErrorCode::NotFound,
|
|
||||||
"Folder does not exist",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = resolve_tree_path(&root, &path)?;
|
let target = resolve_tree_path(&root, &path)?;
|
||||||
if !target.exists() {
|
if !target.exists() {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::not_found("Target file does not exist"));
|
||||||
AppErrorCode::NotFound,
|
|
||||||
"Target file does not exist",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if target == root {
|
if target == root {
|
||||||
return Err(AppCommandError::new(
|
return Err(AppCommandError::invalid_input(
|
||||||
AppErrorCode::InvalidInput,
|
|
||||||
"Cannot delete workspace root",
|
"Cannot delete workspace root",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use sea_orm::DatabaseConnection;
|
use sea_orm::DatabaseConnection;
|
||||||
use tauri::State;
|
use tauri::State;
|
||||||
|
|
||||||
use crate::app_error::{AppCommandError, AppErrorCode};
|
use crate::app_error::AppCommandError;
|
||||||
use crate::db::service::app_metadata_service;
|
use crate::db::service::app_metadata_service;
|
||||||
use crate::db::AppDatabase;
|
use crate::db::AppDatabase;
|
||||||
use crate::models::{SystemLanguageSettings, SystemProxySettings};
|
use crate::models::{SystemLanguageSettings, SystemProxySettings};
|
||||||
@@ -33,15 +33,11 @@ fn normalize_proxy_settings(
|
|||||||
.map(str::trim)
|
.map(str::trim)
|
||||||
.filter(|value| !value.is_empty())
|
.filter(|value| !value.is_empty())
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
AppCommandError::new(
|
AppCommandError::configuration_missing("Proxy URL is required when proxy is enabled")
|
||||||
AppErrorCode::ConfigurationMissing,
|
|
||||||
"Proxy URL is required when proxy is enabled",
|
|
||||||
)
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
reqwest::Proxy::all(proxy_url).map_err(|e| {
|
reqwest::Proxy::all(proxy_url).map_err(|e| {
|
||||||
AppCommandError::new(AppErrorCode::ConfigurationInvalid, "Invalid proxy URL")
|
AppCommandError::configuration_invalid("Invalid proxy URL").with_detail(e.to_string())
|
||||||
.with_detail(e.to_string())
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(SystemProxySettings {
|
Ok(SystemProxySettings {
|
||||||
@@ -62,11 +58,8 @@ pub(crate) async fn load_system_proxy_settings(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let parsed = serde_json::from_str::<SystemProxySettings>(&raw).map_err(|e| {
|
let parsed = serde_json::from_str::<SystemProxySettings>(&raw).map_err(|e| {
|
||||||
AppCommandError::new(
|
AppCommandError::configuration_invalid("Failed to parse stored proxy settings")
|
||||||
AppErrorCode::ConfigurationInvalid,
|
.with_detail(e.to_string())
|
||||||
"Failed to parse stored proxy settings",
|
|
||||||
)
|
|
||||||
.with_detail(e.to_string())
|
|
||||||
})?;
|
})?;
|
||||||
normalize_proxy_settings(parsed)
|
normalize_proxy_settings(parsed)
|
||||||
}
|
}
|
||||||
@@ -83,11 +76,8 @@ pub(crate) async fn load_system_language_settings(
|
|||||||
};
|
};
|
||||||
|
|
||||||
serde_json::from_str::<SystemLanguageSettings>(&raw).map_err(|e| {
|
serde_json::from_str::<SystemLanguageSettings>(&raw).map_err(|e| {
|
||||||
AppCommandError::new(
|
AppCommandError::configuration_invalid("Failed to parse stored language settings")
|
||||||
AppErrorCode::ConfigurationInvalid,
|
.with_detail(e.to_string())
|
||||||
"Failed to parse stored language settings",
|
|
||||||
)
|
|
||||||
.with_detail(e.to_string())
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,11 +95,8 @@ pub async fn update_system_proxy_settings(
|
|||||||
) -> Result<SystemProxySettings, AppCommandError> {
|
) -> Result<SystemProxySettings, AppCommandError> {
|
||||||
let normalized = normalize_proxy_settings(settings)?;
|
let normalized = normalize_proxy_settings(settings)?;
|
||||||
let serialized = serde_json::to_string(&normalized).map_err(|e| {
|
let serialized = serde_json::to_string(&normalized).map_err(|e| {
|
||||||
AppCommandError::new(
|
AppCommandError::invalid_input("Failed to serialize proxy settings")
|
||||||
AppErrorCode::InvalidInput,
|
.with_detail(e.to_string())
|
||||||
"Failed to serialize proxy settings",
|
|
||||||
)
|
|
||||||
.with_detail(e.to_string())
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
app_metadata_service::upsert_value(&db.conn, SYSTEM_PROXY_SETTINGS_KEY, &serialized)
|
app_metadata_service::upsert_value(&db.conn, SYSTEM_PROXY_SETTINGS_KEY, &serialized)
|
||||||
@@ -133,11 +120,8 @@ pub async fn update_system_language_settings(
|
|||||||
db: State<'_, AppDatabase>,
|
db: State<'_, AppDatabase>,
|
||||||
) -> Result<SystemLanguageSettings, AppCommandError> {
|
) -> Result<SystemLanguageSettings, AppCommandError> {
|
||||||
let serialized = serde_json::to_string(&settings).map_err(|e| {
|
let serialized = serde_json::to_string(&settings).map_err(|e| {
|
||||||
AppCommandError::new(
|
AppCommandError::invalid_input("Failed to serialize language settings")
|
||||||
AppErrorCode::InvalidInput,
|
.with_detail(e.to_string())
|
||||||
"Failed to serialize language settings",
|
|
||||||
)
|
|
||||||
.with_detail(e.to_string())
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
app_metadata_service::upsert_value(&db.conn, SYSTEM_LANGUAGE_SETTINGS_KEY, &serialized)
|
app_metadata_service::upsert_value(&db.conn, SYSTEM_LANGUAGE_SETTINGS_KEY, &serialized)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::sync::Mutex;
|
|||||||
|
|
||||||
use tauri::{AppHandle, Manager, WebviewUrl, WebviewWindowBuilder};
|
use tauri::{AppHandle, Manager, WebviewUrl, WebviewWindowBuilder};
|
||||||
|
|
||||||
use crate::app_error::{AppCommandError, AppErrorCode};
|
use crate::app_error::AppCommandError;
|
||||||
use crate::db::AppDatabase;
|
use crate::db::AppDatabase;
|
||||||
use crate::models::FolderHistoryEntry;
|
use crate::models::FolderHistoryEntry;
|
||||||
|
|
||||||
@@ -191,11 +191,10 @@ pub async fn focus_folder_window(app: AppHandle, folder_id: i32) -> Result<(), A
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(AppCommandError::new(
|
Err(
|
||||||
AppErrorCode::NotFound,
|
AppCommandError::not_found(format!("No open window for folder {folder_id}"))
|
||||||
format!("No open window for folder {folder_id}"),
|
.with_detail(format!("folder_id={folder_id}")),
|
||||||
)
|
)
|
||||||
.with_detail(format!("folder_id={folder_id}")))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -259,11 +258,8 @@ pub async fn open_commit_window(
|
|||||||
.await
|
.await
|
||||||
.map_err(AppCommandError::from)?
|
.map_err(AppCommandError::from)?
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
AppCommandError::new(
|
AppCommandError::not_found(format!("Folder {folder_id} not found"))
|
||||||
AppErrorCode::NotFound,
|
.with_detail(format!("folder_id={folder_id}"))
|
||||||
format!("Folder {folder_id} not found"),
|
|
||||||
)
|
|
||||||
.with_detail(format!("folder_id={folder_id}"))
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let url = WebviewUrl::App(format!("commit?folderId={folder_id}").into());
|
let url = WebviewUrl::App(format!("commit?folderId={folder_id}").into());
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::app_error::{AppCommandError, AppErrorCode};
|
use crate::app_error::AppCommandError;
|
||||||
use crate::models::SystemProxySettings;
|
use crate::models::SystemProxySettings;
|
||||||
|
|
||||||
const PROXY_ENV_KEYS: [&str; 6] = [
|
const PROXY_ENV_KEYS: [&str; 6] = [
|
||||||
@@ -18,8 +18,7 @@ pub fn apply_system_proxy_settings(settings: &SystemProxySettings) -> Result<(),
|
|||||||
.map(str::trim)
|
.map(str::trim)
|
||||||
.filter(|value| !value.is_empty())
|
.filter(|value| !value.is_empty())
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
AppCommandError::new(
|
AppCommandError::configuration_missing(
|
||||||
AppErrorCode::ConfigurationMissing,
|
|
||||||
"Proxy URL is required when proxy is enabled",
|
"Proxy URL is required when proxy is enabled",
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ export type AgentType =
|
|||||||
| "stakpak"
|
| "stakpak"
|
||||||
|
|
||||||
export type AppErrorCode =
|
export type AppErrorCode =
|
||||||
| "unknown"
|
|
||||||
| "invalid_input"
|
| "invalid_input"
|
||||||
| "configuration_missing"
|
| "configuration_missing"
|
||||||
| "configuration_invalid"
|
| "configuration_invalid"
|
||||||
|
|||||||
Reference in New Issue
Block a user