dispatch.dart 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import 'dart:ffi';
  2. import 'package:dartz/dartz.dart';
  3. import 'package:flowy_sdk/protobuf/ffi_response.pb.dart';
  4. import 'package:isolates/isolates.dart';
  5. import 'package:isolates/ports.dart';
  6. import 'package:ffi/ffi.dart';
  7. // ignore: unused_import
  8. import 'package:flutter/services.dart';
  9. import 'dart:async';
  10. import 'dart:typed_data';
  11. import 'package:flowy_sdk/ffi/ffi.dart' as ffi;
  12. import 'package:flowy_sdk/protobuf.dart';
  13. import 'package:protobuf/protobuf.dart';
  14. import 'error.dart';
  15. part 'code_gen.dart';
  16. enum FFIException {
  17. RequestIsEmpty,
  18. }
  19. class DispatchException implements Exception {
  20. FFIException type;
  21. DispatchException(this.type);
  22. }
  23. class Dispatch {
  24. static Future<Either<Uint8List, FlowyError>> asyncRequest(
  25. 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, FlowyError>> _extractPayload(
  36. Future<Either<FFIResponse, FlowyError>> responseFuture) {
  37. return responseFuture.then((response) {
  38. return response.fold(
  39. (l) => left(Uint8List.fromList(l.payload)),
  40. (r) => right(r),
  41. );
  42. });
  43. }
  44. Future<Either<FFIResponse, FlowyError>> _extractResponse(
  45. Completer<Uint8List> bytesFuture) {
  46. return bytesFuture.future.then((bytes) {
  47. try {
  48. final response = FFIResponse.fromBuffer(bytes);
  49. if (response.code != FFIStatusCode.Ok) {
  50. return right(FlowyError.from(response));
  51. }
  52. return left(response);
  53. } catch (e, s) {
  54. return right(StackTraceError(e, s).toFlowyError());
  55. }
  56. });
  57. }
  58. Completer<Uint8List> _sendToRust(FFIRequest request) {
  59. Uint8List bytes = request.writeToBuffer();
  60. assert(bytes.isEmpty == false);
  61. if (bytes.isEmpty) {
  62. throw DispatchException(FFIException.RequestIsEmpty);
  63. }
  64. final Pointer<Uint8> input = calloc.allocate<Uint8>(bytes.length);
  65. final list = input.asTypedList(bytes.length);
  66. list.setAll(0, bytes);
  67. final completer = Completer<Uint8List>();
  68. final port = singleCompletePort(completer);
  69. ffi.async_command(port.nativePort, input, bytes.length);
  70. calloc.free(input);
  71. return completer;
  72. }
  73. Either<Uint8List, FlowyError> requestToBytes<T extends GeneratedMessage>(
  74. T? message) {
  75. try {
  76. if (message != null) {
  77. return left(message.writeToBuffer());
  78. } else {
  79. return left(Uint8List.fromList([]));
  80. }
  81. } catch (e, s) {
  82. return right(FlowyError.fromError('${e.runtimeType}. Stack trace: $s'));
  83. }
  84. }