MissionStep.fromJson constructor
MissionStep.fromJson(
- Map<String, dynamic> json
)
Implementation
factory MissionStep.fromJson(Map<String, dynamic> json) {
final rawParams = json['params'];
final params = rawParams is Map
? Map<String, dynamic>.from(rawParams)
: const <String, dynamic>{};
final rawBounds = json['bounds'];
final rawTokens = json['target_tokens'];
final rawAction = json['action'];
if (rawAction is! String || rawAction.trim().isEmpty) {
throw const FormatException('mission step has no string `action`');
}
// Every optional field is read TYPED: a wrong-typed value degrades to
// "no constraint" instead of throwing, so one malformed field cannot drop
// the whole frame (the backend would wait 25s for a terminal status).
String? str(String key) {
final v = json[key];
return v is String ? v : null;
}
final rawNth = json['nth_match'];
return MissionStep(
action: rawAction,
targetStableId: str('target_stable_id'),
targetIdentifier: str('target_identifier'),
targetText: str('target_text'),
targetRole: str('target_role'),
// The backend sends bounds as floats (`[216.0, 720.0, …]`); a lazy
// `cast<int>()` throws on first read, failing every mission.
bounds: rawBounds is List &&
rawBounds.length == 4 &&
rawBounds.every((e) => e is num && e.isFinite)
? rawBounds.map((e) => (e as num).round()).toList(growable: false)
: null,
kind: str('kind'),
targetTokens: rawTokens is List
? rawTokens.map((t) => t.toString()).toList(growable: false)
: null,
insideStableId: str('inside_stable_id'),
insideText: str('inside_text'),
insideRole: str('inside_role'),
forbidText: str('forbid_text'),
nthMatch: rawNth is num && rawNth.isFinite && rawNth == rawNth.floor()
? rawNth.toInt()
: null,
params: params,
);
}