1use std::path::PathBuf;
2use std::process::ExitCode;
3
4#[derive(Debug, thiserror::Error)]
6pub enum Error {
7 #[error("{0}")]
8 General(String),
9
10 #[error(transparent)]
11 Config(#[from] ConfigError),
12
13 #[error(transparent)]
14 Git(#[from] GitError),
15
16 #[error("unmatched files: {}", .paths.iter().map(|p| p.display().to_string()).collect::<Vec<_>>().join(", "))]
17 UnmatchedFiles { paths: Vec<PathBuf> },
18
19 #[error("branch {branch} has diverged from {base}")]
20 DivergedBranch { branch: String, base: String },
21}
22
23#[derive(Debug, thiserror::Error)]
25pub enum ConfigError {
26 #[error("config file not found: {path}")]
27 NotFound { path: PathBuf },
28
29 #[error("invalid config: {reason}")]
30 Invalid { reason: String },
31
32 #[error("invalid glob pattern in component {component:?}: {pattern:?}: {reason}")]
33 InvalidGlob {
34 component: String,
35 pattern: String,
36 reason: String,
37 },
38
39 #[error("failed to read config: {0}")]
40 Io(#[from] std::io::Error),
41}
42
43#[derive(Debug, thiserror::Error)]
45pub enum GitError {
46 #[error("not a git repository: {path}")]
47 NotARepo { path: PathBuf },
48
49 #[error("failed to resolve reference {reference:?}: {reason}")]
50 ResolveRef { reference: String, reason: String },
51
52 #[error("git operation failed: {0}")]
53 Operation(String),
54
55 #[error("tree entry not found: {path}")]
56 TreeEntryNotFound { path: String },
57
58 #[error("ref update failed for {branch}: {reason}")]
59 RefUpdate { branch: String, reason: String },
60
61 #[error(transparent)]
62 Gix(Box<gix::open::Error>),
63}
64
65impl Error {
66 pub fn exit_code(&self) -> ExitCode {
68 match self {
69 Error::General(_) => ExitCode::from(1),
70 Error::Config(_) => ExitCode::from(2),
71 Error::Git(_) => ExitCode::from(3),
72 Error::UnmatchedFiles { .. } => ExitCode::from(4),
73 Error::DivergedBranch { .. } => ExitCode::from(5),
74 }
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 #[test]
83 fn exit_codes_match_spec() {
84 assert_eq!(Error::General("x".into()).exit_code(), ExitCode::from(1));
85 assert_eq!(
86 Error::Config(ConfigError::Invalid {
87 reason: "bad".into()
88 })
89 .exit_code(),
90 ExitCode::from(2)
91 );
92 assert_eq!(
93 Error::Git(GitError::Operation("fail".into())).exit_code(),
94 ExitCode::from(3)
95 );
96 assert_eq!(
97 Error::UnmatchedFiles { paths: vec![] }.exit_code(),
98 ExitCode::from(4)
99 );
100 assert_eq!(
101 Error::DivergedBranch {
102 branch: "a".into(),
103 base: "b".into()
104 }
105 .exit_code(),
106 ExitCode::from(5)
107 );
108 }
109}