Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/scan.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,23 @@ function validateReview(review) {
}
return { errors };
}
// Scan backward from the last } to find its depth-matching { so that stray
// brace pairs in prose before the JSON block (e.g. `Foo{}`) are skipped.
// Return the JSON object that ends at the last }. Stray brace pairs in prose
// before the JSON block (e.g. `Foo{}`) must be skipped, but counting braces
// alone breaks on string values with unmatched braces (e.g. `impl Foo {`), so
// take the earliest { whose slice parses. If none does, fall back to the
// depth-matching { and let the caller report the parse error.
function extractJson(text) {
const lastBrace = text.lastIndexOf('}');
if (lastBrace === -1) return null;
for (let i = text.indexOf('{'); i !== -1 && i < lastBrace; i = text.indexOf('{', i + 1)) {
const candidate = text.slice(i, lastBrace + 1);
try {
JSON.parse(candidate);
return candidate;
} catch {
// not the start of the JSON block
}
}
let depth = 0;
for (let i = lastBrace; i >= 0; i--) {
if (text[i] === '}') depth++;
Expand Down
20 changes: 20 additions & 0 deletions tests/scan.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,3 +301,23 @@ test('extractJson - handles JSON with brace pairs inside string values', () => {
const json = '{"body":"use {} syntax","event":"COMMENT","comments":[]}';
assert.equal(extractJson(json), json);
});

test('extractJson - handles JSON with unmatched braces inside string values', () => {
for (const body of ['The block opened by `impl Foo {` is never closed.', 'Drop the extra `}`.']) {
const json = JSON.stringify({
body: 'Summary.',
event: 'COMMENT',
comments: [
{ path: 'src/a.rs', line: 10, side: 'RIGHT', body },
{ path: 'src/b.rs', line: 20, side: 'RIGHT', body: 'Second comment.' },
],
}, null, 2);
assert.equal(extractJson(json), json);
assert.equal(extractJson('Here is the review: {not json}\n```json\n' + json + '\n```'), json);
}
});

test('extractJson - falls back to the depth-matching brace when nothing parses', () => {
const broken = '{"body":"ok","event":"COMMENT","comments":[{"path":"a"},]}';
assert.equal(extractJson('Some {prose}. ' + broken), broken);
});