1#![doc = include_str!("../README.md")]
2
3use std::time::Instant;
4
5use anyhow::Result;
6use bpaf::Bpaf;
7
8use lintel_diagnostics::reporter::{CheckResult, CheckedFile, Reporter};
9
10#[derive(Debug, Clone, Bpaf)]
15#[bpaf(generate(check_args_inner))]
16pub struct CheckArgs {
17 #[bpaf(long("fix"), switch)]
19 pub fix: bool,
20
21 #[bpaf(external(lintel_validate::validate_args))]
22 pub validate: lintel_validate::ValidateArgs,
23}
24
25pub fn check_args() -> impl bpaf::Parser<CheckArgs> {
27 check_args_inner()
28}
29
30pub async fn check(
39 args: &mut CheckArgs,
40 on_file_checked: impl FnMut(&CheckedFile),
41) -> Result<CheckResult> {
42 let original_globs = args.validate.globs.clone();
44 let original_exclude = args.validate.exclude.clone();
45
46 lintel_validate::merge_config(&mut args.validate);
47
48 let lib_args = lintel_validate::validate::ValidateArgs::from(&args.validate);
49
50 let files = lintel_validate::validate::collect_files(&lib_args.globs, &lib_args.exclude)?;
52 let mut read_errors = Vec::new();
53 let file_contents = lintel_validate::validate::read_files(&files, &mut read_errors).await;
54
55 if args.fix {
56 let fixed = lintel_format::fix_format(&original_globs, &original_exclude)?;
57 if fixed > 0 {
58 eprintln!("Fixed formatting in {fixed} file(s).");
59 }
60
61 let mut result = lintel_validate::validate::run_with_contents(
62 &lib_args,
63 file_contents,
64 None,
65 on_file_checked,
66 )
67 .await?;
68 result.errors.extend(read_errors);
69 sort_errors(&mut result.errors);
70 Ok(result)
71 } else {
72 let format_errors = lintel_format::check_format_contents(
74 &file_contents,
75 &original_globs,
76 &original_exclude,
77 );
78
79 let mut result = lintel_validate::validate::run_with_contents(
81 &lib_args,
82 file_contents,
83 None,
84 on_file_checked,
85 )
86 .await?;
87
88 result.errors.extend(format_errors);
90 result.errors.extend(read_errors);
91 sort_errors(&mut result.errors);
92
93 Ok(result)
94 }
95}
96
97pub async fn run(args: &mut CheckArgs, reporter: &mut dyn Reporter) -> Result<bool> {
108 let start = Instant::now();
109 let result = check(args, |file| reporter.on_file_checked(file)).await?;
110 let had_errors = result.has_errors();
111 let elapsed = start.elapsed();
112 reporter.report(result, elapsed);
113 Ok(had_errors)
114}
115
116fn sort_errors(errors: &mut [lintel_diagnostics::LintelDiagnostic]) {
117 errors.sort_by(|a, b| {
118 a.path()
119 .cmp(b.path())
120 .then_with(|| a.offset().cmp(&b.offset()))
121 });
122}