1use std::fmt;
2
3use thiserror::Error;
4
5use crate::path::{PathError, RepoPath, RepoPathScope};
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("pathspec did not match any tracked files at HEAD: {0}")]
30 PathspecNotMatched(RepoPathScope),
31 #[error("path is a submodule/gitlink and is out of scope: {0}")]
32 PathIsSubmodule(RepoPath),
33 #[error("failed to read vetignore file: {0}")]
34 Vetignore(String),
35 #[error("missing git config user.name")]
36 MissingUserName,
37 #[error("missing git config user.email")]
38 MissingUserEmail,
39 #[error("git config {key} is not valid UTF-8: {details}")]
40 NonUtf8GitConfig { key: &'static str, details: String },
41 #[error(
42 "target paths have uncommitted working-tree changes; rerun with --allow-dirty to proceed with committed HEAD contents"
43 )]
44 DirtyPathsRequireAllowDirty,
45 #[error("aborted because target paths have uncommitted working-tree changes")]
46 DirtyPathsDeclined,
47 #[error("invalid review channel {channel:?}: {details}")]
48 InvalidChannel { channel: String, details: String },
49 #[error("sync remote error: {0}")]
50 Remote(#[from] RemoteError),
51 #[error("I/O error: {0}")]
52 Io(#[from] std::io::Error),
53 #[error("JSON error: {0}")]
54 Json(#[from] serde_json::Error),
55}
56
57impl From<PathError> for AppError {
58 fn from(error: PathError) -> Self {
59 match error {
60 PathError::NonUtf8 => Self::NonUtf8Path,
61 PathError::OutsideRepo => Self::PathOutsideRepo,
62 PathError::NonAbsolute => Self::NonAbsolutePath,
63 PathError::EmptyPath => Self::EmptyPath,
64 PathError::EmptyComponent => Self::InvalidRepoPath {
65 details: "path contains an empty component",
66 },
67 PathError::CurrentDirComponent => Self::InvalidRepoPath {
68 details: "path contains a current-directory component",
69 },
70 PathError::NulByte => Self::InvalidRepoPath {
71 details: "path contains a NUL byte",
72 },
73 }
74 }
75}
76
77pub(crate) fn git_error(operation: &'static str, source: impl fmt::Display) -> AppError {
78 AppError::Git {
79 operation,
80 details: source.to_string(),
81 }
82}