1use std::fmt;
2
3use similar::TextDiff;
4
5use crate::{FilesError, Operation, Plan};
6
7impl fmt::Display for Operation {
8 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
9 match self {
10 Self::Create { path, entry: _ } => write!(f, "create {path}"),
11 Self::Replace { path, entry: _ } => write!(f, "replace {path}"),
12 Self::Delete { path } => write!(f, "delete {path}"),
13 }
14 }
15}
16
17impl Operation {
18 pub fn unified_diff(&self, plan: &Plan) -> Result<Option<String>, FilesError> {
27 let path = self.path();
28 let (old, new, old_name, new_name) = match self {
29 Self::Create { entry, path: _ } => (
30 Vec::new(),
31 entry.content.clone(),
32 "/dev/null".to_owned(),
33 format!("b/{path}"),
34 ),
35 Self::Replace { entry, path: _ } => (
36 plan.current_content(path)?.unwrap_or_default(),
37 entry.content.clone(),
38 format!("a/{path}"),
39 format!("b/{path}"),
40 ),
41 Self::Delete { path: _ } => (
42 plan.current_content(path)?.unwrap_or_default(),
43 Vec::new(),
44 format!("a/{path}"),
45 "/dev/null".to_owned(),
46 ),
47 };
48 let (Ok(old), Ok(new)) = (str::from_utf8(&old), str::from_utf8(&new)) else {
49 return Ok(None);
50 };
51 let diff = TextDiff::from_lines(old, new)
52 .unified_diff()
53 .header(&old_name, &new_name)
54 .to_string();
55 Ok((!diff.is_empty()).then_some(diff))
56 }
57}