use std::io;
use camino::Utf8Path;
use similar::{Algorithm, TextDiff};
use super::delta::LineDelta;
#[must_use]
pub fn render_report_line(display_path: &Utf8Path, delta: LineDelta) -> String {
format!(
"{display_path} +{} -{}",
delta.insertions(),
delta.deletions()
)
}
#[must_use]
pub fn render_summary(changed: usize, unchanged: usize, errored: usize) -> String {
let clauses: Vec<String> = [
(changed, "would be reformatted"),
(unchanged, "left unchanged"),
(errored, "could not be read"),
]
.into_iter()
.filter(|(count, _)| *count > 0)
.map(|(count, verb)| format!("{} {verb}", file_count(count)))
.collect();
if clauses.is_empty() {
String::from("No files were analysed.")
} else {
format!("{}.", clauses.join(", "))
}
}
fn file_count(count: usize) -> String {
if count == 1 {
String::from("1 file")
} else {
format!("{count} files")
}
}
#[derive(Debug, Clone, Copy)]
pub struct DiffOptions {
pub context_radius: usize,
pub patience_threshold: usize,
}
pub fn write_unified_diff(
out: &mut impl io::Write,
display_path: &Utf8Path,
original: &str,
formatted: &str,
options: DiffOptions,
) -> io::Result<()> {
let longest = line_count(original).max(line_count(formatted));
let algorithm = if longest > options.patience_threshold {
Algorithm::Patience
} else {
Algorithm::Myers
};
let header = display_path.as_str().replace('\\', "/");
let diff = TextDiff::configure()
.algorithm(algorithm)
.diff_lines(original, formatted);
let mut unified = diff.unified_diff();
unified
.context_radius(options.context_radius)
.header(&header, &header);
unified.to_writer(out)
}
fn line_count(text: &str) -> usize {
let mut lines = 0;
let mut unterminated = false;
let mut characters = text.chars().peekable();
while let Some(character) = characters.next() {
match character {
'\n' => {
lines += 1;
unterminated = false;
}
'\r' => {
if characters.peek() == Some(&'\n') {
characters.next();
}
lines += 1;
unterminated = false;
}
_ => unterminated = true,
}
}
lines + usize::from(unterminated)
}
#[cfg(test)]
#[path = "render_tests.rs"]
mod tests;