1use std::fmt;
2
3use thiserror::Error;
4
5use crate::path::{PathError, RepoPath};
6use crate::remote::RemoteError;
7
8#[derive(Debug, Error)]
9pub enum AppError {
10 #[error("git operation failed while {operation}: {details}")]
11 Git {
12 operation: &'static str,
13 details: String,
14 },
15 #[error("repository has no worktree")]
16 MissingWorktree,
17 #[error("path is not valid UTF-8")]
18 NonUtf8Path,
19 #[error("path escapes the repository root")]
20 PathOutsideRepo,
21 #[error("path must be absolute")]
22 NonAbsolutePath,
23 #[error("empty paths are not valid tracked files")]
24 EmptyPath,
25 #[error("repo path is invalid: {details}")]
26 InvalidRepoPath { details: &'static str },
27 #[error("path is not tracked at HEAD: {0}")]
28 PathNotTracked(RepoPath),
29 #[error("path is a submodule/gitlink and is out of scope: {0}")]
30 PathIsSubmodule(RepoPath),
31 #[error("failed to read vetignore file: {0}")]
32 Vetignore(String),
33 #[error("missing git config user.name")]
34 MissingUserName,
35 #[error("missing git config user.email")]
36 MissingUserEmail,
37 #[error("git config {key} is not valid UTF-8: {details}")]
38 NonUtf8GitConfig { key: &'static str, details: String },
39 #[error(
40 "target paths have uncommitted working-tree changes; rerun with --allow-dirty to proceed with committed HEAD contents"
41 )]
42 DirtyPathsRequireAllowDirty,
43 #[error("aborted because target paths have uncommitted working-tree changes")]
44 DirtyPathsDeclined,
45 #[error("invalid review channel {channel:?}: {details}")]
46 InvalidChannel { channel: String, details: String },
47 #[error("sync remote error: {0}")]
48 Remote(#[from] RemoteError),
49 #[error("I/O error: {0}")]
50 Io(#[from] std::io::Error),
51 #[error("JSON error: {0}")]
52 Json(#[from] serde_json::Error),
53}
54
55impl From<PathError> for AppError {
56 fn from(error: PathError) -> Self {
57 match error {
58 PathError::NonUtf8 => Self::NonUtf8Path,
59 PathError::OutsideRepo => Self::PathOutsideRepo,
60 PathError::NonAbsolute => Self::NonAbsolutePath,
61 PathError::EmptyPath => Self::EmptyPath,
62 PathError::EmptyComponent => Self::InvalidRepoPath {
63 details: "path contains an empty component",
64 },
65 PathError::CurrentDirComponent => Self::InvalidRepoPath {
66 details: "path contains a current-directory component",
67 },
68 PathError::NulByte => Self::InvalidRepoPath {
69 details: "path contains a NUL byte",
70 },
71 }
72 }
73}
74
75pub(crate) fn git_error(operation: &'static str, source: impl fmt::Display) -> AppError {
76 AppError::Git {
77 operation,
78 details: source.to_string(),
79 }
80}