dispatch.dart 4.1 KB

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