|
1 | 1 | use std::cell::{Ref, RefCell};
|
2 |
| -use std::collections::{BTreeMap, BTreeSet, HashSet}; |
| 2 | +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; |
3 | 3 | use std::rc::Rc;
|
4 | 4 |
|
5 | 5 | use crate::formatting::FormattedSnippet;
|
6 | 6 | use crate::result::{ErrorKind, FormatError};
|
| 7 | +use crate::Config; |
7 | 8 | use crate::FileName;
|
8 | 9 | use crate::NewlineStyle;
|
9 | 10 |
|
@@ -78,6 +79,17 @@ impl FormatResult {
|
78 | 79 | pub(crate) fn formatted_snippet(&self) -> &FormattedSnippet {
|
79 | 80 | &self.formatted_snippet
|
80 | 81 | }
|
| 82 | + |
| 83 | + pub(crate) fn has_error_kind(&self, kind: ErrorKind) -> bool { |
| 84 | + self.all_errors().any(|e| e.kind() == kind) |
| 85 | + } |
| 86 | + |
| 87 | + pub(crate) fn has_any_matching_errors<F>(&self, error_matcher: F) -> bool |
| 88 | + where |
| 89 | + F: FnMut(&FormatError) -> bool, |
| 90 | + { |
| 91 | + self.all_errors().any(error_matcher) |
| 92 | + } |
81 | 93 | }
|
82 | 94 |
|
83 | 95 | impl FormatReport {
|
@@ -154,6 +166,69 @@ impl FormatReport {
|
154 | 166 | .insert(format_error);
|
155 | 167 | }
|
156 | 168 |
|
| 169 | + fn has_any_matching_format_result<F>(&self, format_result_matcher: F) -> bool |
| 170 | + where |
| 171 | + F: FnMut((&FileName, &FormatResult)) -> bool, |
| 172 | + { |
| 173 | + RefCell::borrow(&self.format_result) |
| 174 | + .iter() |
| 175 | + .any(format_result_matcher) |
| 176 | + } |
| 177 | + |
| 178 | + fn has_error_kind(&self, kind: ErrorKind) -> bool { |
| 179 | + self.has_any_matching_format_result(|(_, format_result)| format_result.has_error_kind(kind)) |
| 180 | + } |
| 181 | + |
| 182 | + pub fn has_deprecated_attribute_errors(&self) -> bool { |
| 183 | + self.has_error_kind(ErrorKind::DeprecatedAttr) |
| 184 | + } |
| 185 | + |
| 186 | + pub fn has_invalid_rustfmt_attribute_errors(&self) -> bool { |
| 187 | + self.has_error_kind(ErrorKind::BadAttr) |
| 188 | + } |
| 189 | + |
| 190 | + pub fn has_attribute_errors(&self) -> bool { |
| 191 | + self.has_any_matching_format_result(|(_, format_result)| { |
| 192 | + format_result.has_any_matching_errors(|e| match e.kind() { |
| 193 | + ErrorKind::BadAttr | ErrorKind::DeprecatedAttr => true, |
| 194 | + _ => false, |
| 195 | + }) |
| 196 | + }) |
| 197 | + } |
| 198 | + |
| 199 | + pub fn has_failing_errors(&self, file_config_map: HashMap<FileName, &Config>) -> bool { |
| 200 | + self.has_any_matching_format_result(|(file_name, format_result)| { |
| 201 | + format_result.has_any_matching_errors(|e| match e.kind() { |
| 202 | + ErrorKind::BadAttr | ErrorKind::DeprecatedAttr => true, |
| 203 | + ErrorKind::LicenseCheck => { |
| 204 | + if let Some(config) = file_config_map.get(file_name) { |
| 205 | + if config.was_set().license_template_path() { |
| 206 | + return true; |
| 207 | + } |
| 208 | + } |
| 209 | + false |
| 210 | + } |
| 211 | + ErrorKind::LineOverflow(..) => { |
| 212 | + if let Some(config) = file_config_map.get(file_name) { |
| 213 | + if config.error_on_line_overflow() { |
| 214 | + return true; |
| 215 | + } |
| 216 | + } |
| 217 | + false |
| 218 | + } |
| 219 | + ErrorKind::TrailingWhitespace => { |
| 220 | + if let Some(config) = file_config_map.get(file_name) { |
| 221 | + if config.error_on_unformatted() { |
| 222 | + return true; |
| 223 | + } |
| 224 | + } |
| 225 | + false |
| 226 | + } |
| 227 | + _ => false, |
| 228 | + }) |
| 229 | + }) |
| 230 | + } |
| 231 | + |
157 | 232 | pub fn has_errors(&self) -> bool {
|
158 | 233 | RefCell::borrow(&self.format_result)
|
159 | 234 | .iter()
|
@@ -184,3 +259,165 @@ impl NonFormattedRange {
|
184 | 259 | self.lo <= line && line <= self.hi
|
185 | 260 | }
|
186 | 261 | }
|
| 262 | + |
| 263 | +#[cfg(test)] |
| 264 | +mod test { |
| 265 | + use super::*; |
| 266 | + |
| 267 | + #[cfg(test)] |
| 268 | + mod has_failing_errors { |
| 269 | + use super::*; |
| 270 | + use std::path::PathBuf; |
| 271 | + |
| 272 | + #[test] |
| 273 | + fn false_with_only_macro() { |
| 274 | + let file_name = FileName::Real(PathBuf::from("foo/bar.rs")); |
| 275 | + let report = FormatReport::new(); |
| 276 | + report.add_format_error( |
| 277 | + file_name.clone(), |
| 278 | + FormatError::new(ErrorKind::MacroFormatError, 2, String::new()), |
| 279 | + ); |
| 280 | + assert!( |
| 281 | + !report.has_failing_errors( |
| 282 | + vec![(file_name, &Config::default())].into_iter().collect() |
| 283 | + ) |
| 284 | + ); |
| 285 | + } |
| 286 | + |
| 287 | + #[test] |
| 288 | + fn true_with_bad_attr() { |
| 289 | + let file_name = FileName::Real(PathBuf::from("bar/baz.rs")); |
| 290 | + let report = FormatReport::new(); |
| 291 | + report.add_format_error( |
| 292 | + file_name.clone(), |
| 293 | + FormatError::new(ErrorKind::BadAttr, 2, String::new()), |
| 294 | + ); |
| 295 | + assert!( |
| 296 | + report.has_failing_errors( |
| 297 | + vec![(file_name, &Config::default())].into_iter().collect() |
| 298 | + ) |
| 299 | + ); |
| 300 | + } |
| 301 | + |
| 302 | + #[test] |
| 303 | + fn true_with_deprecated_attr() { |
| 304 | + let file_name = FileName::Real(PathBuf::from("baz/qux.rs")); |
| 305 | + let report = FormatReport::new(); |
| 306 | + report.add_format_error( |
| 307 | + file_name.clone(), |
| 308 | + FormatError::new(ErrorKind::DeprecatedAttr, 2, String::new()), |
| 309 | + ); |
| 310 | + assert!( |
| 311 | + report.has_failing_errors( |
| 312 | + vec![(file_name, &Config::default())].into_iter().collect() |
| 313 | + ) |
| 314 | + ); |
| 315 | + } |
| 316 | + |
| 317 | + #[test] |
| 318 | + fn false_with_license_check_and_config_disabled() { |
| 319 | + let file_name = FileName::Real(PathBuf::from("foo.rs")); |
| 320 | + let bar_file_name = FileName::Real(PathBuf::from("bar.rs")); |
| 321 | + let mut license_config = Config::default(); |
| 322 | + license_config |
| 323 | + .set() |
| 324 | + .license_template_path(String::from("template.txt")); |
| 325 | + let report = FormatReport::new(); |
| 326 | + report.add_format_error( |
| 327 | + bar_file_name.clone(), |
| 328 | + FormatError::new(ErrorKind::LicenseCheck, 2, String::new()), |
| 329 | + ); |
| 330 | + assert!( |
| 331 | + !report.has_failing_errors( |
| 332 | + vec![ |
| 333 | + (file_name, &license_config), |
| 334 | + (bar_file_name, &Config::default()), |
| 335 | + ] |
| 336 | + .into_iter() |
| 337 | + .collect(), |
| 338 | + ) |
| 339 | + ); |
| 340 | + } |
| 341 | + |
| 342 | + #[test] |
| 343 | + fn true_with_license_check_and_config_enabled() { |
| 344 | + let file_name = FileName::Real(PathBuf::from("foo.rs")); |
| 345 | + let report = FormatReport::new(); |
| 346 | + let mut config = Config::default(); |
| 347 | + config |
| 348 | + .set() |
| 349 | + .license_template_path(String::from("license.txt")); |
| 350 | + report.add_license_failure(file_name.clone()); |
| 351 | + assert!(report.has_failing_errors(vec![(file_name, &config)].into_iter().collect())); |
| 352 | + } |
| 353 | + |
| 354 | + #[test] |
| 355 | + fn false_with_line_overflow_and_config_disabled() { |
| 356 | + let file_name = FileName::Real(PathBuf::from("short_enough.rs")); |
| 357 | + let overflow_file_name = FileName::Real(PathBuf::from("too_long.rs")); |
| 358 | + let mut overflow_config = Config::default(); |
| 359 | + overflow_config.set().error_on_line_overflow(true); |
| 360 | + let report = FormatReport::new(); |
| 361 | + report.add_license_failure(file_name.clone()); |
| 362 | + assert!( |
| 363 | + !report.has_failing_errors( |
| 364 | + vec![ |
| 365 | + (file_name, &overflow_config), |
| 366 | + (overflow_file_name, &Config::default()), |
| 367 | + ] |
| 368 | + .into_iter() |
| 369 | + .collect(), |
| 370 | + ) |
| 371 | + ); |
| 372 | + } |
| 373 | + |
| 374 | + #[test] |
| 375 | + fn true_with_line_overflow_and_config_enabled() { |
| 376 | + let file_name = FileName::Real(PathBuf::from("overflowed.rs")); |
| 377 | + let report = FormatReport::new(); |
| 378 | + let mut config = Config::default(); |
| 379 | + config.set().error_on_line_overflow(true); |
| 380 | + report.add_format_error( |
| 381 | + file_name.clone(), |
| 382 | + FormatError::new(ErrorKind::LineOverflow(100, 103), 2, String::new()), |
| 383 | + ); |
| 384 | + assert!(report.has_failing_errors(vec![(file_name, &config)].into_iter().collect())); |
| 385 | + } |
| 386 | + |
| 387 | + #[test] |
| 388 | + fn false_with_trailing_whitespace_and_config_disabled() { |
| 389 | + let file_name = FileName::Real(PathBuf::from("trimmed.rs")); |
| 390 | + let trailing_file_name = FileName::Real(PathBuf::from("trailing_whitespace.rs")); |
| 391 | + let mut trailing_config = Config::default(); |
| 392 | + trailing_config.set().error_on_unformatted(true); |
| 393 | + let report = FormatReport::new(); |
| 394 | + report.add_format_error( |
| 395 | + trailing_file_name.clone(), |
| 396 | + FormatError::new(ErrorKind::TrailingWhitespace, 3, String::new()), |
| 397 | + ); |
| 398 | + assert!( |
| 399 | + !report.has_failing_errors( |
| 400 | + vec![ |
| 401 | + (file_name, &trailing_config), |
| 402 | + (trailing_file_name, &Config::default()), |
| 403 | + ] |
| 404 | + .into_iter() |
| 405 | + .collect(), |
| 406 | + ) |
| 407 | + ); |
| 408 | + } |
| 409 | + |
| 410 | + #[test] |
| 411 | + fn true_with_trailing_whitespace_and_config_enabled() { |
| 412 | + let file_name = FileName::Real(PathBuf::from("trailing_whitespace.rs")); |
| 413 | + let report = FormatReport::new(); |
| 414 | + let mut config = Config::default(); |
| 415 | + config.set().error_on_unformatted(true); |
| 416 | + report.add_format_error( |
| 417 | + file_name.clone(), |
| 418 | + FormatError::new(ErrorKind::TrailingWhitespace, 42, String::new()), |
| 419 | + ); |
| 420 | + assert!(report.has_failing_errors(vec![(file_name, &config)].into_iter().collect())); |
| 421 | + } |
| 422 | + } |
| 423 | +} |
0 commit comments