dispatch.dart 3.8 KB

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