1use crate::surface::Surface;
5
6#[derive(Debug, Default)]
8pub struct SurfaceDiff {
9 pub added: Vec<String>,
11 pub removed: Vec<String>,
13 pub changed: Vec<(String, bool)>,
15}
16
17impl SurfaceDiff {
18 pub fn is_empty(&self) -> bool {
21 self.added.is_empty() && self.removed.is_empty() && self.changed.is_empty()
22 }
23}
24
25pub fn compare(old: &Surface, new: &Surface) -> SurfaceDiff {
28 let mut diff = SurfaceDiff::default();
29 for (path, info) in &new.items {
30 match old.items.get(path) {
31 None => diff.added.push(path.clone()),
32 Some(prev) => {
33 if info.kind != "module" && prev.fingerprint != info.fingerprint {
36 diff.changed
37 .push((path.clone(), prev.signature != info.signature));
38 }
39 }
40 }
41 }
42 for path in old.items.keys() {
43 if !new.items.contains_key(path) {
44 diff.removed.push(path.clone());
45 }
46 }
47 diff
48}
49
50pub fn render(diff: &SurfaceDiff) -> String {
53 if diff.is_empty() {
54 return "surface: no public API changes\n".into();
55 }
56 let mut out = String::new();
57 for p in &diff.added {
58 out.push_str(&format!("ADDED {p}\n"));
59 }
60 for p in &diff.removed {
61 out.push_str(&format!("REMOVED {p}\n"));
62 }
63 for (p, sig) in &diff.changed {
64 let what = if *sig { "signature" } else { "body" };
65 out.push_str(&format!("CHANGED {p} ({what})\n"));
66 }
67 out
68}