fix(review): parse encoded JSON output

This commit is contained in:
Hunter Bown
2026-05-21 00:03:02 +08:00
parent eed4c2dfa6
commit 0e222ed0bd
+76 -2
View File
@@ -86,11 +86,11 @@ pub struct ReviewOutput {
impl ReviewOutput {
#[must_use]
pub fn from_str(raw: &str) -> Self {
if let Ok(parsed) = serde_json::from_str::<ReviewOutput>(raw) {
if let Some(parsed) = parse_review_output_json(raw) {
return parsed.normalize();
}
if let Some(json_block) = extract_json_block(raw)
&& let Ok(parsed) = serde_json::from_str::<ReviewOutput>(json_block)
&& let Some(parsed) = parse_review_output_json(json_block)
{
return parsed.normalize();
}
@@ -129,6 +129,20 @@ impl ReviewOutput {
}
}
fn parse_review_output_json(raw: &str) -> Option<ReviewOutput> {
if let Ok(parsed) = serde_json::from_str::<ReviewOutput>(raw) {
return Some(parsed);
}
let Value::String(inner) = serde_json::from_str::<Value>(raw).ok()? else {
return None;
};
if inner.trim().is_empty() || inner == raw {
return None;
}
parse_review_output_json(&inner)
}
pub struct ReviewTool {
client: Option<DeepSeekClient>,
model: String,
@@ -548,6 +562,66 @@ mod tests {
assert!(block.contains("\"summary\""));
}
#[test]
fn review_output_parses_structured_json() {
let raw = r#"{
"summary": " Looks good overall ",
"issues": [{
"severity": "high",
"title": " Missing test ",
"description": " Add coverage ",
"path": " src/lib.rs ",
"line": 42
}],
"suggestions": [{
"path": "",
"line": 7,
"suggestion": " Keep the helper small "
}],
"overall_assessment": " Safe after test "
}"#;
let output = ReviewOutput::from_str(raw);
assert_eq!(output.summary, "Looks good overall");
assert_eq!(output.issues.len(), 1);
assert_eq!(output.issues[0].severity, "error");
assert_eq!(output.issues[0].title, "Missing test");
assert_eq!(output.issues[0].path.as_deref(), Some("src/lib.rs"));
assert_eq!(output.issues[0].line, Some(42));
assert_eq!(output.suggestions.len(), 1);
assert_eq!(output.suggestions[0].path, None);
assert_eq!(output.suggestions[0].line, Some(7));
assert_eq!(output.suggestions[0].suggestion, "Keep the helper small");
assert_eq!(output.overall_assessment, "Safe after test");
}
#[test]
fn review_output_parses_double_encoded_json_string() {
let inner = serde_json::json!({
"summary": "structured",
"issues": [{
"severity": "warning",
"title": "Risk",
"description": "The parser should not fall back to a raw JSON string.",
"path": "src/main.rs",
"line": 3
}],
"suggestions": [],
"overall_assessment": "usable"
})
.to_string();
let double_encoded = serde_json::to_string(&inner).expect("encode string");
let output = ReviewOutput::from_str(&double_encoded);
assert_eq!(output.summary, "structured");
assert_eq!(output.issues.len(), 1);
assert_eq!(output.issues[0].severity, "warning");
assert_eq!(output.issues[0].path.as_deref(), Some("src/main.rs"));
assert_eq!(output.overall_assessment, "usable");
}
#[test]
fn review_output_fallback_keeps_summary() {
let output = ReviewOutput::from_str("Not JSON");