semdiff_differ_json/
report_html.rs1use crate::{ChangeTag, JsonDiff, JsonDiffBody, JsonDiffLine, JsonDiffReporter, is_json_mime, try_into_json};
2use askama::Template;
3use semdiff_core::fs::FileLeaf;
4use semdiff_core::{DetailReporter, MayUnsupported};
5use semdiff_output::html::{HtmlReport, HtmlReportError};
6use thiserror::Error;
7
8const COMPARES_NAME: &str = "json";
9
10#[derive(Debug, Error)]
11pub enum JsonDiffReportError {
12 #[error("html report error: {0}")]
13 HtmlReport(#[from] HtmlReportError),
14}
15
16#[derive(Template)]
17#[template(path = "json_preview.html")]
18struct JsonPreviewTemplate<'a> {
19 body: JsonPreviewBody<'a>,
20}
21
22enum JsonPreviewBody<'a> {
23 Unchanged { body: &'a str },
24 Modified { lines: &'a [JsonDiffLine] },
25 Added { body: &'a str },
26 Deleted { body: &'a str },
27}
28
29impl JsonPreviewTemplate<'_> {
30 fn is_equal(line: &&JsonDiffLine) -> bool {
31 matches!(line.tag(), ChangeTag::Unchanged)
32 }
33}
34
35#[derive(Template)]
36#[template(path = "json_detail.html")]
37struct JsonDetailTemplate<'a> {
38 detail: JsonDetailBody<'a>,
39}
40
41enum JsonDetailBody<'a> {
42 Diff { lines: &'a [JsonDiffLine] },
43 Single { label: &'a str, body: &'a str },
44}
45
46impl JsonDetailBody<'_> {
47 fn is_multicolumn(&self) -> bool {
48 matches!(self, JsonDetailBody::Diff { .. })
49 }
50}
51
52impl DetailReporter<JsonDiff, FileLeaf, HtmlReport> for JsonDiffReporter {
53 type Error = JsonDiffReportError;
54
55 fn report_unchanged(
56 &self,
57 name: &str,
58 diff: &JsonDiff,
59 reporter: &HtmlReport,
60 ) -> Result<MayUnsupported<()>, Self::Error> {
61 let JsonDiffBody::Equal(body) = diff.body() else {
62 debug_assert!(false, "report_unchanged called with modified diff");
63 return Ok(MayUnsupported::Ok(()));
64 };
65 let preview_html = JsonPreviewTemplate {
66 body: JsonPreviewBody::Unchanged { body },
67 };
68 let detail_html = JsonDetailTemplate {
69 detail: JsonDetailBody::Single { label: "same", body },
70 };
71 reporter.record_unchanged(name, COMPARES_NAME, preview_html, detail_html)?;
72 Ok(MayUnsupported::Ok(()))
73 }
74
75 fn report_modified(
76 &self,
77 name: &str,
78 diff: &JsonDiff,
79 reporter: &HtmlReport,
80 ) -> Result<MayUnsupported<()>, Self::Error> {
81 let JsonDiffBody::Modified(lines) = diff.body() else {
82 debug_assert!(false, "report_modified called with equal diff");
83 return Ok(MayUnsupported::Ok(()));
84 };
85 let preview_html = JsonPreviewTemplate {
86 body: JsonPreviewBody::Modified { lines },
87 };
88 let detail_html = JsonDetailTemplate {
89 detail: JsonDetailBody::Diff { lines },
90 };
91 reporter.record_modified(name, COMPARES_NAME, preview_html, detail_html)?;
92 Ok(MayUnsupported::Ok(()))
93 }
94
95 fn report_added(
96 &self,
97 name: &str,
98 data: &FileLeaf,
99 reporter: &HtmlReport,
100 ) -> Result<MayUnsupported<()>, Self::Error> {
101 if !is_json_mime(&data.kind) {
102 return Ok(MayUnsupported::Unsupported);
103 }
104 let Some(body) = try_into_json(&data.content) else {
105 return Ok(MayUnsupported::Unsupported);
106 };
107 let preview_html = JsonPreviewTemplate {
108 body: JsonPreviewBody::Added { body: &body },
109 };
110 let detail_html = JsonDetailTemplate {
111 detail: JsonDetailBody::Single {
112 label: "added",
113 body: &body,
114 },
115 };
116 reporter.record_added(name, COMPARES_NAME, preview_html, detail_html)?;
117 Ok(MayUnsupported::Ok(()))
118 }
119
120 fn report_deleted(
121 &self,
122 name: &str,
123 data: &FileLeaf,
124 reporter: &HtmlReport,
125 ) -> Result<MayUnsupported<()>, Self::Error> {
126 if !is_json_mime(&data.kind) {
127 return Ok(MayUnsupported::Unsupported);
128 }
129 let Some(body) = try_into_json(&data.content) else {
130 return Ok(MayUnsupported::Unsupported);
131 };
132 let preview_html = JsonPreviewTemplate {
133 body: JsonPreviewBody::Deleted { body: &body },
134 };
135 let detail_html = JsonDetailTemplate {
136 detail: JsonDetailBody::Single {
137 label: "deleted",
138 body: &body,
139 },
140 };
141 reporter.record_deleted(name, COMPARES_NAME, preview_html, detail_html)?;
142 Ok(MayUnsupported::Ok(()))
143 }
144}