Skip to main content

pray_core/
package_upstream_overlay.rs

1use std::collections::BTreeMap;
2
3pub enum OverlayFileChange {
4    Changed,
5    LocalOnly,
6    Missing,
7}
8
9pub fn overlay_file_changes(
10    local_content: &BTreeMap<String, Vec<u8>>,
11    upstream_content: &BTreeMap<String, Vec<u8>>,
12) -> Vec<(String, OverlayFileChange)> {
13    let mut paths = BTreeMap::new();
14    for path in local_content.keys().chain(upstream_content.keys()) {
15        paths.insert(path.clone(), ());
16    }
17    let mut changes = Vec::new();
18    for path in paths.keys() {
19        match (local_content.get(path), upstream_content.get(path)) {
20            (Some(local), Some(upstream)) if local != upstream => {
21                changes.push((path.clone(), OverlayFileChange::Changed));
22            }
23            (Some(_), None) => changes.push((path.clone(), OverlayFileChange::LocalOnly)),
24            (None, Some(_)) => changes.push((path.clone(), OverlayFileChange::Missing)),
25            _ => {}
26        }
27    }
28    changes
29}
30
31pub fn overlay_drift_line(
32    fork: &str,
33    upstream_name: &str,
34    upstream_version: &str,
35    path: &str,
36    change: &OverlayFileChange,
37) -> String {
38    match change {
39        OverlayFileChange::Changed => {
40            format!("{fork} {path} differs from {upstream_name} {upstream_version}")
41        }
42        OverlayFileChange::LocalOnly => format!("{fork} {path} local"),
43        OverlayFileChange::Missing => format!("{fork} {path} missing from fork"),
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn overlay_file_changes_name_local_and_missing_paths() {
53        let local = BTreeMap::from([
54            ("README.md".to_string(), b"edit".to_vec()),
55            ("overlays/note.md".to_string(), b"local".to_vec()),
56        ]);
57        let upstream = BTreeMap::from([
58            ("README.md".to_string(), b"up".to_vec()),
59            ("exports/a.md".to_string(), b"a".to_vec()),
60        ]);
61        let changes = overlay_file_changes(&local, &upstream);
62        assert!(changes.iter().any(|(path, change)| {
63            path == "README.md" && matches!(change, OverlayFileChange::Changed)
64        }));
65        assert!(changes.iter().any(|(path, change)| {
66            path == "overlays/note.md" && matches!(change, OverlayFileChange::LocalOnly)
67        }));
68        assert!(changes.iter().any(|(path, change)| {
69            path == "exports/a.md" && matches!(change, OverlayFileChange::Missing)
70        }));
71    }
72}