1pub mod analysis;
4pub mod config;
5pub mod diagnostic;
6pub mod fix;
7pub mod output;
8pub mod project;
9pub mod rules;
10pub mod scanner;
11
12use std::path::{Path, PathBuf};
13
14use anyhow::Result;
15use serde::Serialize;
16
17pub use config::{Config, SeveritySetting};
18pub use diagnostic::{Diagnostic, Severity, Span};
19pub use rules::{RuleCategory, RuleDefinition};
20
21#[derive(Debug, Clone, Default)]
23pub struct LintOptions {
24 pub config_path: Option<PathBuf>,
25 pub extra_ignores: Vec<String>,
26 pub minimum_severity: Option<Severity>,
27}
28
29#[derive(Debug, Clone, Serialize)]
31pub struct LintReport {
32 pub version: &'static str,
33 pub root: PathBuf,
34 pub files_scanned: usize,
35 pub estimated_total_tokens: usize,
36 pub estimated_duplicated_tokens: usize,
37 pub diagnostics: Vec<Diagnostic>,
38 #[serde(skip_serializing_if = "Vec::is_empty")]
39 pub fixed_files: Vec<PathBuf>,
40}
41
42impl LintReport {
43 pub fn error_count(&self) -> usize {
44 self.diagnostics
45 .iter()
46 .filter(|item| item.severity == Severity::Error)
47 .count()
48 }
49
50 pub fn warning_count(&self) -> usize {
51 self.diagnostics
52 .iter()
53 .filter(|item| item.severity == Severity::Warning)
54 .count()
55 }
56}
57
58pub fn lint(root: &Path, options: &LintOptions) -> Result<LintReport> {
65 let root = root.canonicalize()?;
66 let config = Config::load(&root, options.config_path.as_deref())?;
67 let project = project::ProjectFacts::inspect(&root)?;
68 let files = scanner::scan(&root, &config, &options.extra_ignores)?;
69 Ok(analysis::analyze(
70 &root,
71 &files,
72 &project,
73 &config,
74 options.minimum_severity,
75 ))
76}