decodeFirestoreValue function
The inverse: CBOR back to Dart, resolving the tagged types.
Sentinels are rejected. Firestore does not return them, so one arriving here means the payload does not describe a document.
Implementation
Object? decodeFirestoreValue(CborValue v) {
final tag = v.tags.isEmpty ? null : v.tags.first;
switch (tag) {
case FirestoreTag.timestamp:
final a = (v as CborList).toObject()! as List<Object?>;
return FirestoreTimestamp((a[0]! as num).toInt(), (a[1]! as num).toInt());
case FirestoreTag.geoPoint:
final a = (v as CborList).toObject()! as List<Object?>;
return FirestoreGeoPoint(
(a[0]! as num).toDouble(),
(a[1]! as num).toDouble(),
);
case FirestoreTag.reference:
return FirestoreReference((v as CborString).toString());
case FirestoreTag.delete:
case FirestoreTag.serverTimestamp:
case FirestoreTag.arrayUnion:
case FirestoreTag.arrayRemove:
case FirestoreTag.incrementInt:
case FirestoreTag.incrementDouble:
throw FormatException(
'sentinel tag $tag in a document: sentinels are write-only, so this '
'payload is not a document Firestore produced',
);
}
// Before the generic conversion: toObject() renders a byte string as a plain
// List<int>, which is the one Firestore type that then cannot be told from an
// array of small integers. Encoding takes a Uint8List, so decoding returns
// one — the asymmetry was the bug.
if (v is CborBytes) return Uint8List.fromList(v.bytes);
if (v is CborList) return v.map(decodeFirestoreValue).toList();
if (v is CborMap) {
return <String, Object?>{
for (final e in v.entries)
e.key.toObject().toString(): decodeFirestoreValue(e.value),
};
}
return v.toObject();
}