decodeFirestoreValue function

Object? decodeFirestoreValue(
  1. CborValue v
)

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();
}