use ec4rs::property::{self, TrimTrailingWs};
use itertools::Itertools;
use line_ending::LineEnding;
use snafu::ensure;
use crate::{errors, files};
use super::{PropertyHandler, charset, end_of_line};
pub struct TrimTrailingWhitespaceHandler {
charset: property::Charset,
line_ending: LineEnding,
}
impl PropertyHandler for TrimTrailingWhitespaceHandler {
fn check(&self, file_path: &std::path::Path) -> anyhow::Result<()> {
let content = files::read_file(file_path, &self.charset)?;
let lines_with_trailing_whitespace =
self.get_lines_with_trailing_whitespace(&content).count();
ensure!(
lines_with_trailing_whitespace == 0,
errors::TrimTrailingWhitespaceSnafu {
lines_with_trailing_whitespace,
}
);
Ok(())
}
fn fix(&self, file_path: &std::path::Path) -> anyhow::Result<()> {
let mut content = files::read_file(file_path, &self.charset)?;
if self.remove_trailing_whitespace(&mut content) {
files::overwrite_file(file_path, &self.charset, &content)?;
}
Ok(())
}
}
impl TrimTrailingWhitespaceHandler {
pub fn build(properties: &ec4rs::Properties) -> Option<TrimTrailingWhitespaceHandler> {
match properties.get::<property::TrimTrailingWs>() {
Ok(TrimTrailingWs::Value(true)) => Some(TrimTrailingWhitespaceHandler {
charset: charset::get_charset(properties),
line_ending: end_of_line::get_line_ending(properties),
}),
Ok(TrimTrailingWs::Value(false)) | Err(_) => None,
}
}
fn get_lines_with_trailing_whitespace(&self, content: &str) -> impl Iterator<Item = usize> {
content
.split(self.line_ending.as_str())
.enumerate()
.filter_map(|(index, line)| {
line.chars().rev().nth(0).and_then(|last_char| {
if last_char.is_whitespace() {
Some(index)
} else {
None
}
})
})
}
fn remove_trailing_whitespace(&self, content: &mut String) -> bool {
let mut lines_with_trailing_whitespace =
self.get_lines_with_trailing_whitespace(content).peekable();
let trailing_whitespace = lines_with_trailing_whitespace.peek().is_some();
if trailing_whitespace {
let mut lines = content.split(self.line_ending.as_str()).collect_vec();
for index in lines_with_trailing_whitespace {
lines[index] = lines[index].trim_end();
}
*content = lines.join(self.line_ending.as_str());
}
trailing_whitespace
}
}
#[cfg(test)]
mod tests;