use difference::Changeset; use difference::Difference; use sputnik::html_escape; use std::cmp; use std::fmt::Write as FmtWrite; pub fn diff(first: &str, second: &str) -> String { if first == second { return "(no changes)".into(); } let Changeset { diffs, .. } = Changeset::new(first, second, "\n"); let mut output = String::new(); output.push_str("
");

    for i in 0..diffs.len() {
        match diffs[i] {
            Difference::Same(ref text) => {
                let text = html_escape(text);
                let lines: Vec<_> = text.split('\n').collect();
                if i == 0 {
                    output.push_str(&lines[lines.len().saturating_sub(3)..].join("\n"));
                } else if i == diffs.len() - 1 {
                    output.push_str(&lines[..cmp::min(3, lines.len())].join("\n"));
                } else {
                    output.push_str(&text);
                }
            }
            Difference::Add(ref text) => {
                output.push_str("
"); if i == 0 { output.push_str(&html_escape(text).replace('\n', "
")); } else { match diffs.get(i - 1) { Some(Difference::Rem(ref rem)) => { word_diff(&mut output, rem, text, "ins"); } _ => { output.push_str(&html_escape(text).replace('\n', "
")); } } } output.push_str("\n
"); } Difference::Rem(ref text) => { output.push_str("
"); match diffs.get(i + 1) { Some(Difference::Add(ref add)) => { word_diff(&mut output, add, text, "del"); } _ => { output.push_str(&html_escape(text).replace('\n', "
")); } } output.push_str("\n
"); } } } output.push_str("
"); output } fn word_diff(out: &mut String, text1: &str, text2: &str, tagname: &str) { let Changeset { diffs, .. } = Changeset::new(text1, text2, " "); for c in diffs { match c { Difference::Same(ref z) => { out.push_str(&html_escape(z).replace('\n', "
")); out.push(' '); } Difference::Add(ref z) => { write!( out, "<{0}>{1} ", tagname, html_escape(z).replace('\n', "
") ) .expect("write error"); } _ => {} } } }