Skip to main content

mobench_report/
github.rs

1//! Canonical GitHub report adapters.
2//!
3//! This module owns payload shape and provider limits. HTTP authentication and
4//! transport stay in the CLI crate.
5
6use serde::Serialize;
7
8pub const GITHUB_CHECK_ANNOTATION_LIMIT: usize = 50;
9
10#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
11pub struct CheckRunAnnotation {
12    pub path: String,
13    pub start_line: u32,
14    pub end_line: u32,
15    pub annotation_level: String,
16    pub message: String,
17    pub title: String,
18}
19
20#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
21pub struct CheckRunRequest {
22    pub name: String,
23    pub head_sha: String,
24    pub status: String,
25    pub conclusion: String,
26    pub output: CheckRunOutput,
27}
28
29#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
30pub struct CheckRunOutput {
31    pub title: String,
32    pub summary: String,
33    #[serde(skip_serializing_if = "Vec::is_empty")]
34    pub annotations: Vec<CheckRunAnnotation>,
35}
36
37impl CheckRunRequest {
38    #[must_use]
39    pub fn completed(
40        name: impl Into<String>,
41        head_sha: impl Into<String>,
42        conclusion: impl Into<String>,
43        title: impl Into<String>,
44        summary: impl Into<String>,
45        mut annotations: Vec<CheckRunAnnotation>,
46    ) -> Self {
47        annotations.truncate(GITHUB_CHECK_ANNOTATION_LIMIT);
48        Self {
49            name: name.into(),
50            head_sha: head_sha.into(),
51            status: "completed".to_owned(),
52            conclusion: conclusion.into(),
53            output: CheckRunOutput {
54                title: title.into(),
55                summary: summary.into(),
56                annotations,
57            },
58        }
59    }
60
61    #[must_use]
62    pub fn annotations_count(&self) -> usize {
63        self.output.annotations.len()
64    }
65}
66
67#[must_use]
68pub fn render_sticky_comment(marker: &str, markdown: &str) -> String {
69    format!("{marker}\n\n{markdown}")
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn completed_check_payload_applies_the_provider_annotation_limit() {
78        let annotations = (0..55)
79            .map(|index| CheckRunAnnotation {
80                path: "bench.rs".to_owned(),
81                start_line: index + 1,
82                end_line: index + 1,
83                annotation_level: "failure".to_owned(),
84                message: format!("regression {index}"),
85                title: "Mobench regression".to_owned(),
86            })
87            .collect();
88        let request = CheckRunRequest::completed(
89            "mobench",
90            "abc123",
91            "failure",
92            "Regressions",
93            "summary",
94            annotations,
95        );
96
97        assert_eq!(request.status, "completed");
98        assert_eq!(request.annotations_count(), GITHUB_CHECK_ANNOTATION_LIMIT);
99        assert_eq!(request.output.annotations[49].start_line, 50);
100    }
101
102    #[test]
103    fn sticky_comment_preserves_the_released_marker_layout() {
104        assert_eq!(
105            render_sticky_comment("<!-- mobench -->", "### Results\n"),
106            "<!-- mobench -->\n\n### Results\n"
107        );
108    }
109}