1use git2::{ErrorClass, ErrorCode};
2use std::fmt;
3
4#[derive(Debug)]
6pub enum SquishError {
7 Git { message: String },
9 Other { message: String },
11}
12
13impl fmt::Display for SquishError {
14 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15 match self {
16 SquishError::Git { message } => write!(f, "{message}"),
17 SquishError::Other { message } => write!(f, "{message}"),
18 }
19 }
20}
21
22impl std::error::Error for SquishError {}
23
24impl From<git2::Error> for SquishError {
25 fn from(error: git2::Error) -> Self {
26 let is_conflict = match (error.class(), error.code()) {
28 (ErrorClass::Merge, ErrorCode::Conflict) => true,
29 (ErrorClass::Merge, ErrorCode::MergeConflict) => true,
30 (ErrorClass::Index, ErrorCode::Unmerged) => true,
31 (ErrorClass::Checkout, ErrorCode::Conflict) => true,
32 _ => {
33 let msg = error.message().to_lowercase();
35 msg.contains("conflict") || (msg.contains("merge") && msg.contains("failed"))
36 }
37 };
38
39 let message = if is_conflict {
40 "There was a conflict during this squish, please retry using git rebase -i and resolve the conflicts".to_string()
41 } else {
42 error.message().to_string()
43 };
44
45 SquishError::Git { message }
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52 use git2::Error;
53 use std::error::Error as StdError;
54
55 #[test]
56 fn test_squish_error_display() {
57 let git_error = SquishError::Git {
58 message: "Test git error".to_string(),
59 };
60 assert_eq!(format!("{}", git_error), "Test git error");
61
62 let other_error = SquishError::Other {
63 message: "Test other error".to_string(),
64 };
65 assert_eq!(format!("{}", other_error), "Test other error");
66 }
67
68 #[test]
69 fn test_squish_error_debug() {
70 let error = SquishError::Git {
71 message: "Debug test".to_string(),
72 };
73 let debug_output = format!("{:?}", error);
74 assert!(debug_output.contains("Git"));
75 assert!(debug_output.contains("Debug test"));
76 }
77
78 #[test]
79 fn test_from_git2_conflict_error() {
80 let git_error = Error::from_str("merge conflict in file.txt");
82 let squish_error = SquishError::from(git_error);
83
84 if let SquishError::Git { message } = squish_error {
85 assert_eq!(
86 message,
87 "There was a conflict during this squish, please retry using git rebase -i and resolve the conflicts"
88 );
89 } else {
90 panic!("Expected Git error variant");
91 }
92 }
93
94 #[test]
95 fn test_from_git2_non_conflict_error() {
96 let git_error = Error::from_str("repository not found");
97 let squish_error = SquishError::from(git_error);
98
99 if let SquishError::Git { message } = squish_error {
100 assert_eq!(message, "repository not found");
101 } else {
102 panic!("Expected Git error variant");
103 }
104 }
105
106 #[test]
107 fn test_from_git2_merge_failed_error() {
108 let git_error = Error::from_str("merge failed due to conflicts");
109 let squish_error = SquishError::from(git_error);
110
111 if let SquishError::Git { message } = squish_error {
112 assert_eq!(
113 message,
114 "There was a conflict during this squish, please retry using git rebase -i and resolve the conflicts"
115 );
116 } else {
117 panic!("Expected Git error variant");
118 }
119 }
120
121 #[test]
122 fn test_conflict_detection_keywords() {
123 let test_cases = vec![
124 ("conflict in file", true),
125 ("CONFLICT in file", true),
126 ("merge failed", true),
127 ("repository not found", false),
128 ("invalid reference", false),
129 ("nothing to merge failed", true), ];
131
132 for (error_msg, should_be_conflict) in test_cases {
133 let git_error = Error::from_str(error_msg);
134 let squish_error = SquishError::from(git_error);
135
136 if let SquishError::Git { message } = squish_error {
137 if should_be_conflict {
138 assert_eq!(
139 message,
140 "There was a conflict during this squish, please retry using git rebase -i and resolve the conflicts",
141 "Error message '{}' should be detected as conflict",
142 error_msg
143 );
144 } else {
145 assert_eq!(
146 message, error_msg,
147 "Error message '{}' should not be detected as conflict",
148 error_msg
149 );
150 }
151 } else {
152 panic!("Expected Git error variant");
153 }
154 }
155 }
156
157 #[test]
158 fn test_error_trait_implementation() {
159 let error = SquishError::Other {
160 message: "test error".to_string(),
161 };
162
163 let _error_ref: &dyn std::error::Error = &error;
165
166 assert!(StdError::source(&error).is_none());
168 }
169}