Skip to main content

docgen_diff/
payloads.rs

1//! Payload summarization — a faithful port of `payloads.ts`.
2//!
3//! Produces the lightweight JSON the history page ships as data: heavy `hunks`
4//! and rendered `blocks` are stripped while file-tree and metadata are kept.
5
6use crate::types::{DocDiffFile, DocDiffReport, DocDiffTimelinePoint};
7
8/// Clone a file with its heavy `hunks` cleared and `blocks` dropped.
9pub fn summarize_file(file: &DocDiffFile) -> DocDiffFile {
10    DocDiffFile {
11        hunks: vec![],
12        blocks: None,
13        ..file.clone()
14    }
15}
16
17/// Clone a timeline point with each file summarized.
18pub fn summarize_timeline_point(point: &DocDiffTimelinePoint) -> DocDiffTimelinePoint {
19    DocDiffTimelinePoint {
20        files: point.files.iter().map(summarize_file).collect(),
21        ..point.clone()
22    }
23}
24
25/// Clone a report with both its timeline points and top-level files summarized.
26pub fn summarize_report(report: &DocDiffReport) -> DocDiffReport {
27    DocDiffReport {
28        timeline: report
29            .timeline
30            .iter()
31            .map(summarize_timeline_point)
32            .collect(),
33        files: report.files.iter().map(summarize_file).collect(),
34        ..report.clone()
35    }
36}
37
38#[cfg(test)]
39mod tests {
40    use super::*;
41    use crate::types::{
42        DocDiffBlock, DocDiffBlockKind, DocDiffFileStatus, DocDiffFileTreeNode, DocDiffHunk,
43        DocDiffLine, DocDiffLineKind, DocDiffTimelinePointKind,
44    };
45
46    fn full_point() -> DocDiffTimelinePoint {
47        DocDiffTimelinePoint {
48            id: "abc123".into(),
49            kind: DocDiffTimelinePointKind::Commit,
50            hash: Some("abc123".into()),
51            short_hash: "abc123".into(),
52            subject: "docs".into(),
53            author: Some("author".into()),
54            date: Some("2026-05-13T00:00:00.000Z".into()),
55            base_ref: "abc122".into(),
56            head_ref: "abc123".into(),
57            files: vec![DocDiffFile {
58                path: "docs/a.md".into(),
59                old_path: None,
60                status: DocDiffFileStatus::Modified,
61                added_lines: 1,
62                removed_lines: 1,
63                hunks: vec![DocDiffHunk {
64                    old_start: 1,
65                    old_lines: 1,
66                    new_start: 1,
67                    new_lines: 1,
68                    lines: vec![DocDiffLine {
69                        kind: DocDiffLineKind::Added,
70                        old_line: None,
71                        new_line: Some(1),
72                        text: "new".into(),
73                    }],
74                }],
75                blocks: Some(vec![DocDiffBlock {
76                    id: "block".into(),
77                    kind: DocDiffBlockKind::Added,
78                    raw: "new".into(),
79                    html: "<p>new</p>".into(),
80                    old_index: None,
81                    new_index: Some(1),
82                }]),
83            }],
84            file_tree: vec![DocDiffFileTreeNode::Group {
85                id: "docs".into(),
86                label: "docs".into(),
87                children: vec![DocDiffFileTreeNode::File {
88                    id: "docs/a.md".into(),
89                    label: "a.md".into(),
90                    path: "docs/a.md".into(),
91                    old_path: None,
92                    status: DocDiffFileStatus::Modified,
93                    added_lines: 1,
94                    removed_lines: 1,
95                }],
96            }],
97            total_added_lines: 1,
98            total_removed_lines: 1,
99            warnings: vec![],
100        }
101    }
102
103    #[test]
104    fn summarize_strips_hunks_and_blocks() {
105        let summary = summarize_timeline_point(&full_point());
106        assert_eq!(summary.files.len(), 1);
107        assert!(summary.files[0].hunks.is_empty());
108        assert!(summary.files[0].blocks.is_none());
109        assert!(matches!(
110            summary.file_tree[0],
111            DocDiffFileTreeNode::Group { .. }
112        ));
113    }
114}