pub fn lines(old: &str, new: &str) -> String {
let a: Vec<&str> = old.lines().collect();
let b: Vec<&str> = new.lines().collect();
let old_set: std::collections::HashSet<&str> = a.iter().copied().collect();
let new_set: std::collections::HashSet<&str> = b.iter().copied().collect();
let mut out = String::new();
for l in &a {
if !new_set.contains(l) {
out.push_str("- ");
out.push_str(l);
out.push('\n');
}
}
for l in &b {
if !old_set.contains(l) {
out.push_str("+ ");
out.push_str(l);
out.push('\n');
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn identical_produces_no_diff() {
assert_eq!(lines("a\nb\nc", "a\nb\nc"), "");
}
#[test]
fn added_lines_prefixed_plus() {
assert_eq!(lines("a\nb", "a\nb\nc"), "+ c\n");
}
#[test]
fn removed_lines_prefixed_minus() {
assert_eq!(lines("a\nb\nc", "a\nc"), "- b\n");
}
#[test]
fn removed_come_before_added() {
assert_eq!(lines("old", "new"), "- old\n+ new\n");
}
#[test]
fn empty_old_marks_everything_added() {
assert_eq!(lines("", "x\ny"), "+ x\n+ y\n");
}
#[test]
fn empty_new_marks_everything_removed() {
assert_eq!(lines("x\ny", ""), "- x\n- y\n");
}
}