Skip to main content

asbuilt_core/
check.rs

1//! The drift check: the committed model against a fresh survey, as a
2//! unified diff when they differ. Line endings are normalized first so
3//! a consumer with `autocrlf` gets a clean check.
4
5use similar::TextDiff;
6
7/// What the comparison found.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum Outcome {
10    Current,
11    /// A unified diff from the committed text to the fresh one.
12    Drift(String),
13}
14
15fn normalize(text: &str) -> String {
16    text.replace("\r\n", "\n")
17}
18
19/// Compare the committed text with the fresh survey; `label` is the
20/// path shown in the diff header.
21pub fn compare(committed: &str, fresh: &str, label: &str) -> Outcome {
22    let committed = normalize(committed);
23    let fresh = normalize(fresh);
24    if committed == fresh {
25        return Outcome::Current;
26    }
27    let diff = TextDiff::from_lines(&committed, &fresh)
28        .unified_diff()
29        .header(&format!("a/{label}"), &format!("b/{label}"))
30        .to_string();
31    Outcome::Drift(diff)
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37
38    #[test]
39    fn identical_text_is_current() {
40        assert_eq!(compare("a\nb\n", "a\nb\n", "m.c4"), Outcome::Current);
41    }
42
43    #[test]
44    fn crlf_in_the_committed_text_is_not_drift() {
45        assert_eq!(compare("a\r\nb\r\n", "a\nb\n", "m.c4"), Outcome::Current);
46    }
47
48    #[test]
49    fn a_changed_line_is_drift_with_both_versions_and_the_label_in_the_header() {
50        match compare("a\nb\nc\n", "a\nB\nc\n", "docs/model.c4") {
51            Outcome::Drift(diff) => {
52                assert!(
53                    diff.starts_with("--- a/docs/model.c4\n+++ b/docs/model.c4\n"),
54                    "{diff}"
55                );
56                assert!(diff.contains("\n-b\n"), "{diff}");
57                assert!(diff.contains("\n+B\n"), "{diff}");
58            }
59            other => panic!("expected Drift, got {other:?}"),
60        }
61    }
62
63    #[test]
64    fn an_added_line_is_drift() {
65        assert!(matches!(compare("a\n", "a\nb\n", "m"), Outcome::Drift(_)));
66    }
67}