use crate::config::Config;
use crate::core::{CheckContext, CheckRegistry, Issue};
use crate::parser::{PythonParser, RustPythonParser};
use anyhow::{Context, Result};
use std::path::{Path, PathBuf};
pub struct DeterministicAnalyzer {
registry: CheckRegistry,
parser: RustPythonParser,
}
#[derive(Debug, Default)]
pub struct AnalyzeResult {
pub issues: Vec<Issue>,
pub file_errors: Vec<FileError>,
}
#[derive(Debug, Clone)]
pub struct FileError {
pub path: PathBuf,
pub message: String,
}
impl Default for DeterministicAnalyzer {
fn default() -> Self {
Self::new()
}
}
impl DeterministicAnalyzer {
pub fn new() -> Self {
Self {
registry: CheckRegistry::new(),
parser: RustPythonParser,
}
}
pub fn registry(&self) -> &CheckRegistry {
&self.registry
}
pub fn analyze_file(&self, path: &Path, config: &Config) -> Result<Vec<Issue>> {
let source =
std::fs::read_to_string(path).with_context(|| format!("read {}", path.display()))?;
let parsed = self
.parser
.parse_file(&source, path)
.with_context(|| format!("parse {}", path.display()))?;
let ctx = CheckContext {
path,
source: &source,
parsed: &parsed,
config,
};
let mut issues = Vec::new();
for check in self.registry.iter() {
if config.is_check_enabled(check.id()) {
let severity_override = config.check_config(check.id()).severity;
let mut check_issues = check.run(&ctx);
if let Some(severity) = severity_override {
for issue in &mut check_issues {
issue.severity = severity;
}
}
issues.extend(check_issues);
}
}
issues.sort_by(|a, b| {
(&a.file, a.line, a.column, &a.id, &a.message)
.cmp(&(&b.file, b.line, b.column, &b.id, &b.message))
});
Ok(issues)
}
pub fn analyze_paths(&self, paths: &[PathBuf], config: &Config) -> AnalyzeResult {
let mut result = AnalyzeResult::default();
for path in paths {
match self.analyze_file(path, config) {
Ok(mut issues) => result.issues.append(&mut issues),
Err(err) => {
eprintln!("warning: {err:#}");
result.file_errors.push(FileError {
path: path.clone(),
message: format!("{err:#}"),
});
}
}
}
result.issues.sort_by(|a, b| {
(&a.file, a.line, a.column, &a.id, &a.message)
.cmp(&(&b.file, b.line, b.column, &b.id, &b.message))
});
result
}
}