//! Markdown summary of a review, as posted on the pull request. use crate::domain::review::{ReviewItem, ReviewResult, ReviewSeverity}; pub(super) fn review_result_to_markdown(review_result: &ReviewResult) -> String { if review_result.reviews.is_empty() { return String::from("No issues found. ✅"); } let mut md = String::from("## Review Feedback\n\n"); md.push_str(&format!( "### {} issues found.\n\n", review_result.reviews.len() )); let breakdown = severity_breakdown(&review_result.reviews); if !breakdown.is_empty() { md.push_str(&breakdown); md.push('\n'); } if !review_result.comment.is_empty() { md.push_str("\n---\n\n"); md.push_str("### Summary\n\n"); md.push_str(&review_result.comment); md.push('\n'); } if let Some(cost) = review_result.cost { md.push_str("\n---\n\n"); md.push_str(&format!("### Cost: ${}", cost)); md.push('\n'); } md } /// Counts the reviews per severity, most severe first. /// /// A review whose severity the model omitted or wrote unreadably is counted as /// [`ReviewSeverity::Maintainability`], the same fallback used when posting it. fn severity_breakdown(reviews: &[ReviewItem]) -> String { [ ReviewSeverity::Bug, ReviewSeverity::Security, ReviewSeverity::Performance, ReviewSeverity::Maintainability, ] .into_iter() .filter_map(|severity| { let count = reviews .iter() .filter(|review| review.severity.unwrap_or(ReviewSeverity::Maintainability) == severity) .count(); (count > 0).then(|| format!("- {} {}", count, severity.label())) }) .collect::>() .join("\n") } #[cfg(test)] mod tests { use super::*; use crate::bot_actions::review::fixtures::*; #[test] fn the_breakdown_counts_severities_most_severe_first() { let reviews = vec![ ReviewItem { severity: Some(ReviewSeverity::Maintainability), ..review("a", Some(1), None) }, ReviewItem { severity: Some(ReviewSeverity::Bug), ..review("b", Some(2), None) }, ReviewItem { severity: Some(ReviewSeverity::Bug), ..review("c", Some(3), None) }, // A severity the model left out counts as maintainability. review("d", Some(4), None), ]; assert_eq!(severity_breakdown(&reviews), "- 2 bug\n- 2 maintainability"); } #[test] fn the_markdown_breaks_the_issues_down_by_severity() { let mut result = review_result(vec![ ReviewItem { severity: Some(ReviewSeverity::Bug), ..review("a", Some(1), None) }, ReviewItem { severity: Some(ReviewSeverity::Performance), ..review("b", Some(2), None) }, ]); result.comment = String::from("Le découpage en crates est propre."); let markdown = review_result_to_markdown(&result); assert!(markdown.contains("- 1 bug")); assert!(markdown.contains("- 1 performance")); assert!(markdown.contains("Le découpage en crates est propre.")); } }