1use std::fmt::Formatter;
25
26use getset::Getters;
27use itertools::fold;
28use std::fmt::Display;
29use strict_yaml_rust::ScanError;
30use strict_yaml_rust::StrictYaml;
31use subprocess::ExitStatus;
32use subprocess::PopenError;
33
34use crate::Directive;
35
36pub fn process_until_first_err<I, F, E>(iterable: I, mut process_function: F) -> Result<(), E>
40where
41 I: IntoIterator,
42 F: FnMut(I::Item) -> Result<(), E>,
43{
44 fold(iterable, Ok(()), |prev_res, item| match prev_res {
45 Ok(()) => process_function(item),
46 Err(err) => Err(err),
47 })
48}
49
50pub fn fold_until_first_err<I, Folded, Processed, F, P, E>(
54 iterable: I,
55 init: Result<Folded, E>,
56 process_function: P,
57 mut fold_function: F,
58) -> Result<Folded, E>
59where
60 I: IntoIterator,
61 F: FnMut(Folded, Processed) -> Result<Folded, E>,
62 P: FnMut(I::Item) -> Result<Processed, E>,
63{
64 let processed_vec_res: Result<Vec<Processed>, E> =
65 iterable.into_iter().map(process_function).collect();
66
67 processed_vec_res.and_then(|processed_vec| {
68 fold(
69 processed_vec.into_iter(),
70 init,
71 |prev_res, item| match prev_res {
72 Ok(prev_folded) => fold_function(prev_folded, item),
73 Err(err) => Err(err),
74 },
75 )
76 })
77}
78
79#[derive(Debug)]
81pub enum ErrorType {
82 ExecutionError {
84 popen_error: Option<PopenError>,
87 exit_status: Option<ExitStatus>,
90 },
91 FileSystemError {
94 fs_error: std::io::Error,
96 },
97 InconsistentConfigurationError,
99 IncompleteConfigurationError {
101 missing_field: String,
103 },
104 YamlParseError {
106 scan_error: ScanError,
108 },
109 UnexpectedYamlTypeError {
111 encountered: StrictYaml,
113 expected: StrictYaml,
115 },
116 CoreError,
118 TestingErrorActionSucceedsWhenItShouldFail,
120}
121
122impl Display for ErrorType {
123 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
124 write!(
125 f,
126 "{}",
127 match self {
128 ErrorType::ExecutionError {
129 popen_error: _,
130 exit_status: _,
131 } => "ExecutionError",
132 ErrorType::FileSystemError { fs_error: _ } => "FileSystemError",
133 ErrorType::InconsistentConfigurationError => "InconsistentConfigurationError",
134 ErrorType::IncompleteConfigurationError { missing_field: _ } =>
135 "IncompleteConfigurationError",
136 ErrorType::YamlParseError { scan_error: _ } => "YamlParseError",
137 ErrorType::UnexpectedYamlTypeError {
138 encountered: _,
139 expected: _,
140 } => "UnexpectedYamlTypeError",
141 ErrorType::CoreError => "CoreError",
142 ErrorType::TestingErrorActionSucceedsWhenItShouldFail =>
143 "TestingErrorActionSucceedsWhenItShouldFail",
144 }
145 )
146 }
147}
148
149pub fn execution_error(
151 popen_error: Option<PopenError>,
152 exit_status: Option<ExitStatus>,
153) -> ErrorType {
154 ErrorType::ExecutionError {
155 popen_error,
156 exit_status,
157 }
158}
159
160#[derive(Getters, Debug)]
162pub struct DotfilesError {
163 #[getset(get = "pub")]
165 message: String,
166 #[getset(get = "pub")]
168 error_type: ErrorType,
169}
170
171impl Display for DotfilesError {
172 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
173 write!(f, "{}: {}", self.error_type(), self.message())
174 }
175}
176
177impl DotfilesError {
178 pub fn add_message_prefix(&mut self, prefix: String) {
180 self.message = format!("{prefix}: {}", self.message,);
181 }
182 pub fn is_missing_config(&self, config_name: &str) -> bool {
184 match &self.error_type {
185 ErrorType::IncompleteConfigurationError { missing_field } => missing_field == config_name,
186 _ => false,
187 }
188 }
189
190 pub fn is_wrong_yaml(&self) -> bool {
192 matches!(
193 &self.error_type,
194 ErrorType::UnexpectedYamlTypeError {
195 encountered: _,
196 expected: _,
197 }
198 )
199 }
200
201 pub fn is_yaml_parse_error(&self) -> bool {
203 matches!(
204 &self.error_type,
205 ErrorType::YamlParseError { scan_error: _ }
206 )
207 }
208
209 pub fn is_inconsistent_config(&self) -> bool {
211 matches!(&self.error_type, ErrorType::InconsistentConfigurationError)
212 }
213 pub fn is_fs_error(&self) -> bool {
215 matches!(&self.error_type, ErrorType::FileSystemError { fs_error: _ })
216 }
217
218 pub fn from(message: String, error_type: ErrorType) -> Self {
220 DotfilesError {
221 message,
222 error_type,
223 }
224 }
225
226 pub fn from_wrong_yaml(
228 message: String,
229 wrong_yaml: StrictYaml,
230 expected_type: StrictYaml,
231 ) -> Self {
232 DotfilesError {
233 message,
234 error_type: ErrorType::UnexpectedYamlTypeError {
235 encountered: wrong_yaml,
236 expected: expected_type,
237 },
238 }
239 }
240 pub fn from_io_error(io_error: std::io::Error) -> Self {
242 DotfilesError {
243 message: io_error.to_string(),
244 error_type: ErrorType::FileSystemError { fs_error: io_error },
245 }
246 }
247}
248
249pub fn add_directive_error_prefix<'a, D, T>(
251 dir: &'a D,
252 res: Result<T, DotfilesError>,
253) -> Result<T, DotfilesError>
254where
255 D: Directive<'a>,
256{
257 res.map_err(|mut error| {
258 error.add_message_prefix(format!("Directive {}", dir.name()));
259 error
260 })
261}