diff options
Diffstat (limited to 'src/diff.rs')
-rw-r--r-- | src/diff.rs | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/src/diff.rs b/src/diff.rs new file mode 100644 index 0000000..382c7db --- /dev/null +++ b/src/diff.rs @@ -0,0 +1,86 @@ +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 "<em>(no changes)</em>".into(); + } + + let Changeset { diffs, .. } = Changeset::new(&first, &second, "\n"); + + let mut output = String::new(); + + output.push_str("<pre>"); + + 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("<div class=addition>"); + if i == 0 { + output.push_str(&html_escape(text).replace("\n", "<br>")); + } 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", "<br>")); + } + } + } + output.push_str("\n</div>"); + } + Difference::Rem(ref text) => { + output.push_str("<div class=deletion>"); + 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", "<br>")); + } + } + output.push_str("\n</div>"); + } + } + } + + output.push_str("</pre>"); + 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", "<br>")); + out.push(' '); + } + Difference::Add(ref z) => { + write!( + out, + "<{0}>{1}</{0}> ", + tagname, + html_escape(z).replace("\n", "<br>") + ) + .expect("write error"); + } + _ => {} + } + } +} |