use crate::checkstyle::api::check::FileSetCheck;
use crate::checkstyle::api::config::{Configuration, Context};
use crate::checkstyle::api::error::{CheckstyleError, CheckstyleResult};
use crate::checkstyle::api::event::{AuditEvent, AuditEventType, SeverityLevel};
use crate::checkstyle::api::file::{FileContents, FileText};
use crate::checkstyle::api::listener::AuditListener;
use crate::checkstyle::runner::module_factory::{
DefaultModuleFactory, ModuleFactory, ModuleInstance, configure_module,
};
use std::path::{Path, PathBuf};
pub struct Checker {
file_set_checks: Vec<Box<dyn FileSetCheck>>,
listeners: Vec<Box<dyn AuditListener>>,
file_extensions: Vec<String>,
context: Context,
charset: String,
}
impl Checker {
pub fn new() -> Self {
Self {
file_set_checks: Vec::new(),
listeners: Vec::new(),
file_extensions: vec!["java".to_string()],
context: Context::new(),
charset: "UTF-8".to_string(),
}
}
pub fn add_file_set_check(&mut self, check: Box<dyn FileSetCheck>) {
self.file_set_checks.push(check);
}
pub fn add_listener(&mut self, listener: Box<dyn AuditListener>) {
self.listeners.push(listener);
}
pub fn configure(&mut self, config: &Configuration) -> CheckstyleResult<()> {
if let Some(extensions) = config.get_property("fileExtensions") {
self.file_extensions = extensions
.split(',')
.map(|s| s.trim().to_string())
.collect();
}
if let Some(charset) = config.get_property("charset") {
self.charset = charset.clone();
}
if let Some(severity_str) = config.get_property("severity") {
self.context.severity = match severity_str.as_str() {
"ignore" => SeverityLevel::Ignore,
"info" => SeverityLevel::Info,
"warning" => SeverityLevel::Warning,
"error" => SeverityLevel::Error,
_ => SeverityLevel::Error,
};
}
let factory = DefaultModuleFactory::new();
for child_config in config.get_children() {
let mut instance = factory.create_module(&child_config.name)?;
configure_module(&mut instance, child_config, &self.context)?;
match instance {
ModuleInstance::FileSetCheck(check) => {
self.add_file_set_check(check);
}
ModuleInstance::Check(_) => {
}
}
}
Ok(())
}
pub fn process(&mut self, files: &[PathBuf]) -> CheckstyleResult<usize> {
let mut error_count = 0;
let audit_started = AuditEvent::new(None, None, AuditEventType::AuditStarted);
for listener in &mut self.listeners {
listener.audit_started(&audit_started)?;
}
for check in &mut self.file_set_checks {
check.init()?;
check.begin_processing(&self.charset)?;
}
for file in files {
if !self.should_process_file(file) {
continue;
}
let file_started =
AuditEvent::new(Some(file.clone()), None, AuditEventType::FileStarted);
for listener in &mut self.listeners {
listener.file_started(&file_started)?;
}
let file_text = self.read_file(file)?;
let file_contents = FileContents::new(file_text);
for check in &mut self.file_set_checks {
match check.process(file, file_contents.get_text()) {
Ok(violations) => {
for violation in violations {
if violation.severity_level == SeverityLevel::Error {
error_count += 1;
}
let event = AuditEvent::new(
Some(file.clone()),
Some(violation),
AuditEventType::AddError,
);
for listener in &mut self.listeners {
listener.add_error(&event)?;
}
}
}
Err(e) => {
let event =
AuditEvent::new(Some(file.clone()), None, AuditEventType::AddException);
for listener in &mut self.listeners {
listener.add_exception(&event, &e)?;
}
}
}
}
let file_finished =
AuditEvent::new(Some(file.clone()), None, AuditEventType::FileFinished);
for listener in &mut self.listeners {
listener.file_finished(&file_finished)?;
}
}
for check in &mut self.file_set_checks {
check.finish_processing()?;
check.destroy()?;
}
let audit_finished = AuditEvent::new(None, None, AuditEventType::AuditFinished);
for listener in &mut self.listeners {
listener.audit_finished(&audit_finished)?;
}
Ok(error_count)
}
fn should_process_file(&self, file: &Path) -> bool {
if let Some(ext) = file.extension() {
if let Some(ext_str) = ext.to_str() {
return self.file_extensions.iter().any(|e| e == ext_str);
}
}
false
}
fn read_file(&self, path: &Path) -> CheckstyleResult<FileText> {
let content = std::fs::read_to_string(path).map_err(CheckstyleError::Io)?;
Ok(FileText::new(path.to_path_buf(), content))
}
}
impl Default for Checker {
fn default() -> Self {
Self::new()
}
}