Skip to main content

ontocore_diff/
format.rs

1use crate::model::DiffResult;
2
3pub fn format_diff_text(diff: &DiffResult, breaking_only: bool) -> String {
4    let mut out = String::new();
5    if !breaking_only {
6        out.push_str(&format!(
7            "Summary: {} entity, {} axiom, {} annotation, {} import, {} inference, {} breaking\n\n",
8            diff.entity_changes.len(),
9            diff.axiom_changes.len(),
10            diff.annotation_changes.len(),
11            diff.import_changes.len(),
12            diff.inference_changes.len(),
13            diff.breaking_changes.len(),
14        ));
15    }
16    if !diff.breaking_changes.is_empty() {
17        out.push_str("Breaking changes:\n");
18        for b in &diff.breaking_changes {
19            out.push_str(&format!("  - [{}] {}\n", reason_label(b.reason), b.message));
20        }
21        out.push('\n');
22    }
23    if breaking_only {
24        return out;
25    }
26    if !diff.entity_changes.is_empty() {
27        out.push_str("Entity changes:\n");
28        for e in &diff.entity_changes {
29            out.push_str(&format!("  - {:?} {}\n", e.kind, e.iri));
30        }
31        out.push('\n');
32    }
33    if !diff.axiom_changes.is_empty() {
34        out.push_str("Axiom changes:\n");
35        for a in &diff.axiom_changes {
36            out.push_str(&format!(
37                "  - {} {} {} {} {}\n",
38                a.change, a.axiom_kind, a.subject, a.predicate, a.object
39            ));
40        }
41        out.push('\n');
42    }
43    if !diff.annotation_changes.is_empty() {
44        out.push_str("Annotation changes:\n");
45        for a in &diff.annotation_changes {
46            out.push_str(&format!("  - {} {} {} {}\n", a.change, a.subject, a.predicate, a.object));
47        }
48        out.push('\n');
49    }
50    if !diff.import_changes.is_empty() {
51        out.push_str("Import changes:\n");
52        for i in &diff.import_changes {
53            out.push_str(&format!("  - {} {}\n", i.change, i.import_iri));
54        }
55        out.push('\n');
56    }
57    out
58}
59
60pub fn format_diff_markdown(diff: &DiffResult, breaking_only: bool) -> String {
61    let mut out = String::from("# Ontology semantic diff\n\n");
62    if !diff.breaking_changes.is_empty() {
63        out.push_str("## Breaking changes\n\n");
64        for b in &diff.breaking_changes {
65            out.push_str(&format!("- **{}**: {}\n", reason_label(b.reason), b.message));
66        }
67        out.push('\n');
68    }
69    if breaking_only {
70        return out;
71    }
72    out.push_str(&format!(
73        "## Summary\n\n| Category | Count |\n|---|---|\n| Entities | {} |\n| Axioms | {} |\n| Annotations | {} |\n| Imports | {} |\n| Inferences | {} |\n| Breaking | {} |\n\n",
74        diff.entity_changes.len(),
75        diff.axiom_changes.len(),
76        diff.annotation_changes.len(),
77        diff.import_changes.len(),
78        diff.inference_changes.len(),
79        diff.breaking_changes.len(),
80    ));
81    out
82}
83
84pub fn format_diff_json(diff: &DiffResult) -> String {
85    serde_json::to_string_pretty(diff).unwrap_or_else(|_| "{}".to_string())
86}
87
88fn reason_label(reason: crate::model::BreakingReason) -> &'static str {
89    use crate::model::BreakingReason::*;
90    match reason {
91        RemovedEntity => "removed_entity",
92        RenamedIri => "renamed_iri",
93        RemovedSuperclass => "removed_superclass",
94        RemovedImport => "removed_import",
95        UnsatisfiableClass => "unsatisfiable_class",
96        DomainRangeChange => "domain_range_change",
97    }
98}