1use std::fmt;
2
3use thiserror::Error;
4
5use crate::channel::ChannelError;
6use crate::path::{PathError, RepoPath};
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: {0}")]
18 NonUtf8Path(String),
19 #[error("path escapes the repository root: {0}")]
20 PathOutsideRepo(String),
21 #[error("empty paths are not valid tracked files")]
22 EmptyPath,
23 #[error("path is not tracked at HEAD: {0}")]
24 PathNotTracked(RepoPath),
25 #[error("path is a submodule/gitlink and is out of scope: {0}")]
26 PathIsSubmodule(RepoPath),
27 #[error("failed to read .vetignore: {0}")]
28 Vetignore(String),
29 #[error("missing git config user.name")]
30 MissingUserName,
31 #[error("missing git config user.email")]
32 MissingUserEmail,
33 #[error("invalid review channel {channel:?}: {details}")]
34 InvalidChannel { channel: String, details: String },
35 #[error("notes ref points to a {actual}; expected a commit")]
36 InvalidNotesRefTarget { actual: &'static str },
37 #[error("I/O error: {0}")]
38 Io(#[from] std::io::Error),
39 #[error("JSON error: {0}")]
40 Json(#[from] serde_json::Error),
41}
42
43impl From<PathError> for AppError {
44 fn from(error: PathError) -> Self {
45 match error {
46 PathError::NonUtf8Path(path) => Self::NonUtf8Path(path),
47 PathError::PathOutsideRepo(path) => Self::PathOutsideRepo(path),
48 PathError::EmptyPath => Self::EmptyPath,
49 }
50 }
51}
52
53impl From<ChannelError> for AppError {
54 fn from(error: ChannelError) -> Self {
55 Self::InvalidChannel {
56 channel: error.channel,
57 details: error.details,
58 }
59 }
60}
61
62pub fn git_error(operation: &'static str, source: impl fmt::Display) -> AppError {
63 AppError::Git {
64 operation,
65 details: source.to_string(),
66 }
67}