dispatch.dart 2.9 KB

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