use std::collections::HashMap;
use ec4rs::property::{self, EndOfLine};
use line_ending::LineEnding;
use snafu::ensure;
use crate::{errors, files};
use super::{PropertyHandler, charset};
pub struct EndOfLineHandler {
charset: property::Charset,
end_of_line: property::EndOfLine,
}
impl PropertyHandler for EndOfLineHandler {
fn check(&self, file_path: &std::path::Path) -> anyhow::Result<()> {
let content = files::read_file(file_path, &self.charset)?;
let wrong_end_of_lines: HashMap<property::EndOfLine, usize> = self
.get_wrong_line_endings(&content)
.map(|(line_ending, count)| {
(
Self::line_ending_as_end_of_line(&line_ending),
count,
)
})
.collect();
ensure!(
wrong_end_of_lines.is_empty(),
errors::EndOfLineSnafu {
expected_end_of_line: self.end_of_line,
wrong_end_of_lines,
}
);
Ok(())
}
fn fix(&self, file_path: &std::path::Path) -> anyhow::Result<()> {
let mut content = files::read_file(file_path, &self.charset)?;
if self.fix_wrong_line_endings(&mut content) {
files::overwrite_file(file_path, &self.charset, &content)?;
}
Ok(())
}
}
impl EndOfLineHandler {
pub fn build(properties: &ec4rs::Properties) -> Option<EndOfLineHandler> {
match properties.get::<property::EndOfLine>() {
Ok(end_of_line) => Some(EndOfLineHandler {
charset: charset::get_charset(properties),
end_of_line,
}),
Err(_) => None, }
}
fn get_wrong_line_endings(&self, content: &str) -> impl Iterator<Item = (LineEnding, usize)> {
let expected_line_ending = Self::end_of_line_as_line_ending(&self.end_of_line);
let scores = LineEnding::score_mixed_types(content);
scores
.into_iter()
.filter(move |(line_ending, count)| *count != 0 && *line_ending != expected_line_ending)
}
fn fix_wrong_line_endings(&self, content: &mut String) -> bool {
let wrong_line_endings = self.get_wrong_line_endings(content).any(|_| true);
if wrong_line_endings {
*content = Self::end_of_line_as_line_ending(&self.end_of_line)
.denormalize(&LineEnding::normalize(content));
}
wrong_line_endings
}
fn end_of_line_as_line_ending(end_of_line: &EndOfLine) -> LineEnding {
match *end_of_line {
EndOfLine::Lf => LineEnding::LF,
EndOfLine::CrLf => LineEnding::CRLF,
EndOfLine::Cr => LineEnding::CR,
}
}
fn line_ending_as_end_of_line(line_ending: &LineEnding) -> EndOfLine {
match *line_ending {
LineEnding::LF => EndOfLine::Lf,
LineEnding::CRLF => EndOfLine::CrLf,
LineEnding::CR => EndOfLine::Cr,
}
}
}
#[cfg(test)]
mod tests;