fromJson static method
Reads the report JSON native sends across FFI/wasm, pairing it with the ChangeSet bytes sent alongside (null on failure). Never throws: text that is not the expected JSON shape becomes one error, as in RmlCompileError.listFromJson.
Implementation
static RmlCommitDiff fromJson(String report, Uint8List? changes) {
try {
final decoded = jsonDecode(report);
if (decoded is! Map<String, dynamic>) {
throw const FormatException('not a JSON object');
}
if (decoded.containsKey('errors')) {
final errors = RmlCompileError.listFromJson(
jsonEncode(decoded['errors']),
);
// An empty list would read as success.
if (errors.isEmpty) {
throw const FormatException('an error report with no errors');
}
final document = decoded['document'];
if (document != 'base' && document != 'sandbox') {
throw const FormatException('an error report with no document');
}
return RmlCommitDiff.failure(errors, baseFailed: document == 'base');
}
// A success report always carries all four fields; anything less is
// not the contract this reads, so it fails closed rather than
// silently substituting empty defaults.
if (!decoded.containsKey('idsUsed') ||
!decoded.containsKey('entries') ||
!decoded.containsKey('skipped') ||
!decoded.containsKey('orders')) {
throw const FormatException('missing a required success field');
}
final entries = (decoded['entries'] as List<dynamic>)
.cast<Map<String, dynamic>>()
.map(RmlCommitEntry.fromJson)
.toList();
if (changes == null) {
throw const FormatException('a success with no ChangeSet bytes');
}
return RmlCommitDiff.success(
changes: changes,
idsUsed: decoded['idsUsed'] as int,
entries: entries,
skipped: (decoded['skipped'] as List<dynamic>)
.cast<Map<String, dynamic>>()
.map(RmlCommitSkip.fromJson)
.toList(),
orders: (decoded['orders'] as List<dynamic>)
.cast<Map<String, dynamic>>()
.map(RmlOrderGroup.fromJson)
.toList(),
);
} catch (_) {
return RmlCommitDiff.failure([
RmlCompileError(line: 0, message: report.trim()),
]);
}
}