Ver código fonte

test event from dart to rust using protobuf as bytes adaptor

appflowy 3 anos atrás
pai
commit
2cb26d9e90
44 arquivos alterados com 878 adições e 182 exclusões
  1. 3 2
      app_flowy/lib/welcome/presentation/welcome_screen.dart
  2. 14 0
      app_flowy/packages/flowy_logger/pubspec.lock
  3. 14 0
      app_flowy/packages/flowy_sdk/example/pubspec.lock
  4. 33 0
      app_flowy/packages/flowy_sdk/lib/dispatch/code_gen.dart
  5. 88 0
      app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart
  6. 35 0
      app_flowy/packages/flowy_sdk/lib/dispatch/flowy_error.dart
  7. 0 1
      app_flowy/packages/flowy_sdk/lib/ffi/adaptor.dart
  8. 20 3
      app_flowy/packages/flowy_sdk/lib/flowy_sdk.dart
  9. 1 0
      app_flowy/packages/flowy_sdk/lib/protobuf.dart
  10. 11 0
      app_flowy/packages/flowy_sdk/lib/protobuf/event.pb.dart
  11. 30 0
      app_flowy/packages/flowy_sdk/lib/protobuf/event.pbenum.dart
  12. 18 0
      app_flowy/packages/flowy_sdk/lib/protobuf/event.pbjson.dart
  13. 9 0
      app_flowy/packages/flowy_sdk/lib/protobuf/event.pbserver.dart
  14. 23 19
      app_flowy/packages/flowy_sdk/lib/protobuf/ffi_response.pb.dart
  15. 21 0
      app_flowy/packages/flowy_sdk/lib/protobuf/ffi_response.pbenum.dart
  16. 12 3
      app_flowy/packages/flowy_sdk/lib/protobuf/ffi_response.pbjson.dart
  17. 14 0
      app_flowy/packages/flowy_sdk/pubspec.lock
  18. 1 1
      app_flowy/pubspec.lock
  19. 15 28
      rust-lib/dart-ffi/src/lib.rs
  20. 7 2
      rust-lib/dart-ffi/src/model/ffi_request.rs
  21. 41 4
      rust-lib/dart-ffi/src/model/ffi_response.rs
  22. 112 61
      rust-lib/dart-ffi/src/protobuf/model/ffi_response.rs
  23. 8 3
      rust-lib/dart-ffi/src/protobuf/proto/ffi_response.proto
  24. 1 0
      rust-lib/dart-ffi/src/util.rs
  25. 3 0
      rust-lib/flowy-derive/src/derive_cache/derive_cache.rs
  26. 2 2
      rust-lib/flowy-derive/src/proto_buf/deserialize.rs
  27. 16 7
      rust-lib/flowy-derive/src/proto_buf/enum_serde.rs
  28. 5 5
      rust-lib/flowy-derive/src/proto_buf/serialize.rs
  29. 0 4
      rust-lib/flowy-sdk/src/lib.rs
  30. 17 11
      rust-lib/flowy-sys/src/dispatch.rs
  31. 0 4
      rust-lib/flowy-sys/tests/api/helper.rs
  32. 9 2
      rust-lib/flowy-sys/tests/api/module.rs
  33. 5 2
      rust-lib/flowy-test/src/lib.rs
  34. 27 0
      rust-lib/flowy-user/src/domain/event.rs
  35. 1 0
      rust-lib/flowy-user/src/domain/mod.rs
  36. 1 1
      rust-lib/flowy-user/src/lib.rs
  37. 1 15
      rust-lib/flowy-user/src/module.rs
  38. 108 0
      rust-lib/flowy-user/src/protobuf/model/event.rs
  39. 3 0
      rust-lib/flowy-user/src/protobuf/model/mod.rs
  40. 8 0
      rust-lib/flowy-user/src/protobuf/proto/event.proto
  41. 1 1
      rust-lib/flowy-user/tests/sign_in.rs
  42. 67 0
      scripts/flowy-tool/src/dart_event/event_template.rs
  43. 72 0
      scripts/flowy-tool/src/dart_event/event_template.tera
  44. 1 1
      scripts/flowy-tool/src/util/crate_config.rs

+ 3 - 2
app_flowy/lib/welcome/presentation/welcome_screen.dart

@@ -13,8 +13,9 @@ class WelcomeScreen extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     return BlocProvider(
-      create: (context) =>
-          getIt<WelcomeBloc>()..add(const WelcomeEvent.check()),
+      create: (context) {
+        return getIt<WelcomeBloc>()..add(const WelcomeEvent.check());
+      },
       child: Scaffold(
         body: BlocListener<WelcomeBloc, WelcomeState>(
           listener: (context, state) {

+ 14 - 0
app_flowy/packages/flowy_logger/pubspec.lock

@@ -55,11 +55,25 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_lints:
+    dependency: "direct dev"
+    description:
+      name: flutter_lints
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.3"
   flutter_test:
     dependency: "direct dev"
     description: flutter
     source: sdk
     version: "0.0.0"
+  lints:
+    dependency: transitive
+    description:
+      name: lints
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.1"
   logger:
     dependency: "direct main"
     description:

+ 14 - 0
app_flowy/packages/flowy_sdk/example/pubspec.lock

@@ -123,6 +123,13 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_lints:
+    dependency: "direct dev"
+    description:
+      name: flutter_lints
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.3"
   flutter_test:
     dependency: "direct dev"
     description: flutter
@@ -166,6 +173,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "4.0.1"
+  lints:
+    dependency: transitive
+    description:
+      name: lints
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.1"
   logger:
     dependency: transitive
     description:

+ 33 - 0
app_flowy/packages/flowy_sdk/lib/dispatch/code_gen.dart

@@ -0,0 +1,33 @@
+/// Auto gen code from rust ast, do not edit
+part of 'dispatch.dart';
+
+class UserEventSignIn {
+  UserSignInParams payload;
+
+  UserEventSignIn(this.payload);
+  Future<Either<UserSignInResult, FlowyError>> send() {
+    var request = FFIRequest.create()..event = UserEvent.SignIn.toString();
+    return protobufToBytes(payload).fold(
+      (payload) {
+        request.payload = payload;
+        return Dispatch.asyncRequest(request).then((response) {
+          try {
+            if (response.code != FFIStatusCode.Ok) {
+              return right(FlowyError.from(response));
+            } else {
+              final pb = UserSignInResult.fromBuffer(response.payload);
+              return left(pb);
+            }
+          } catch (e, s) {
+            final error =
+                FlowyError.fromError('${e.runtimeType}. Stack trace: $s');
+            return right(error);
+          }
+        });
+      },
+      (err) => Future(() {
+        return right(FlowyError.fromError(err));
+      }),
+    );
+  }
+}

+ 88 - 0
app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart

@@ -0,0 +1,88 @@
+import 'dart:ffi';
+import 'package:dartz/dartz.dart';
+import 'package:flowy_logger/flowy_logger.dart';
+import 'package:flowy_sdk/dispatch/flowy_error.dart';
+import 'package:flowy_sdk/protobuf/ffi_response.pb.dart';
+import 'package:isolates/isolates.dart';
+import 'package:isolates/ports.dart';
+import 'package:ffi/ffi.dart';
+// ignore: unused_import
+import 'package:flutter/services.dart';
+import 'dart:async';
+import 'dart:typed_data';
+import 'package:flowy_sdk/ffi/ffi.dart' as ffi;
+import 'package:flowy_sdk/protobuf.dart';
+import 'package:protobuf/protobuf.dart';
+
+part 'code_gen.dart';
+
+enum FFIException {
+  RequestIsEmpty,
+}
+
+class DispatchException implements Exception {
+  FFIException type;
+  DispatchException(this.type);
+}
+
+class Dispatch {
+  static Future<FFIResponse> asyncRequest(FFIRequest request) {
+    try {
+      return _asyncRequest(request).future.then((value) {
+        try {
+          final response = FFIResponse.fromBuffer(value);
+          return Future.microtask(() => response);
+        } catch (e, s) {
+          Log.error('FlowyFFI asyncRequest error: ${e.runtimeType}\n');
+          Log.error('Stack trace \n $s');
+          final response = error_response(request, "${e.runtimeType}");
+          return Future.microtask(() => response);
+        }
+      });
+    } catch (e, s) {
+      Log.error('FlowyFFI asyncRequest error: ${e.runtimeType}\n');
+      Log.error('Stack trace \n $s');
+      final response = error_response(request, "${e.runtimeType}");
+      return Future.microtask(() => response);
+    }
+  }
+}
+
+Completer<Uint8List> _asyncRequest(FFIRequest request) {
+  Uint8List bytes = request.writeToBuffer();
+  assert(bytes.isEmpty == false);
+  if (bytes.isEmpty) {
+    throw DispatchException(FFIException.RequestIsEmpty);
+  }
+
+  final Pointer<Uint8> input = calloc.allocate<Uint8>(bytes.length);
+  final list = input.asTypedList(bytes.length);
+  list.setAll(0, bytes);
+
+  final completer = Completer<Uint8List>();
+  final port = singleCompletePort(completer);
+  ffi.async_command(port.nativePort, input, bytes.length);
+  calloc.free(input);
+
+  return completer;
+}
+
+FFIResponse error_response(FFIRequest request, String message) {
+  var response = FFIResponse();
+  response.code = FFIStatusCode.Err;
+  response.error = "${request.event}: ${message}";
+  return response;
+}
+
+Either<Uint8List, String> protobufToBytes<T extends GeneratedMessage>(
+    T? message) {
+  try {
+    if (message != null) {
+      return left(message.writeToBuffer());
+    } else {
+      return left(Uint8List.fromList([]));
+    }
+  } catch (e, s) {
+    return right('FlowyFFI error: ${e.runtimeType}. Stack trace: $s');
+  }
+}

+ 35 - 0
app_flowy/packages/flowy_sdk/lib/dispatch/flowy_error.dart

@@ -0,0 +1,35 @@
+import '../protobuf/ffi_response.pb.dart';
+
+class FlowyError {
+  late FFIStatusCode _statusCode;
+  late String _error;
+
+  FFIStatusCode get statusCode {
+    return _statusCode;
+  }
+
+  String get error {
+    return _error;
+  }
+
+  bool get has_error {
+    return _statusCode != FFIStatusCode.Ok;
+  }
+
+  String toString() {
+    return "$_statusCode: $_error";
+  }
+
+  FlowyError({required FFIStatusCode statusCode, required String error}) {
+    _statusCode = statusCode;
+    _error = error;
+  }
+
+  factory FlowyError.from(FFIResponse resp) {
+    return FlowyError(statusCode: resp.code, error: resp.error);
+  }
+
+  factory FlowyError.fromError(String error) {
+    return FlowyError(statusCode: FFIStatusCode.Err, error: error);
+  }
+}

+ 0 - 1
app_flowy/packages/flowy_sdk/lib/ffi/adaptor.dart

@@ -5,7 +5,6 @@ import 'package:isolates/isolates.dart';
 // ignore: import_of_legacy_library_into_null_safe
 import 'package:isolates/ports.dart';
 import 'package:ffi/ffi.dart';
-
 // ignore: unused_import
 import 'package:flutter/services.dart';
 import 'dart:async';

+ 20 - 3
app_flowy/packages/flowy_sdk/lib/flowy_sdk.dart

@@ -2,12 +2,16 @@ export 'package:async/async.dart';
 
 import 'dart:io';
 import 'dart:async';
+import 'package:dartz/dartz.dart';
+import 'package:flowy_sdk/dispatch/flowy_error.dart';
 import 'package:flutter/services.dart';
 import 'dart:ffi';
-import 'ffi/adaptor.dart';
 import 'ffi/ffi.dart' as ffi;
 import 'package:ffi/ffi.dart';
 
+import 'package:flowy_sdk/protobuf.dart';
+import 'package:flowy_sdk/dispatch/dispatch.dart';
+
 class FlowySDK {
   static const MethodChannel _channel = MethodChannel('flowy_sdk');
   static Future<String> get platformVersion async {
@@ -23,7 +27,20 @@ class FlowySDK {
     ffi.store_dart_post_cobject(NativeApi.postCObject);
 
     ffi.init_sdk(sdkDir.path.toNativeUtf8());
-    final resp = await FFIAdaptor.asyncRequest();
-    print(resp);
+
+    final params = UserSignInParams.create();
+    params.email = "[email protected]";
+    params.password = "Helloworld!2";
+    Either<UserSignInResult, FlowyError> resp =
+        await UserEventSignIn(params).send();
+
+    resp.fold(
+      (result) {
+        print(result);
+      },
+      (error) {
+        print(error);
+      },
+    );
   }
 }

+ 1 - 0
app_flowy/packages/flowy_sdk/lib/protobuf.dart

@@ -3,4 +3,5 @@ export 'protobuf/ffi_response.pb.dart';
 export 'protobuf/ffi_request.pb.dart';
 export 'protobuf/sign_up.pb.dart';
 export 'protobuf/sign_in.pb.dart';
+export 'protobuf/event.pb.dart';
 export 'protobuf/user.pb.dart';

+ 11 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/event.pb.dart

@@ -0,0 +1,11 @@
+///
+//  Generated code. Do not modify.
+//  source: event.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+import 'dart:core' as $core;
+
+export 'event.pbenum.dart';
+

+ 30 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/event.pbenum.dart

@@ -0,0 +1,30 @@
+///
+//  Generated code. Do not modify.
+//  source: event.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+// ignore_for_file: UNDEFINED_SHOWN_NAME
+import 'dart:core' as $core;
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class UserEvent extends $pb.ProtobufEnum {
+  static const UserEvent AuthCheck = UserEvent._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AuthCheck');
+  static const UserEvent SignIn = UserEvent._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SignIn');
+  static const UserEvent SignUp = UserEvent._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SignUp');
+  static const UserEvent SignOut = UserEvent._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SignOut');
+
+  static const $core.List<UserEvent> values = <UserEvent> [
+    AuthCheck,
+    SignIn,
+    SignUp,
+    SignOut,
+  ];
+
+  static final $core.Map<$core.int, UserEvent> _byValue = $pb.ProtobufEnum.initByValue(values);
+  static UserEvent? valueOf($core.int value) => _byValue[value];
+
+  const UserEvent._($core.int v, $core.String n) : super(v, n);
+}
+

+ 18 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/event.pbjson.dart

@@ -0,0 +1,18 @@
+///
+//  Generated code. Do not modify.
+//  source: event.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+import 'dart:core' as $core;
+const UserEvent$json = const {
+  '1': 'UserEvent',
+  '2': const [
+    const {'1': 'AuthCheck', '2': 0},
+    const {'1': 'SignIn', '2': 1},
+    const {'1': 'SignUp', '2': 2},
+    const {'1': 'SignOut', '2': 3},
+  ],
+};
+

+ 9 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/event.pbserver.dart

@@ -0,0 +1,9 @@
+///
+//  Generated code. Do not modify.
+//  source: event.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+export 'event.pb.dart';
+

+ 23 - 19
app_flowy/packages/flowy_sdk/lib/protobuf/ffi_response.pb.dart

@@ -9,30 +9,34 @@ import 'dart:core' as $core;
 
 import 'package:protobuf/protobuf.dart' as $pb;
 
+import 'ffi_response.pbenum.dart';
+
+export 'ffi_response.pbenum.dart';
+
 class FFIResponse extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'FFIResponse', createEmptyInstance: create)
-    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'event')
-    ..a<$core.List<$core.int>>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'payload', $pb.PbFieldType.OY)
-    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'error')
+    ..a<$core.List<$core.int>>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'payload', $pb.PbFieldType.OY)
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'error')
+    ..e<FFIStatusCode>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'code', $pb.PbFieldType.OE, defaultOrMaker: FFIStatusCode.Unknown, valueOf: FFIStatusCode.valueOf, enumValues: FFIStatusCode.values)
     ..hasRequiredFields = false
   ;
 
   FFIResponse._() : super();
   factory FFIResponse({
-    $core.String? event,
     $core.List<$core.int>? payload,
     $core.String? error,
+    FFIStatusCode? code,
   }) {
     final _result = create();
-    if (event != null) {
-      _result.event = event;
-    }
     if (payload != null) {
       _result.payload = payload;
     }
     if (error != null) {
       _result.error = error;
     }
+    if (code != null) {
+      _result.code = code;
+    }
     return _result;
   }
   factory FFIResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
@@ -57,30 +61,30 @@ class FFIResponse extends $pb.GeneratedMessage {
   static FFIResponse? _defaultInstance;
 
   @$pb.TagNumber(1)
-  $core.String get event => $_getSZ(0);
+  $core.List<$core.int> get payload => $_getN(0);
   @$pb.TagNumber(1)
-  set event($core.String v) { $_setString(0, v); }
+  set payload($core.List<$core.int> v) { $_setBytes(0, v); }
   @$pb.TagNumber(1)
-  $core.bool hasEvent() => $_has(0);
+  $core.bool hasPayload() => $_has(0);
   @$pb.TagNumber(1)
-  void clearEvent() => clearField(1);
+  void clearPayload() => clearField(1);
 
   @$pb.TagNumber(2)
-  $core.List<$core.int> get payload => $_getN(1);
+  $core.String get error => $_getSZ(1);
   @$pb.TagNumber(2)
-  set payload($core.List<$core.int> v) { $_setBytes(1, v); }
+  set error($core.String v) { $_setString(1, v); }
   @$pb.TagNumber(2)
-  $core.bool hasPayload() => $_has(1);
+  $core.bool hasError() => $_has(1);
   @$pb.TagNumber(2)
-  void clearPayload() => clearField(2);
+  void clearError() => clearField(2);
 
   @$pb.TagNumber(3)
-  $core.String get error => $_getSZ(2);
+  FFIStatusCode get code => $_getN(2);
   @$pb.TagNumber(3)
-  set error($core.String v) { $_setString(2, v); }
+  set code(FFIStatusCode v) { setField(3, v); }
   @$pb.TagNumber(3)
-  $core.bool hasError() => $_has(2);
+  $core.bool hasCode() => $_has(2);
   @$pb.TagNumber(3)
-  void clearError() => clearField(3);
+  void clearCode() => clearField(3);
 }
 

+ 21 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/ffi_response.pbenum.dart

@@ -5,3 +5,24 @@
 // @dart = 2.12
 // ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
 
+// ignore_for_file: UNDEFINED_SHOWN_NAME
+import 'dart:core' as $core;
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class FFIStatusCode extends $pb.ProtobufEnum {
+  static const FFIStatusCode Unknown = FFIStatusCode._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Unknown');
+  static const FFIStatusCode Ok = FFIStatusCode._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Ok');
+  static const FFIStatusCode Err = FFIStatusCode._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Err');
+
+  static const $core.List<FFIStatusCode> values = <FFIStatusCode> [
+    Unknown,
+    Ok,
+    Err,
+  ];
+
+  static final $core.Map<$core.int, FFIStatusCode> _byValue = $pb.ProtobufEnum.initByValue(values);
+  static FFIStatusCode? valueOf($core.int value) => _byValue[value];
+
+  const FFIStatusCode._($core.int v, $core.String n) : super(v, n);
+}
+

+ 12 - 3
app_flowy/packages/flowy_sdk/lib/protobuf/ffi_response.pbjson.dart

@@ -6,12 +6,21 @@
 // ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
 
 import 'dart:core' as $core;
+const FFIStatusCode$json = const {
+  '1': 'FFIStatusCode',
+  '2': const [
+    const {'1': 'Unknown', '2': 0},
+    const {'1': 'Ok', '2': 1},
+    const {'1': 'Err', '2': 2},
+  ],
+};
+
 const FFIResponse$json = const {
   '1': 'FFIResponse',
   '2': const [
-    const {'1': 'event', '3': 1, '4': 1, '5': 9, '10': 'event'},
-    const {'1': 'payload', '3': 2, '4': 1, '5': 12, '10': 'payload'},
-    const {'1': 'error', '3': 3, '4': 1, '5': 9, '10': 'error'},
+    const {'1': 'payload', '3': 1, '4': 1, '5': 12, '10': 'payload'},
+    const {'1': 'error', '3': 2, '4': 1, '5': 9, '10': 'error'},
+    const {'1': 'code', '3': 3, '4': 1, '5': 14, '6': '.FFIStatusCode', '10': 'code'},
   ],
 };
 

+ 14 - 0
app_flowy/packages/flowy_sdk/pubspec.lock

@@ -209,6 +209,13 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_lints:
+    dependency: "direct dev"
+    description:
+      name: flutter_lints
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.3"
   flutter_test:
     dependency: "direct dev"
     description: flutter
@@ -284,6 +291,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "4.0.1"
+  lints:
+    dependency: transitive
+    description:
+      name: lints
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.1"
   logger:
     dependency: transitive
     description:

+ 1 - 1
app_flowy/pubspec.lock

@@ -259,7 +259,7 @@ packages:
     source: hosted
     version: "7.0.1"
   flutter_lints:
-    dependency: "direct main"
+    dependency: "direct dev"
     description:
       name: flutter_lints
       url: "https://pub.dartlang.org"

+ 15 - 28
rust-lib/dart-ffi/src/lib.rs

@@ -1,10 +1,11 @@
 mod c;
 mod model;
 mod protobuf;
+mod util;
 
 use crate::{
     c::{extend_front_four_bytes_into_bytes, forget_rust},
-    protobuf::FFIRequest,
+    model::{FFIRequest, FFIResponse},
 };
 use flowy_sdk::*;
 use flowy_sys::prelude::*;
@@ -30,7 +31,7 @@ pub extern "C" fn init_sdk(path: *mut c_char) -> i64 {
 
 #[no_mangle]
 pub extern "C" fn async_command(port: i64, input: *const u8, len: usize) {
-    let mut request: DispatchRequest = FFIRequest::from_u8_pointer(input, len).into();
+    let request: DispatchRequest = FFIRequest::from_u8_pointer(input, len).into();
     log::trace!(
         "[FFI]: {} Async Event: {:?} with {} port",
         &request.id,
@@ -38,16 +39,10 @@ pub extern "C" fn async_command(port: i64, input: *const u8, len: usize) {
         port
     );
 
-    request = request.callback(Box::new(move |resp: EventResponse| {
-        let bytes = match resp.payload {
-            Payload::Bytes(bytes) => bytes,
-            Payload::None => vec![],
-        };
+    let _ = EventDispatch::async_send(request, move |resp: EventResponse| {
         log::trace!("[FFI]: Post data to dart through {} port", port);
-        Box::pin(spawn_future(async { bytes }, port))
-    }));
-
-    let _ = EventDispatch::async_send(request);
+        Box::pin(post_to_flutter(resp, port))
+    });
 }
 
 #[no_mangle]
@@ -66,13 +61,17 @@ pub extern "C" fn sync_command(input: *const u8, len: usize) -> *const u8 {
 #[no_mangle]
 pub extern "C" fn link_me_please() {}
 
+use flowy_sys::prelude::ToBytes;
 #[inline(always)]
-async fn spawn_future<F>(future: F, port: i64)
-where
-    F: Future<Output = Vec<u8>> + Send + 'static,
-{
+async fn post_to_flutter(response: EventResponse, port: i64) {
     let isolate = allo_isolate::Isolate::new(port);
-    match isolate.catch_unwind(future).await {
+    match isolate
+        .catch_unwind(async {
+            let ffi_resp = FFIResponse::from(response);
+            ffi_resp.into_bytes().unwrap()
+        })
+        .await
+    {
         Ok(_success) => {
             log::trace!("[FFI]: Post data to dart success");
         },
@@ -85,15 +84,3 @@ where
         },
     }
 }
-
-impl std::convert::From<FFIRequest> for DispatchRequest {
-    fn from(ffi_request: FFIRequest) -> Self {
-        let payload = if !ffi_request.payload.is_empty() {
-            Payload::Bytes(ffi_request.payload)
-        } else {
-            Payload::None
-        };
-        let request = DispatchRequest::new(ffi_request.event).payload(payload);
-        request
-    }
-}

+ 7 - 2
rust-lib/dart-ffi/src/model/ffi_request.rs

@@ -1,13 +1,14 @@
 use flowy_derive::ProtoBuf;
+use flowy_sys::prelude::DispatchRequest;
 use std::convert::TryFrom;
 
 #[derive(Default, ProtoBuf)]
 pub struct FFIRequest {
     #[pb(index = 1)]
-    event: String,
+    pub(crate) event: String,
 
     #[pb(index = 2)]
-    payload: Vec<u8>,
+    pub(crate) payload: Vec<u8>,
 }
 
 impl FFIRequest {
@@ -17,3 +18,7 @@ impl FFIRequest {
         request
     }
 }
+
+impl std::convert::Into<DispatchRequest> for FFIRequest {
+    fn into(self) -> DispatchRequest { DispatchRequest::new(self.event).payload(self.payload) }
+}

+ 41 - 4
rust-lib/dart-ffi/src/model/ffi_response.rs

@@ -1,13 +1,50 @@
-use flowy_derive::ProtoBuf;
+use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
+use flowy_sys::prelude::{EventResponse, Payload, StatusCode};
+
+#[derive(ProtoBuf_Enum, Clone, Copy)]
+pub enum FFIStatusCode {
+    Unknown = 0,
+    Ok      = 1,
+    Err     = 2,
+}
+
+impl std::default::Default for FFIStatusCode {
+    fn default() -> FFIStatusCode { FFIStatusCode::Unknown }
+}
 
 #[derive(ProtoBuf, Default)]
 pub struct FFIResponse {
     #[pb(index = 1)]
-    event: String,
+    payload: Vec<u8>,
 
     #[pb(index = 2)]
-    payload: Vec<u8>,
+    error: String,
 
     #[pb(index = 3)]
-    error: String,
+    code: FFIStatusCode,
+}
+
+impl std::convert::From<EventResponse> for FFIResponse {
+    fn from(resp: EventResponse) -> Self {
+        let payload = match resp.payload {
+            Payload::Bytes(bytes) => bytes,
+            Payload::None => vec![],
+        };
+
+        let error = match resp.error {
+            Some(e) => format!("{}", e),
+            None => "".to_owned(),
+        };
+
+        let code = match resp.status_code {
+            StatusCode::Ok => FFIStatusCode::Ok,
+            StatusCode::Err => FFIStatusCode::Err,
+        };
+
+        FFIResponse {
+            payload,
+            error,
+            code,
+        }
+    }
 }

+ 112 - 61
rust-lib/dart-ffi/src/protobuf/model/ffi_response.rs

@@ -26,9 +26,9 @@
 #[derive(PartialEq,Clone,Default)]
 pub struct FFIResponse {
     // message fields
-    pub event: ::std::string::String,
     pub payload: ::std::vec::Vec<u8>,
     pub error: ::std::string::String,
+    pub code: FFIStatusCode,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -45,33 +45,7 @@ impl FFIResponse {
         ::std::default::Default::default()
     }
 
-    // string event = 1;
-
-
-    pub fn get_event(&self) -> &str {
-        &self.event
-    }
-    pub fn clear_event(&mut self) {
-        self.event.clear();
-    }
-
-    // Param is passed by value, moved
-    pub fn set_event(&mut self, v: ::std::string::String) {
-        self.event = v;
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_event(&mut self) -> &mut ::std::string::String {
-        &mut self.event
-    }
-
-    // Take field
-    pub fn take_event(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.event, ::std::string::String::new())
-    }
-
-    // bytes payload = 2;
+    // bytes payload = 1;
 
 
     pub fn get_payload(&self) -> &[u8] {
@@ -97,7 +71,7 @@ impl FFIResponse {
         ::std::mem::replace(&mut self.payload, ::std::vec::Vec::new())
     }
 
-    // string error = 3;
+    // string error = 2;
 
 
     pub fn get_error(&self) -> &str {
@@ -122,6 +96,21 @@ impl FFIResponse {
     pub fn take_error(&mut self) -> ::std::string::String {
         ::std::mem::replace(&mut self.error, ::std::string::String::new())
     }
+
+    // .FFIStatusCode code = 3;
+
+
+    pub fn get_code(&self) -> FFIStatusCode {
+        self.code
+    }
+    pub fn clear_code(&mut self) {
+        self.code = FFIStatusCode::Unknown;
+    }
+
+    // Param is passed by value, moved
+    pub fn set_code(&mut self, v: FFIStatusCode) {
+        self.code = v;
+    }
 }
 
 impl ::protobuf::Message for FFIResponse {
@@ -134,13 +123,13 @@ impl ::protobuf::Message for FFIResponse {
             let (field_number, wire_type) = is.read_tag_unpack()?;
             match field_number {
                 1 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.event)?;
+                    ::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.payload)?;
                 },
                 2 => {
-                    ::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.payload)?;
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.error)?;
                 },
                 3 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.error)?;
+                    ::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.code, 3, &mut self.unknown_fields)?
                 },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@@ -154,14 +143,14 @@ impl ::protobuf::Message for FFIResponse {
     #[allow(unused_variables)]
     fn compute_size(&self) -> u32 {
         let mut my_size = 0;
-        if !self.event.is_empty() {
-            my_size += ::protobuf::rt::string_size(1, &self.event);
-        }
         if !self.payload.is_empty() {
-            my_size += ::protobuf::rt::bytes_size(2, &self.payload);
+            my_size += ::protobuf::rt::bytes_size(1, &self.payload);
         }
         if !self.error.is_empty() {
-            my_size += ::protobuf::rt::string_size(3, &self.error);
+            my_size += ::protobuf::rt::string_size(2, &self.error);
+        }
+        if self.code != FFIStatusCode::Unknown {
+            my_size += ::protobuf::rt::enum_size(3, self.code);
         }
         my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
         self.cached_size.set(my_size);
@@ -169,14 +158,14 @@ impl ::protobuf::Message for FFIResponse {
     }
 
     fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        if !self.event.is_empty() {
-            os.write_string(1, &self.event)?;
-        }
         if !self.payload.is_empty() {
-            os.write_bytes(2, &self.payload)?;
+            os.write_bytes(1, &self.payload)?;
         }
         if !self.error.is_empty() {
-            os.write_string(3, &self.error)?;
+            os.write_string(2, &self.error)?;
+        }
+        if self.code != FFIStatusCode::Unknown {
+            os.write_enum(3, ::protobuf::ProtobufEnum::value(&self.code))?;
         }
         os.write_unknown_fields(self.get_unknown_fields())?;
         ::std::result::Result::Ok(())
@@ -216,11 +205,6 @@ impl ::protobuf::Message for FFIResponse {
         static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
         descriptor.get(|| {
             let mut fields = ::std::vec::Vec::new();
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
-                "event",
-                |m: &FFIResponse| { &m.event },
-                |m: &mut FFIResponse| { &mut m.event },
-            ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>(
                 "payload",
                 |m: &FFIResponse| { &m.payload },
@@ -231,6 +215,11 @@ impl ::protobuf::Message for FFIResponse {
                 |m: &FFIResponse| { &m.error },
                 |m: &mut FFIResponse| { &mut m.error },
             ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<FFIStatusCode>>(
+                "code",
+                |m: &FFIResponse| { &m.code },
+                |m: &mut FFIResponse| { &mut m.code },
+            ));
             ::protobuf::reflect::MessageDescriptor::new_pb_name::<FFIResponse>(
                 "FFIResponse",
                 fields,
@@ -247,9 +236,9 @@ impl ::protobuf::Message for FFIResponse {
 
 impl ::protobuf::Clear for FFIResponse {
     fn clear(&mut self) {
-        self.event.clear();
         self.payload.clear();
         self.error.clear();
+        self.code = FFIStatusCode::Unknown;
         self.unknown_fields.clear();
     }
 }
@@ -266,20 +255,82 @@ impl ::protobuf::reflect::ProtobufValue for FFIResponse {
     }
 }
 
+#[derive(Clone,PartialEq,Eq,Debug,Hash)]
+pub enum FFIStatusCode {
+    Unknown = 0,
+    Ok = 1,
+    Err = 2,
+}
+
+impl ::protobuf::ProtobufEnum for FFIStatusCode {
+    fn value(&self) -> i32 {
+        *self as i32
+    }
+
+    fn from_i32(value: i32) -> ::std::option::Option<FFIStatusCode> {
+        match value {
+            0 => ::std::option::Option::Some(FFIStatusCode::Unknown),
+            1 => ::std::option::Option::Some(FFIStatusCode::Ok),
+            2 => ::std::option::Option::Some(FFIStatusCode::Err),
+            _ => ::std::option::Option::None
+        }
+    }
+
+    fn values() -> &'static [Self] {
+        static values: &'static [FFIStatusCode] = &[
+            FFIStatusCode::Unknown,
+            FFIStatusCode::Ok,
+            FFIStatusCode::Err,
+        ];
+        values
+    }
+
+    fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            ::protobuf::reflect::EnumDescriptor::new_pb_name::<FFIStatusCode>("FFIStatusCode", file_descriptor_proto())
+        })
+    }
+}
+
+impl ::std::marker::Copy for FFIStatusCode {
+}
+
+impl ::std::default::Default for FFIStatusCode {
+    fn default() -> Self {
+        FFIStatusCode::Unknown
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for FFIStatusCode {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
+    }
+}
+
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x12ffi_response.proto\"S\n\x0bFFIResponse\x12\x14\n\x05event\x18\x01\
-    \x20\x01(\tR\x05event\x12\x18\n\x07payload\x18\x02\x20\x01(\x0cR\x07payl\
-    oad\x12\x14\n\x05error\x18\x03\x20\x01(\tR\x05errorJ\xcf\x01\n\x06\x12\
-    \x04\0\0\x06\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\
-    \x02\0\x06\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x13\n\x0b\n\x04\x04\0\
-    \x02\0\x12\x03\x03\x04\x15\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\x03\x04\n\
-    \n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x0b\x10\n\x0c\n\x05\x04\0\x02\0\
-    \x03\x12\x03\x03\x13\x14\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\x16\n\
-    \x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x04\x04\t\n\x0c\n\x05\x04\0\x02\x01\
-    \x01\x12\x03\x04\n\x11\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x14\x15\
-    \n\x0b\n\x04\x04\0\x02\x02\x12\x03\x05\x04\x15\n\x0c\n\x05\x04\0\x02\x02\
-    \x05\x12\x03\x05\x04\n\n\x0c\n\x05\x04\0\x02\x02\x01\x12\x03\x05\x0b\x10\
-    \n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\x05\x13\x14b\x06proto3\
+    \n\x12ffi_response.proto\"a\n\x0bFFIResponse\x12\x18\n\x07payload\x18\
+    \x01\x20\x01(\x0cR\x07payload\x12\x14\n\x05error\x18\x02\x20\x01(\tR\x05\
+    error\x12\"\n\x04code\x18\x03\x20\x01(\x0e2\x0e.FFIStatusCodeR\x04code*-\
+    \n\rFFIStatusCode\x12\x0b\n\x07Unknown\x10\0\x12\x06\n\x02Ok\x10\x01\x12\
+    \x07\n\x03Err\x10\x02J\xe2\x02\n\x06\x12\x04\0\0\x0b\x01\n\x08\n\x01\x0c\
+    \x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x06\x01\n\n\n\x03\x04\0\
+    \x01\x12\x03\x02\x08\x13\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x16\n\
+    \x0c\n\x05\x04\0\x02\0\x05\x12\x03\x03\x04\t\n\x0c\n\x05\x04\0\x02\0\x01\
+    \x12\x03\x03\n\x11\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x14\x15\n\x0b\
+    \n\x04\x04\0\x02\x01\x12\x03\x04\x04\x15\n\x0c\n\x05\x04\0\x02\x01\x05\
+    \x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\x0b\x10\n\
+    \x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x13\x14\n\x0b\n\x04\x04\0\x02\
+    \x02\x12\x03\x05\x04\x1b\n\x0c\n\x05\x04\0\x02\x02\x06\x12\x03\x05\x04\
+    \x11\n\x0c\n\x05\x04\0\x02\x02\x01\x12\x03\x05\x12\x16\n\x0c\n\x05\x04\0\
+    \x02\x02\x03\x12\x03\x05\x19\x1a\n\n\n\x02\x05\0\x12\x04\x07\0\x0b\x01\n\
+    \n\n\x03\x05\0\x01\x12\x03\x07\x05\x12\n\x0b\n\x04\x05\0\x02\0\x12\x03\
+    \x08\x04\x10\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x08\x04\x0b\n\x0c\n\x05\
+    \x05\0\x02\0\x02\x12\x03\x08\x0e\x0f\n\x0b\n\x04\x05\0\x02\x01\x12\x03\t\
+    \x04\x0b\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\t\x04\x06\n\x0c\n\x05\x05\
+    \0\x02\x01\x02\x12\x03\t\t\n\n\x0b\n\x04\x05\0\x02\x02\x12\x03\n\x04\x0c\
+    \n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\n\x04\x07\n\x0c\n\x05\x05\0\x02\
+    \x02\x02\x12\x03\n\n\x0bb\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 8 - 3
rust-lib/dart-ffi/src/protobuf/proto/ffi_response.proto

@@ -1,7 +1,12 @@
 syntax = "proto3";
 
 message FFIResponse {
-    string event = 1;
-    bytes payload = 2;
-    string error = 3;
+    bytes payload = 1;
+    string error = 2;
+    FFIStatusCode code = 3;
+}
+enum FFIStatusCode {
+    Unknown = 0;
+    Ok = 1;
+    Err = 2;
 }

+ 1 - 0
rust-lib/dart-ffi/src/util.rs

@@ -0,0 +1 @@
+

+ 3 - 0
rust-lib/flowy-derive/src/derive_cache/derive_cache.rs

@@ -25,6 +25,9 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
         | "UserSignInRequest"
         | "UserSignInResult"
         => TypeCategory::Protobuf,
+        "FFIStatusCode"
+        | "UserEvent"
+        => TypeCategory::Enum,
 
         "Option" => TypeCategory::Opt,
         _ => TypeCategory::Primitive,

+ 2 - 2
rust-lib/flowy-derive/src/proto_buf/deserialize.rs

@@ -60,7 +60,7 @@ fn token_stream_for_one_of(ctxt: &Ctxt, field: &ASTField) -> Option<TokenStream>
             let ty = ty_info.ty;
             Some(quote! {
                 if pb.#has_func() {
-                    let enum_de_from_pb = #ty::try_from(&mut pb.#get_func()).unwrap();
+                    let enum_de_from_pb = #ty::try_from(&pb.#get_func()).unwrap();
                     o.#member = Some(enum_de_from_pb);
                 }
             })
@@ -122,7 +122,7 @@ fn token_stream_for_field(
         TypeCategory::Enum => {
             let ty = ty_info.ty;
             Some(quote! {
-                let enum_de_from_pb = #ty::try_from(&mut pb.#member).unwrap();
+                let enum_de_from_pb = #ty::try_from(&pb.#member).unwrap();
                  o.#member = enum_de_from_pb;
 
             })

+ 16 - 7
rust-lib/flowy-derive/src/proto_buf/enum_serde.rs

@@ -5,27 +5,36 @@ use proc_macro2::TokenStream;
 pub fn make_enum_token_stream(_ctxt: &Ctxt, cont: &ASTContainer) -> Option<TokenStream> {
     let enum_ident = &cont.ident;
     let pb_enum = cont.attrs.pb_enum_type()?;
-    let _build_to_pb_enum = cont.data.all_idents().map(|i| {
+    let build_to_pb_enum = cont.data.all_idents().map(|i| {
         let token_stream: TokenStream = quote! {
-            #enum_ident::#i => #pb_enum::#i,
+            #enum_ident::#i => crate::protobuf::#pb_enum::#i,
         };
         token_stream
     });
 
     let build_from_pb_enum = cont.data.all_idents().map(|i| {
         let token_stream: TokenStream = quote! {
-            #pb_enum::#i => #enum_ident::#i,
+            crate::protobuf::#pb_enum::#i => #enum_ident::#i,
         };
         token_stream
     });
 
     Some(quote! {
-        impl std::convert::TryFrom<#pb_enum> for #enum_ident {
+        impl std::convert::TryFrom<&crate::protobuf::#pb_enum> for #enum_ident {
             type Error = String;
-            fn try_from(pb: #pb_enum) -> Result<Self, Self::Error> {
-                match field_type {
+            fn try_from(pb:&crate::protobuf::#pb_enum) -> Result<Self, Self::Error> {
+                Ok(match pb {
                     #(#build_from_pb_enum)*
-                }
+                })
+            }
+        }
+
+        impl std::convert::TryInto<crate::protobuf::#pb_enum> for #enum_ident {
+            type Error = String;
+            fn try_into(self) -> Result<crate::protobuf::#pb_enum, Self::Error> {
+                Ok(match self {
+                    #(#build_to_pb_enum)*
+                })
             }
         }
     })

+ 5 - 5
rust-lib/flowy-derive/src/proto_buf/serialize.rs

@@ -58,7 +58,7 @@ fn se_token_stream_for_field(ctxt: &Ctxt, field: &ASTField, _take: bool) -> Opti
                 let set_func = format_ident!("set_{}", ident.to_string());
                 Some(quote! {
                     match self.#member {
-                        Some(ref s) => { pb.#set_func(s.to_protobuf()) }
+                        Some(ref s) => { pb.#set_func(s.try_into().unwrap()) }
                         None => {}
                     }
                 })
@@ -98,7 +98,7 @@ fn gen_token_stream(
             }
         },
         TypeCategory::Protobuf => Some(
-            quote! { pb.#member =  ::protobuf::SingularPtrField::some(self.#member.to_protobuf()); },
+            quote! { pb.#member =  ::protobuf::SingularPtrField::some(self.#member.try_into().unwrap()); },
         ),
         TypeCategory::Opt => {
             gen_token_stream(ctxt, member, ty_info.bracket_ty_info.unwrap().ty, true)
@@ -109,7 +109,7 @@ fn gen_token_stream(
             // flowy_protobuf::#pb_enum_ident::from_i32(self.#member.value()).unwrap();
             // })
             Some(quote! {
-                pb.#member = self.#member.to_protobuf();
+                pb.#member = self.#member.try_into().unwrap();
             })
         },
         _ => Some(quote! { pb.#member = self.#member; }),
@@ -124,7 +124,7 @@ fn token_stream_for_vec(ctxt: &Ctxt, member: &syn::Member, ty: &syn::Type) -> Op
             pb.#member = ::protobuf::RepeatedField::from_vec(
                 self.#member
                 .iter()
-                .map(|m| m.to_protobuf())
+                .map(|m| m.try_into().unwrap())
                 .collect());
         }),
         TypeCategory::Bytes => Some(quote! { pb.#member = self.#member.clone(); }),
@@ -146,7 +146,7 @@ fn token_stream_for_map(ctxt: &Ctxt, member: &syn::Member, ty: &syn::Type) -> Op
             Some(quote! {
                 let mut m: std::collections::HashMap<String, #flowy_protobuf::#value_type> = std::collections::HashMap::new();
                 self.#member.iter().for_each(|(k,v)| {
-                    m.insert(k.clone(), v.to_protobuf());
+                    m.insert(k.clone(), v.try_into().unwrap());
                 });
                 pb.#member = m;
             })

+ 0 - 4
rust-lib/flowy-sdk/src/lib.rs

@@ -15,8 +15,4 @@ impl FlowySDK {
     }
 }
 
-pub async fn async_send(request: DispatchRequest) -> EventResponse {
-    EventDispatch::async_send(request).await
-}
-
 pub fn sync_send(request: DispatchRequest) -> EventResponse { EventDispatch::sync_send(request) }

+ 17 - 11
rust-lib/flowy-sys/src/dispatch.rs

@@ -46,7 +46,14 @@ impl EventDispatch {
         *(EVENT_DISPATCH.write().unwrap()) = Some(dispatch);
     }
 
-    pub fn async_send(request: DispatchRequest) -> DispatchFuture {
+    pub fn async_send<Req, Callback>(request: Req, callback: Callback) -> DispatchFuture
+    where
+        Req: std::convert::Into<DispatchRequest>,
+        Callback: FnOnce(EventResponse) -> BoxFuture<'static, ()> + 'static + Send + Sync,
+    {
+        let mut request = request.into();
+        request.callback = Some(Box::new(callback));
+
         match EVENT_DISPATCH.read() {
             Ok(dispatch) => {
                 let dispatch = dispatch.as_ref().unwrap();
@@ -81,7 +88,13 @@ impl EventDispatch {
     }
 
     pub fn sync_send(request: DispatchRequest) -> EventResponse {
-        futures::executor::block_on(async { EventDispatch::async_send(request).await })
+        futures::executor::block_on(async {
+            EventDispatch::async_send(request, |response| {
+                dbg!(&response);
+                Box::pin(async {})
+            })
+            .await
+        })
     }
 }
 
@@ -130,16 +143,9 @@ impl DispatchRequest {
 
     pub fn payload<P>(mut self, payload: P) -> Self
     where
-        P: TryInto<Payload, Error = String>,
+        P: Into<Payload>,
     {
-        let payload = match payload.try_into() {
-            Ok(payload) => payload,
-            Err(e) => {
-                log::error!("{}", e);
-                Payload::None
-            },
-        };
-        self.payload = payload;
+        self.payload = payload.into();
         self
     }
 

+ 0 - 4
rust-lib/flowy-sys/tests/api/helper.rs

@@ -10,10 +10,6 @@ pub fn setup_env() {
     });
 }
 
-pub async fn async_send(request: DispatchRequest) -> EventResponse {
-    EventDispatch::async_send(request).await
-}
-
 pub fn init_dispatch<F>(module_factory: F)
 where
     F: FnOnce() -> Vec<Module>,

+ 9 - 2
rust-lib/flowy-sys/tests/api/module.rs

@@ -10,6 +10,13 @@ async fn test_init() {
     init_dispatch(|| vec![Module::new().event(event, hello)]);
 
     let request = DispatchRequest::new(event);
-    let resp = async_send(request).await;
-    dbg!(&resp);
+    let _ = EventDispatch::async_send(
+        request,
+        Some(|resp| {
+            Box::pin(async move {
+                dbg!(&resp);
+            })
+        }),
+    )
+    .await;
 }

+ 5 - 2
rust-lib/flowy-test/src/lib.rs

@@ -66,7 +66,8 @@ impl EventTester {
         P: ToBytes,
     {
         let mut request = self.request.take().unwrap();
-        request = request.payload(Data(payload));
+        let bytes = payload.into_bytes().unwrap();
+        request = request.payload(bytes);
         self.request = Some(request);
         self
     }
@@ -78,7 +79,9 @@ impl EventTester {
 
     #[allow(dead_code)]
     pub async fn async_send(mut self) -> Self {
-        let resp = async_send(self.request.take().unwrap()).await;
+        let resp =
+            EventDispatch::async_send(self.request.take().unwrap(), |_| Box::pin(async {})).await;
+
         if let Some(ref status_code) = self.assert_status_code {
             assert_eq!(&resp.status_code, status_code)
         }

+ 27 - 0
rust-lib/flowy-user/src/domain/event.rs

@@ -0,0 +1,27 @@
+use derive_more::Display;
+use flowy_derive::ProtoBuf_Enum;
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum)]
+pub enum UserEvent {
+    #[display(fmt = "AuthCheck")]
+    AuthCheck = 0,
+    #[display(fmt = "SignIn")]
+    SignIn    = 1,
+    #[display(fmt = "SignUp")]
+    SignUp    = 2,
+    #[display(fmt = "SignOut")]
+    SignOut   = 3,
+}
+
+// impl std::convert::TryFrom<&crate::protobuf::UserEvent> for UserEvent {
+//     type Error = String;
+//     fn try_from(pb: &crate::protobuf::UserEvent) -> Result<Self, Self::Error>
+// {         let a = UserEvent::SignIn;
+//         match pb {
+//             crate::protobuf::UserEvent::AuthCheck => { UserEvent::SignIn }
+//             UserEvent::SignIn => { UserEvent::SignIn }
+//             UserEvent::SignUp => {UserEvent::SignIn }
+//             UserEvent::SignOut => {UserEvent::SignIn}
+//         }
+//     }
+// }

+ 1 - 0
rust-lib/flowy-user/src/domain/mod.rs

@@ -1,3 +1,4 @@
+pub mod event;
 pub mod user;
 
 pub use user::*;

+ 1 - 1
rust-lib/flowy-user/src/lib.rs

@@ -5,5 +5,5 @@ pub mod module;
 mod protobuf;
 
 pub mod prelude {
-    pub use crate::{domain::*, handlers::auth::*, module::UserEvent::*};
+    pub use crate::{domain::*, handlers::auth::*};
 }

+ 1 - 15
rust-lib/flowy-user/src/module.rs

@@ -1,20 +1,6 @@
-use crate::handlers::*;
+use crate::{domain::event::UserEvent, handlers::*};
 use flowy_sys::prelude::*;
 
-use derive_more::Display;
-
-#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash)]
-pub enum UserEvent {
-    #[display(fmt = "AuthCheck")]
-    AuthCheck = 0,
-    #[display(fmt = "SignIn")]
-    SignIn    = 1,
-    #[display(fmt = "SignUp")]
-    SignUp    = 2,
-    #[display(fmt = "SignOut")]
-    SignOut   = 3,
-}
-
 pub fn create() -> Module {
     Module::new()
         .name("Flowy-User")

+ 108 - 0
rust-lib/flowy-user/src/protobuf/model/event.rs

@@ -0,0 +1,108 @@
+// This file is generated by rust-protobuf 2.22.1. Do not edit
+// @generated
+
+// https://github.com/rust-lang/rust-clippy/issues/702
+#![allow(unknown_lints)]
+#![allow(clippy::all)]
+
+#![allow(unused_attributes)]
+#![cfg_attr(rustfmt, rustfmt::skip)]
+
+#![allow(box_pointers)]
+#![allow(dead_code)]
+#![allow(missing_docs)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+#![allow(non_upper_case_globals)]
+#![allow(trivial_casts)]
+#![allow(unused_imports)]
+#![allow(unused_results)]
+//! Generated file from `event.proto`
+
+/// Generated files are compatible only with the same version
+/// of protobuf runtime.
+// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_22_1;
+
+#[derive(Clone,PartialEq,Eq,Debug,Hash)]
+pub enum UserEvent {
+    AuthCheck = 0,
+    SignIn = 1,
+    SignUp = 2,
+    SignOut = 3,
+}
+
+impl ::protobuf::ProtobufEnum for UserEvent {
+    fn value(&self) -> i32 {
+        *self as i32
+    }
+
+    fn from_i32(value: i32) -> ::std::option::Option<UserEvent> {
+        match value {
+            0 => ::std::option::Option::Some(UserEvent::AuthCheck),
+            1 => ::std::option::Option::Some(UserEvent::SignIn),
+            2 => ::std::option::Option::Some(UserEvent::SignUp),
+            3 => ::std::option::Option::Some(UserEvent::SignOut),
+            _ => ::std::option::Option::None
+        }
+    }
+
+    fn values() -> &'static [Self] {
+        static values: &'static [UserEvent] = &[
+            UserEvent::AuthCheck,
+            UserEvent::SignIn,
+            UserEvent::SignUp,
+            UserEvent::SignOut,
+        ];
+        values
+    }
+
+    fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            ::protobuf::reflect::EnumDescriptor::new_pb_name::<UserEvent>("UserEvent", file_descriptor_proto())
+        })
+    }
+}
+
+impl ::std::marker::Copy for UserEvent {
+}
+
+impl ::std::default::Default for UserEvent {
+    fn default() -> Self {
+        UserEvent::AuthCheck
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for UserEvent {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
+    }
+}
+
+static file_descriptor_proto_data: &'static [u8] = b"\
+    \n\x0bevent.proto*?\n\tUserEvent\x12\r\n\tAuthCheck\x10\0\x12\n\n\x06Sig\
+    nIn\x10\x01\x12\n\n\x06SignUp\x10\x02\x12\x0b\n\x07SignOut\x10\x03J\xce\
+    \x01\n\x06\x12\x04\0\0\x07\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\
+    \x05\0\x12\x04\x02\0\x07\x01\n\n\n\x03\x05\0\x01\x12\x03\x02\x05\x0e\n\
+    \x0b\n\x04\x05\0\x02\0\x12\x03\x03\x04\x12\n\x0c\n\x05\x05\0\x02\0\x01\
+    \x12\x03\x03\x04\r\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x03\x10\x11\n\x0b\
+    \n\x04\x05\0\x02\x01\x12\x03\x04\x04\x0f\n\x0c\n\x05\x05\0\x02\x01\x01\
+    \x12\x03\x04\x04\n\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x04\r\x0e\n\x0b\
+    \n\x04\x05\0\x02\x02\x12\x03\x05\x04\x0f\n\x0c\n\x05\x05\0\x02\x02\x01\
+    \x12\x03\x05\x04\n\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\x05\r\x0e\n\x0b\
+    \n\x04\x05\0\x02\x03\x12\x03\x06\x04\x10\n\x0c\n\x05\x05\0\x02\x03\x01\
+    \x12\x03\x06\x04\x0b\n\x0c\n\x05\x05\0\x02\x03\x02\x12\x03\x06\x0e\x0fb\
+    \x06proto3\
+";
+
+static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
+
+fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
+    ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
+}
+
+pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
+    file_descriptor_proto_lazy.get(|| {
+        parse_descriptor_proto()
+    })
+}

+ 3 - 0
rust-lib/flowy-user/src/protobuf/model/mod.rs

@@ -6,5 +6,8 @@ pub use sign_up::*;
 mod sign_in; 
 pub use sign_in::*; 
 
+mod event; 
+pub use event::*; 
+
 mod user; 
 pub use user::*; 

+ 8 - 0
rust-lib/flowy-user/src/protobuf/proto/event.proto

@@ -0,0 +1,8 @@
+syntax = "proto3";
+
+enum UserEvent {
+    AuthCheck = 0;
+    SignIn = 1;
+    SignUp = 2;
+    SignOut = 3;
+}

+ 1 - 1
rust-lib/flowy-user/tests/sign_in.rs

@@ -1,5 +1,5 @@
 use flowy_test::prelude::*;
-use flowy_user::prelude::*;
+use flowy_user::prelude::{event::UserEvent::*, *};
 
 #[test]
 fn sign_in_with_invalid_email() {

+ 67 - 0
scripts/flowy-tool/src/dart_event/event_template.rs

@@ -4,6 +4,11 @@ pub struct EventTemplate {
     tera_context: Context,
 }
 
+pub const DART_IMPORTED: &'static str = r#"
+/// Auto gen code from rust ast, do not edit
+part of 'cqrs.dart';
+"#;
+
 pub struct EventRenderContext {
     pub input_deserializer: String,
     pub output_deserializer: String,
@@ -21,5 +26,67 @@ impl EventTemplate {
 
     pub fn render(&mut self, _render_context: EventRenderContext, _index: usize) -> Option<String> {
         None
+        // if index == 0 {
+        //     self.tera_context
+        //         .insert("imported_dart_files", DART_IMPORTED)
+        // }
+        // self.tera_context.insert("index", &index);
+        //
+        //
+        //
+        // self.tera_context.insert(
+        //     "command_request_struct_ident",
+        //     &render_context.command_request_struct_ident,
+        // );
+        //
+        // self.tera_context
+        //     .insert("request_deserializer", &render_context.request_deserializer);
+        //
+        // if render_context.request_deserializer.is_empty() {
+        //     self.tera_context.insert("has_request_deserializer", &false);
+        // } else {
+        //     self.tera_context.insert("has_request_deserializer", &true);
+        // }
+        // self.tera_context
+        //     .insert("command_ident", &render_context.event);
+        //
+        // if render_context.response_deserializer.is_empty() {
+        //     self.tera_context
+        //         .insert("has_response_deserializer", &false);
+        //     self.tera_context
+        //         .insert("response_deserializer", "ResponsePacket");
+        // } else {
+        //     self.tera_context.insert("has_response_deserializer", &true);
+        //     self.tera_context.insert(
+        //         "response_deserializer",
+        //         &render_context.response_deserializer,
+        //     );
+        // }
+        //
+        // self.tera_context
+        //     .insert("async_cqrs_type", &render_context.async_cqrs_type);
+        // let repo_absolute_path =
+        //     std::fs::canonicalize("./flowy-scripts/rust-tool/src/flutter/cqrs")
+        //         .unwrap()
+        //         .as_path()
+        //         .display()
+        //         .to_string();
+        //
+        // let template_path = format!("{}/**/*.tera", repo_absolute_path);
+        // let tera = match Tera::new(&template_path) {
+        //     Ok(t) => t,
+        //     Err(e) => {
+        //         log::error!("Parsing error(s): {}", e);
+        //         ::std::process::exit(1);
+        //     }
+        // };
+        //
+        // match tera.render("command_request_template.tera", &self.tera_context) {
+        //     Ok(r) => Some(r),
+        //     Err(e) => {
+        //         log::error!("{:?}", e);
+        //         None
+        //     }
+        // }
     }
 }

+ 72 - 0
scripts/flowy-tool/src/dart_event/event_template.tera

@@ -0,0 +1,72 @@
+{%- if index == 0 %}
+{{ imported_dart_files }}
+{%- endif -%}
+
+class {{ command_request_struct_ident }} {
+{%- if has_request_deserializer %}
+    {{ request_deserializer }} body;
+{%- else %}
+    Uint8List? body;
+{%- endif %}
+
+{%- if has_request_deserializer %}
+    {{ command_request_struct_ident }}(this.body);
+{%- else %}
+    {{ command_request_struct_ident }}();
+{%- endif %}
+    Future<Either<{{ response_deserializer }}, FlowyError>> send() {
+      final command = Command.{{ command_ident }};
+      var request = RequestPacket.create()
+      ..command = command
+      ..id = uuid();
+
+{%- if has_request_deserializer  %}
+      return protobufToBytes(body).fold(
+        (req_bytes) {
+          request.body = req_bytes;
+          return {{ async_cqrs_type }}(request).then((response) {
+            {%- if has_response_deserializer  %}
+            try {
+              if (response.hasErr()) {
+                return right(FlowyError.from(response));
+              } else {
+                final pb = {{ response_deserializer }}.fromBuffer(response.body);
+                return left(pb);
+              }
+
+            } catch (e, s) {
+              final error = FlowyError.fromError('error: ${e.runtimeType}. Stack trace: $s', StatusCode.ProtobufDeserializeError);
+              return right(error);
+            }
+            {%- else %}
+            return left(response);
+            {%- endif %}
+          });
+        },
+        (err) => Future(() {
+            final error = FlowyError.fromError(err, StatusCode.ProtobufSerializeError);
+            return right(error);
+        }),
+      );
+{%- else %}
+      return {{ async_cqrs_type }}(request).then((response) {
+        {%- if has_response_deserializer  %}
+        try {
+          if (response.hasErr()) {
+            return right(FlowyError.from(response));
+          } else {
+            final pb = {{ response_deserializer }}.fromBuffer(response.body);
+            return left(pb);
+          }
+        } catch (e, s) {
+          final error = FlowyError.fromError('error: ${e.runtimeType}. Stack trace: $s', StatusCode.ProtobufDeserializeError);
+          return right(error);
+        }
+        {%- else %}
+        return left(response);
+        {%- endif %}
+
+      });
+{%- endif %}
+    }
+}

+ 1 - 1
scripts/flowy-tool/src/util/crate_config.rs

@@ -24,7 +24,7 @@ pub fn parse_crate_config_from(entry: &walkdir::DirEntry) -> Option<CrateConfig>
     let folder_name = path.file_stem().unwrap().to_str().unwrap().to_string();
     let config_path = format!("{}/Flowy.toml", crate_path);
 
-    if std::path::Path::new(&config_path).exists() {
+    if !std::path::Path::new(&config_path).exists() {
         return None;
     }