dispatch.dart 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import 'dart:ffi';
  2. import 'package:dartz/dartz.dart';
  3. import 'package:flowy_sdk/log.dart';
  4. // ignore: unnecessary_import
  5. import 'package:flowy_sdk/protobuf/dart-ffi/ffi_response.pb.dart';
  6. import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
  7. import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
  8. import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
  9. import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
  10. import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
  11. import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
  12. import 'package:flowy_sdk/protobuf/flowy-net/network_state.pb.dart';
  13. import 'package:flowy_sdk/protobuf/flowy-user/event_map.pb.dart';
  14. import 'package:flowy_sdk/protobuf/flowy-folder/event_map.pb.dart';
  15. import 'package:flowy_sdk/protobuf/flowy-grid/event_map.pb.dart';
  16. import 'package:isolates/isolates.dart';
  17. import 'package:isolates/ports.dart';
  18. import 'package:ffi/ffi.dart';
  19. // ignore: unused_import
  20. import 'package:flutter/services.dart';
  21. import 'dart:async';
  22. import 'dart:typed_data';
  23. import 'package:flowy_sdk/ffi.dart' as ffi;
  24. import 'package:flowy_sdk/protobuf/flowy-user-data-model/protobuf.dart';
  25. import 'package:flowy_sdk/protobuf/dart-ffi/protobuf.dart';
  26. import 'package:flowy_sdk/protobuf/flowy-folder-data-model/protobuf.dart';
  27. import 'package:flowy_sdk/protobuf/flowy-text-block/protobuf.dart';
  28. import 'package:flowy_sdk/protobuf/flowy-grid-data-model/protobuf.dart';
  29. import 'package:flowy_sdk/protobuf/flowy-sync/protobuf.dart';
  30. // ignore: unused_import
  31. import 'package:protobuf/protobuf.dart';
  32. import 'dart:convert' show utf8;
  33. import '../protobuf/flowy-net/event_map.pb.dart';
  34. import 'error.dart';
  35. part 'dart_event/flowy-folder/dart_event.dart';
  36. part 'dart_event/flowy-net/dart_event.dart';
  37. part 'dart_event/flowy-user/dart_event.dart';
  38. part 'dart_event/flowy-grid/dart_event.dart';
  39. part 'dart_event/flowy-text-block/dart_event.dart';
  40. enum FFIException {
  41. RequestIsEmpty,
  42. }
  43. class DispatchException implements Exception {
  44. FFIException type;
  45. DispatchException(this.type);
  46. }
  47. class Dispatch {
  48. static Future<Either<Uint8List, Uint8List>> asyncRequest(FFIRequest request) {
  49. // FFIRequest => Rust SDK
  50. final bytesFuture = _sendToRust(request);
  51. // Rust SDK => FFIResponse
  52. final responseFuture = _extractResponse(bytesFuture);
  53. // FFIResponse's payload is the bytes of the Response object
  54. final payloadFuture = _extractPayload(responseFuture);
  55. return payloadFuture;
  56. }
  57. }
  58. Future<Either<Uint8List, Uint8List>> _extractPayload(Future<Either<FFIResponse, FlowyInternalError>> responseFuture) {
  59. return responseFuture.then((result) {
  60. return result.fold(
  61. (response) {
  62. switch (response.code) {
  63. case FFIStatusCode.Ok:
  64. return left(Uint8List.fromList(response.payload));
  65. case FFIStatusCode.Err:
  66. return right(Uint8List.fromList(response.payload));
  67. case FFIStatusCode.Internal:
  68. final error = utf8.decode(response.payload);
  69. Log.error("Dispatch internal error: $error");
  70. return right(emptyBytes());
  71. default:
  72. Log.error("Impossible to here");
  73. return right(emptyBytes());
  74. }
  75. },
  76. (error) {
  77. Log.error("Response should not be empty $error");
  78. return right(emptyBytes());
  79. },
  80. );
  81. });
  82. }
  83. Future<Either<FFIResponse, FlowyInternalError>> _extractResponse(Completer<Uint8List> bytesFuture) {
  84. return bytesFuture.future.then((bytes) {
  85. try {
  86. final response = FFIResponse.fromBuffer(bytes);
  87. return left(response);
  88. } catch (e, s) {
  89. final error = StackTraceError(e, s);
  90. Log.error('Deserialize response failed. ${error.toString()}');
  91. return right(error.asFlowyError());
  92. }
  93. });
  94. }
  95. Completer<Uint8List> _sendToRust(FFIRequest request) {
  96. Uint8List bytes = request.writeToBuffer();
  97. assert(bytes.isEmpty == false);
  98. if (bytes.isEmpty) {
  99. throw DispatchException(FFIException.RequestIsEmpty);
  100. }
  101. final Pointer<Uint8> input = calloc.allocate<Uint8>(bytes.length);
  102. final list = input.asTypedList(bytes.length);
  103. list.setAll(0, bytes);
  104. final completer = Completer<Uint8List>();
  105. final port = singleCompletePort(completer);
  106. ffi.async_event(port.nativePort, input, bytes.length);
  107. calloc.free(input);
  108. return completer;
  109. }
  110. Uint8List requestToBytes<T extends GeneratedMessage>(T? message) {
  111. try {
  112. if (message != null) {
  113. return message.writeToBuffer();
  114. } else {
  115. return emptyBytes();
  116. }
  117. } catch (e, s) {
  118. final error = StackTraceError(e, s);
  119. Log.error('Serial request failed. ${error.toString()}');
  120. return emptyBytes();
  121. }
  122. }
  123. Uint8List emptyBytes() {
  124. return Uint8List.fromList([]);
  125. }