diff --git a/lib/src/robot/client.dart b/lib/src/robot/client.dart index 71fbbfa5147..a0d04b02c82 100644 --- a/lib/src/robot/client.dart +++ b/lib/src/robot/client.dart @@ -83,7 +83,7 @@ class Discovery { return Discovery( subtype: proto.query.subtype, model: proto.query.model, - results: proto.results.toMap(), + results: proto.results.toMap().unwrap(), ); } diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 5527458fcc8..4cba7b923ba 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -39,6 +39,84 @@ extension StructUtils on Struct { } } +extension UnwrapProtoMapUtils on Map { + /// Deeply unwraps a nested proto map structure by removing unnecessary proto wrappers and + /// converting all nested values to their primitive Dart types. + /// + /// Handles: + /// - Removes 'Kind' wrappers from proto Values + /// - Removes 'fields' wrappers from proto Structs + /// - Converts proto scalar types (String, Number, Bool, Null) + /// - Converts proto complex types (List, Struct) + /// - Preserves original structure minus wrappers + Map unwrap() { + final result = {}; + + // Core recursive function that processes each value in the structure + dynamic unwrapValue(dynamic value) { + // Handle maps - primary case for proto wrapper removal + if (value is Map) { + final mapValue = Map.from(value); + + // Two main wrapper types to handle: + + // 1. Kind wrapper (from proto Value) + if (mapValue.containsKey('Kind')) { + final kindValue = mapValue['Kind']; + if (kindValue is Map) { + final kindMap = Map.from(kindValue); + + // Scalar types are direct conversions - base cases + if (kindMap.containsKey('StringValue')) return kindMap['StringValue']; + if (kindMap.containsKey('NumberValue')) return kindMap['NumberValue']; + if (kindMap.containsKey('BoolValue')) return kindMap['BoolValue']; + if (kindMap.containsKey('NullValue')) return null; + + // Complex types need recursive handling - can be a base case when empty + if (kindMap.containsKey('ListValue')) { + final listValue = kindMap['ListValue']; + if (listValue is Map && listValue.containsKey('values')) { + return (listValue['values'] as List).map(unwrapValue).toList(); + } + return []; + } + + if (kindMap.containsKey('StructValue')) { + final structValue = kindMap['StructValue']; + if (structValue is Map && structValue.containsKey('fields')) { + return Map.from(structValue['fields']).unwrap(); + } + } + } + return value; + } + + // 2. Fields wrapper (from proto Struct) + if (mapValue.containsKey('fields')) { + return Map.from(mapValue['fields']).unwrap(); + } + + // Regular map - continue unwrapping all values + return mapValue.map((key, val) => MapEntry(key, unwrapValue(val))); + } + + // Handle lists - unwrap each element + if (value is List) { + return value.map(unwrapValue).toList(); + } + + return value; // base case: primitive value + } + + // Process each top-level key-value pair + forEach((key, value) { + result[key] = unwrapValue(value); + }); + + return result; + } +} + extension ListValueUtils on List { Value toValue() { final values = map((e) {