use crate::checkstyle::api::check::FileSetCheck;
use crate::checkstyle::api::config::{Configurable, Configuration, Context, Contextualizable};
use crate::checkstyle::api::error::CheckstyleResult;
use crate::checkstyle::api::file::FileText;
use crate::checkstyle::api::violation::Violation;
use crate::checkstyle::utils::common_util::line_length_expanded;
use regex::Regex;
use std::collections::BTreeSet;
use std::path::PathBuf;
pub struct LineLengthCheck {
max: usize,
ignore_pattern: Regex,
tab_width: usize,
context: Context,
violations: BTreeSet<Violation>,
}
impl LineLengthCheck {
pub fn new() -> Self {
Self {
max: 80, ignore_pattern: Regex::new(r"^(package|import) .*").unwrap(),
tab_width: 8, context: Context::new(),
violations: BTreeSet::new(),
}
}
pub fn set_max(&mut self, max: usize) {
self.max = max;
}
pub fn set_ignore_pattern(&mut self, pattern: String) -> Result<(), regex::Error> {
self.ignore_pattern = Regex::new(&pattern)?;
Ok(())
}
pub fn set_tab_width(&mut self, tab_width: usize) {
self.tab_width = tab_width;
}
}
impl Default for LineLengthCheck {
fn default() -> Self {
Self::new()
}
}
impl Configurable for LineLengthCheck {
fn configure(&mut self, config: &Configuration) -> CheckstyleResult<()> {
if let Some(max_str) = config.get_property("max") {
if let Ok(max_val) = max_str.parse::<usize>() {
self.set_max(max_val);
}
}
if let Some(pattern_str) = config.get_property("ignorePattern") {
if let Err(e) = self.set_ignore_pattern(pattern_str.clone()) {
return Err(crate::checkstyle::api::error::CheckstyleError::Configuration(format!(
"Invalid ignorePattern: {e}"
)));
}
}
if let Some(tab_width_str) = config.get_property("tabWidth") {
if let Ok(tab_width_val) = tab_width_str.parse::<usize>() {
self.set_tab_width(tab_width_val);
}
}
Ok(())
}
}
impl Contextualizable for LineLengthCheck {
fn contextualize(&mut self, context: &Context) -> CheckstyleResult<()> {
self.context = context.clone();
if self.tab_width == 8 {
self.tab_width = context.tab_width;
}
Ok(())
}
}
impl FileSetCheck for LineLengthCheck {
fn set_message_dispatcher(
&mut self,
_dispatcher: Box<dyn crate::checkstyle::api::check::MessageDispatcher>,
) {
}
fn init(&mut self) -> CheckstyleResult<()> {
self.violations.clear();
Ok(())
}
fn destroy(&mut self) -> CheckstyleResult<()> {
Ok(())
}
fn begin_processing(&mut self, _charset: &str) -> CheckstyleResult<()> {
Ok(())
}
fn process(
&mut self,
_file: &PathBuf,
file_text: &FileText,
) -> CheckstyleResult<BTreeSet<Violation>> {
let mut violations = BTreeSet::new();
for (i, line) in file_text.lines.iter().enumerate() {
let line_no = i + 1;
let real_length = line_length_expanded(line, self.tab_width);
if real_length > self.max && !self.ignore_pattern.is_match(line) {
let violation = Violation::new(
line_no,
0,
0,
0,
self.context.severity,
"LineLength".to_string(),
"maxLineLen".to_string(),
vec![self.max.to_string(), real_length.to_string()],
"checkstyle".to_string(),
"LineLength".to_string(),
None,
);
violations.insert(violation);
}
}
Ok(violations)
}
fn finish_processing(&mut self) -> CheckstyleResult<()> {
Ok(())
}
}