|
1 | 1 | import 'dart:convert'; |
| 2 | +import 'dart:typed_data'; |
2 | 3 |
|
3 | | -import 'message.dart'; |
| 4 | +import 'package:phoenix_socket/phoenix_socket.dart'; |
| 5 | +import 'package:phoenix_socket/src/utils/serializer.dart'; |
| 6 | +import 'package:phoenix_socket/src/utils/map_utils.dart'; |
4 | 7 |
|
5 | 8 | typedef DecoderCallback = dynamic Function(String rawData); |
6 | 9 | typedef EncoderCallback = String Function(Object? data); |
| 10 | +typedef PayloadDecoderCallback = dynamic Function(Uint8List payload); |
7 | 11 |
|
8 | 12 | /// Default class to serialize [Message] instances to JSON. |
9 | 13 | class MessageSerializer { |
| 14 | + static const int headerLength = 1; |
| 15 | + static const int metaLength = 4; |
| 16 | + |
| 17 | + static const Map<String, int> kinds = { |
| 18 | + 'push': 0, |
| 19 | + 'reply': 1, |
| 20 | + 'broadcast': 2, |
| 21 | + }; |
| 22 | + |
10 | 23 | final DecoderCallback _decoder; |
11 | 24 | final EncoderCallback _encoder; |
| 25 | + final PayloadDecoderCallback? _payloadDecoder; |
12 | 26 |
|
13 | 27 | /// Default constructor returning the singleton instance of this class. |
14 | 28 | const MessageSerializer({ |
15 | 29 | DecoderCallback decoder = jsonDecode, |
16 | 30 | EncoderCallback encoder = jsonEncode, |
| 31 | + PayloadDecoderCallback? payloadDecoder, |
17 | 32 | }) : _decoder = decoder, |
18 | | - _encoder = encoder; |
| 33 | + _encoder = encoder, |
| 34 | + _payloadDecoder = payloadDecoder; |
| 35 | + |
| 36 | + /// Encode a [Message] into a raw string or a Uint8List. |
| 37 | + /// |
| 38 | + /// If the message has a binary payload, it will be encoded using the |
| 39 | + /// [BinaryDecoder.binaryEncode] method. Otherwise, the message will be |
| 40 | + /// encoded using the [encoder] callback. |
| 41 | + /// Given a [Message], return the raw string that would be sent through |
| 42 | + /// a websocket. |
| 43 | + dynamic encode(Message message) { |
| 44 | + if (message.payload is Uint8List) { |
| 45 | + return BinaryDecoder.binaryEncode(message); |
| 46 | + } |
| 47 | + return _encoder(message.encode()); |
| 48 | + } |
19 | 49 |
|
20 | | - /// Yield a [Message] from some raw string arriving from a websocket. |
| 50 | + /// Decode a [Message] from a raw string or a Uint8List. |
| 51 | + /// |
| 52 | + /// If the message has a binary payload, it will be decoded using the |
| 53 | + /// [BinaryDecoder.binaryDecode] method. Otherwise, the message will be |
| 54 | + /// decoded using the [decoder] callback. |
21 | 55 | Message decode(dynamic rawData) { |
22 | | - if (rawData is String || rawData is List<int>) { |
| 56 | + if (rawData is String) { |
23 | 57 | return Message.fromJson(_decoder(rawData)); |
| 58 | + } else if (rawData is Uint8List) { |
| 59 | + final rawMap = BinaryDecoder.binaryDecode(rawData); |
| 60 | + return Message( |
| 61 | + joinRef: rawMap['join_ref'], |
| 62 | + ref: rawMap['ref'], |
| 63 | + topic: rawMap['topic'], |
| 64 | + event: PhoenixChannelEvent.custom(rawMap['event']), |
| 65 | + payload: _getPayload(rawMap['payload']), |
| 66 | + ); |
24 | 67 | } else { |
25 | 68 | throw ArgumentError('Received a non-string or a non-list of integers'); |
26 | 69 | } |
27 | 70 | } |
28 | 71 |
|
29 | | - /// Given a [Message], return the raw string that would be sent through |
30 | | - /// a websocket. |
31 | | - String encode(Message message) => _encoder(message.encode()); |
| 72 | + dynamic _getPayload(dynamic payLoad) { |
| 73 | + if (_payloadDecoder != null && payLoad is Uint8List) { |
| 74 | + final deserializedPayload = _payloadDecoder!(payLoad); |
| 75 | + if (deserializedPayload is Map) { |
| 76 | + return MapUtils.deepConvertToStringDynamic(deserializedPayload); |
| 77 | + } else if (deserializedPayload is Uint8List) { |
| 78 | + return deserializedPayload; |
| 79 | + } else { |
| 80 | + return {'data': deserializedPayload}; |
| 81 | + } |
| 82 | + } else { |
| 83 | + return payLoad; |
| 84 | + } |
| 85 | + } |
32 | 86 | } |
0 commit comments