codex_protocol/
review_format.rs1use crate::protocol::ReviewFinding;
2use crate::protocol::ReviewOutputEvent;
3
4fn format_location(item: &ReviewFinding) -> String {
7 let path = item.code_location.absolute_file_path.display();
8 let start = item.code_location.line_range.start;
9 let end = item.code_location.line_range.end;
10 format!("{path}:{start}-{end}")
11}
12
13pub const REVIEW_FALLBACK_MESSAGE: &str = "Reviewer failed to output a response.";
15
16pub fn format_review_findings_block(
24 findings: &[ReviewFinding],
25 selection: Option<&[bool]>,
26) -> String {
27 let mut lines: Vec<String> = Vec::new();
28 lines.push(String::new());
29
30 if findings.len() > 1 {
32 lines.push("Full review comments:".to_string());
33 } else {
34 lines.push("Review comment:".to_string());
35 }
36
37 for (idx, item) in findings.iter().enumerate() {
38 lines.push(String::new());
39
40 let title = &item.title;
41 let location = format_location(item);
42
43 if let Some(flags) = selection {
44 let checked = flags.get(idx).copied().unwrap_or(true);
46 let marker = if checked { "[x]" } else { "[ ]" };
47 lines.push(format!("- {marker} {title} — {location}"));
48 } else {
49 lines.push(format!("- {title} — {location}"));
50 }
51
52 for body_line in item.body.lines() {
53 lines.push(format!(" {body_line}"));
54 }
55 }
56
57 lines.join("\n")
58}
59
60pub fn render_review_output_text(output: &ReviewOutputEvent) -> String {
65 let mut sections = Vec::new();
66 let explanation = output.overall_explanation.trim();
67 if !explanation.is_empty() {
68 sections.push(explanation.to_string());
69 }
70 if !output.findings.is_empty() {
71 let findings = format_review_findings_block(&output.findings, None);
72 let trimmed = findings.trim();
73 if !trimmed.is_empty() {
74 sections.push(trimmed.to_string());
75 }
76 }
77 if sections.is_empty() {
78 REVIEW_FALLBACK_MESSAGE.to_string()
79 } else {
80 sections.join("\n\n")
81 }
82}