Browse Source

add pb generation log

appflowy 3 years ago
parent
commit
3fa0f97e74

+ 11 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/lib-ot/cache.pb.dart

@@ -0,0 +1,11 @@
+///
+//  Generated code. Do not modify.
+//  source: cache.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 'cache.pbenum.dart';
+

+ 26 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/lib-ot/cache.pbenum.dart

@@ -0,0 +1,26 @@
+///
+//  Generated code. Do not modify.
+//  source: cache.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 RevState extends $pb.ProtobufEnum {
+  static const RevState Local = RevState._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Local');
+  static const RevState Acked = RevState._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Acked');
+
+  static const $core.List<RevState> values = <RevState> [
+    Local,
+    Acked,
+  ];
+
+  static final $core.Map<$core.int, RevState> _byValue = $pb.ProtobufEnum.initByValue(values);
+  static RevState? valueOf($core.int value) => _byValue[value];
+
+  const RevState._($core.int v, $core.String n) : super(v, n);
+}
+

+ 21 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/lib-ot/cache.pbjson.dart

@@ -0,0 +1,21 @@
+///
+//  Generated code. Do not modify.
+//  source: cache.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,deprecated_member_use_from_same_package
+
+import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use revStateDescriptor instead')
+const RevState$json = const {
+  '1': 'RevState',
+  '2': const [
+    const {'1': 'Local', '2': 0},
+    const {'1': 'Acked', '2': 1},
+  ],
+};
+
+/// Descriptor for `RevState`. Decode as a `google.protobuf.EnumDescriptorProto`.
+final $typed_data.Uint8List revStateDescriptor = $convert.base64Decode('CghSZXZTdGF0ZRIJCgVMb2NhbBAAEgkKBUFja2VkEAE=');

+ 9 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/lib-ot/cache.pbserver.dart

@@ -0,0 +1,9 @@
+///
+//  Generated code. Do not modify.
+//  source: cache.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,deprecated_member_use_from_same_package
+
+export 'cache.pb.dart';
+

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

@@ -1,2 +1,3 @@
 // Auto-generated, do not edit 
 // Auto-generated, do not edit 
+export './cache.pb.dart';
 export './model.pb.dart';
 export './model.pb.dart';

+ 14 - 15
frontend/rust-lib/flowy-document/src/services/doc/edit/queue.rs

@@ -41,31 +41,31 @@ impl EditCommandQueue {
         };
         };
         stream
         stream
             .for_each(|msg| async {
             .for_each(|msg| async {
-                match self.handle_message(msg).await {
-                    Ok(_) => {},
-                    Err(e) => log::error!("{:?}", e),
-                }
+                self.handle_message(msg).await;
             })
             })
             .await;
             .await;
     }
     }
 
 
-    async fn handle_message(&self, msg: EditCommand) -> Result<(), DocumentError> {
+    async fn handle_message(&self, msg: EditCommand) {
         match msg {
         match msg {
             EditCommand::ComposeDelta { delta, ret } => {
             EditCommand::ComposeDelta { delta, ret } => {
                 let result = self.composed_delta(delta).await;
                 let result = self.composed_delta(delta).await;
                 let _ = ret.send(result);
                 let _ = ret.send(result);
             },
             },
             EditCommand::ProcessRemoteRevision { bytes, ret } => {
             EditCommand::ProcessRemoteRevision { bytes, ret } => {
-                let revision = Revision::try_from(bytes)?;
-                let delta = RichTextDelta::from_bytes(&revision.delta_data)?;
-                let rev_id: RevId = revision.rev_id.into();
-                let (server_prime, client_prime) = self.document.read().await.delta().transform(&delta)?;
-                let transform_delta = TransformDeltas {
-                    client_prime,
-                    server_prime,
-                    server_rev_id: rev_id,
+                let f = || async {
+                    let revision = Revision::try_from(bytes)?;
+                    let delta = RichTextDelta::from_bytes(&revision.delta_data)?;
+                    let rev_id: RevId = revision.rev_id.into();
+                    let (server_prime, client_prime) = self.document.read().await.delta().transform(&delta)?;
+                    let transform_delta = TransformDeltas {
+                        client_prime,
+                        server_prime,
+                        server_rev_id: rev_id,
+                    };
+                    Ok::<TransformDeltas, DocumentError>(transform_delta)
                 };
                 };
-                let _ = ret.send(Ok(transform_delta));
+                let _ = ret.send(f().await);
             },
             },
             EditCommand::Insert { index, data, ret } => {
             EditCommand::Insert { index, data, ret } => {
                 let delta = self.document.write().await.insert(index, data);
                 let delta = self.document.write().await.insert(index, data);
@@ -110,7 +110,6 @@ impl EditCommandQueue {
                 let _ = ret.send(Ok(delta));
                 let _ = ret.send(Ok(delta));
             },
             },
         }
         }
-        Ok(())
     }
     }
 
 
     #[tracing::instrument(level = "debug", skip(self, delta), fields(compose_result), err)]
     #[tracing::instrument(level = "debug", skip(self, delta), fields(compose_result), err)]

+ 2 - 1
frontend/rust-lib/flowy-document/src/services/doc/revision/sync.rs

@@ -65,11 +65,12 @@ impl RevisionDownStream {
                 };
                 };
             }
             }
         };
         };
+
         stream
         stream
             .for_each(|msg| async {
             .for_each(|msg| async {
                 match self.handle_message(msg).await {
                 match self.handle_message(msg).await {
                     Ok(_) => {},
                     Ok(_) => {},
-                    Err(e) => log::error!("RevisionDownStream error: {}", e),
+                    Err(e) => log::error!("[RevisionDownStream:{}] error: {}", self.editor.doc_id, e),
                 }
                 }
             })
             })
             .await;
             .await;

+ 14 - 1
frontend/rust-lib/flowy-document/tests/editor/revision_test.rs

@@ -1,5 +1,5 @@
 use flowy_test::editor::{EditorScript::*, *};
 use flowy_test::editor::{EditorScript::*, *};
-use lib_ot::revision::RevState;
+use lib_ot::{core::DeltaBuilder, revision::RevState, rich_text::RichTextDeltaBuilder};
 
 
 #[tokio::test]
 #[tokio::test]
 async fn doc_rev_state_test1() {
 async fn doc_rev_state_test1() {
@@ -37,3 +37,16 @@ async fn doc_rev_state_test2() {
     ];
     ];
     EditorTest::new().await.run_scripts(scripts).await;
     EditorTest::new().await.run_scripts(scripts).await;
 }
 }
+
+#[tokio::test]
+async fn doc_push_test() {
+    let delta = RichTextDeltaBuilder::new().insert("abc\n").build();
+    let scripts = vec![
+        InsertText("1", 0),
+        InsertText("2", 1),
+        InsertText("3", 2),
+        SimulatePushRevisionMessageWithDelta(delta),
+        AssertJson(r#"[{"insert":"123\nabc\n"}]"#),
+    ];
+    EditorTest::new().await.run_scripts(scripts).await;
+}

+ 39 - 20
frontend/rust-lib/flowy-test/src/editor.rs

@@ -1,14 +1,32 @@
 use crate::{helper::ViewTest, FlowySDKTest};
 use crate::{helper::ViewTest, FlowySDKTest};
 use flowy_document::services::doc::{edit::ClientDocEditor, revision::RevisionIterator};
 use flowy_document::services::doc::{edit::ClientDocEditor, revision::RevisionIterator};
-use flowy_document_infra::entities::{doc::DocIdentifier, ws::WsDocumentDataBuilder};
+use flowy_document_infra::entities::{
+    doc::DocIdentifier,
+    ws::{WsDocumentData, WsDocumentDataBuilder},
+};
 use lib_ot::{
 use lib_ot::{
     core::Interval,
     core::Interval,
-    revision::{RevState, Revision, RevisionRange},
+    revision::{RevState, RevType, Revision, RevisionRange},
     rich_text::RichTextDelta,
     rich_text::RichTextDelta,
 };
 };
-use std::sync::Arc;
+use std::{str::FromStr, sync::Arc};
 use tokio::time::{sleep, Duration};
 use tokio::time::{sleep, Duration};
 
 
+pub enum EditorScript {
+    InsertText(&'static str, usize),
+    Delete(Interval),
+    Replace(Interval, &'static str),
+    Undo(),
+    Redo(),
+    SimulatePushRevisionMessageWithDelta(RichTextDelta),
+    SimulatePullRevisionMessage(RevisionRange),
+    SimulateAckedMessage(i64),
+    AssertRevisionState(i64, RevState),
+    AssertNextSendingRevision(Option<i64>),
+    AssertCurrentRevId(i64),
+    AssertJson(&'static str),
+}
+
 pub struct EditorTest {
 pub struct EditorTest {
     pub sdk: FlowySDKTest,
     pub sdk: FlowySDKTest,
     pub editor: Arc<ClientDocEditor>,
     pub editor: Arc<ClientDocEditor>,
@@ -71,12 +89,23 @@ impl EditorTest {
                 let next_revision = next_revision.unwrap();
                 let next_revision = next_revision.unwrap();
                 assert_eq!(next_revision.revision.rev_id, rev_id.unwrap());
                 assert_eq!(next_revision.revision.rev_id, rev_id.unwrap());
             },
             },
-            EditorScript::SimulatePushRevisionMessage(_revision) => {},
+            EditorScript::SimulatePushRevisionMessageWithDelta(delta) => {
+                let local_base_rev_id = rev_manager.rev_id();
+                let local_rev_id = local_base_rev_id + 1;
+                let revision = Revision::new(
+                    local_base_rev_id,
+                    local_rev_id,
+                    delta.to_bytes().to_vec(),
+                    &doc_id,
+                    RevType::Remote,
+                );
+                let data = WsDocumentDataBuilder::build_push_rev_message(&doc_id, revision);
+                self.send_ws_message(data).await;
+            },
             EditorScript::SimulatePullRevisionMessage(_range) => {},
             EditorScript::SimulatePullRevisionMessage(_range) => {},
             EditorScript::SimulateAckedMessage(i64) => {
             EditorScript::SimulateAckedMessage(i64) => {
                 let data = WsDocumentDataBuilder::build_acked_message(&doc_id, i64);
                 let data = WsDocumentDataBuilder::build_acked_message(&doc_id, i64);
-                self.editor.handle_ws_message(data).await.unwrap();
-                sleep(Duration::from_millis(200)).await;
+                self.send_ws_message(data).await;
             },
             },
             EditorScript::AssertJson(expected) => {
             EditorScript::AssertJson(expected) => {
                 let expected_delta: RichTextDelta = serde_json::from_str(expected).unwrap();
                 let expected_delta: RichTextDelta = serde_json::from_str(expected).unwrap();
@@ -90,19 +119,9 @@ impl EditorTest {
             },
             },
         }
         }
     }
     }
-}
 
 
-pub enum EditorScript {
-    InsertText(&'static str, usize),
-    Delete(Interval),
-    Replace(Interval, &'static str),
-    Undo(),
-    Redo(),
-    SimulatePushRevisionMessage(Revision),
-    SimulatePullRevisionMessage(RevisionRange),
-    SimulateAckedMessage(i64),
-    AssertRevisionState(i64, RevState),
-    AssertNextSendingRevision(Option<i64>),
-    AssertCurrentRevId(i64),
-    AssertJson(&'static str),
+    async fn send_ws_message(&self, data: WsDocumentData) {
+        self.editor.handle_ws_message(data).await.unwrap();
+        sleep(Duration::from_millis(200)).await;
+    }
 }
 }

+ 24 - 8
frontend/rust-lib/flowy-user/src/services/user/user_session.rs

@@ -20,6 +20,7 @@ use flowy_database::{
     ExpressionMethods,
     ExpressionMethods,
     UserDatabaseConnection,
     UserDatabaseConnection,
 };
 };
+use flowy_user_infra::entities::{SignInResponse, SignUpResponse};
 use lib_infra::{entities::network_state::NetworkState, kv::KV};
 use lib_infra::{entities::network_state::NetworkState, kv::KV};
 use lib_sqlite::ConnectionPool;
 use lib_sqlite::ConnectionPool;
 use lib_ws::{WsConnectState, WsMessageHandler, WsSender};
 use lib_ws::{WsConnectState, WsMessageHandler, WsSender};
@@ -98,7 +99,7 @@ impl UserSession {
             self.user_profile().await
             self.user_profile().await
         } else {
         } else {
             let resp = self.server.sign_in(params).await?;
             let resp = self.server.sign_in(params).await?;
-            let session = Session::new(&resp.user_id, &resp.token, &resp.email);
+            let session: Session = resp.clone().into();
             let _ = self.set_session(Some(session))?;
             let _ = self.set_session(Some(session))?;
             let user_table = self.save_user(resp.into()).await?;
             let user_table = self.save_user(resp.into()).await?;
             let user_profile: UserProfile = user_table.into();
             let user_profile: UserProfile = user_table.into();
@@ -113,7 +114,7 @@ impl UserSession {
             self.user_profile().await
             self.user_profile().await
         } else {
         } else {
             let resp = self.server.sign_up(params).await?;
             let resp = self.server.sign_up(params).await?;
-            let session = Session::new(&resp.user_id, &resp.token, &resp.email);
+            let session: Session = resp.clone().into();
             let _ = self.set_session(Some(session))?;
             let _ = self.set_session(Some(session))?;
             let user_table = self.save_user(resp.into()).await?;
             let user_table = self.save_user(resp.into()).await?;
             let user_profile: UserProfile = user_table.into();
             let user_profile: UserProfile = user_table.into();
@@ -326,17 +327,32 @@ struct Session {
     user_id: String,
     user_id: String,
     token: String,
     token: String,
     email: String,
     email: String,
+    name: String,
 }
 }
 
 
-impl Session {
-    pub fn new(user_id: &str, token: &str, email: &str) -> Self {
-        Self {
-            user_id: user_id.to_owned(),
-            token: token.to_owned(),
-            email: email.to_owned(),
+impl std::convert::From<SignInResponse> for Session {
+    fn from(resp: SignInResponse) -> Self {
+        Session {
+            user_id: resp.user_id,
+            token: resp.token,
+            email: resp.email,
+            name: resp.name,
+        }
+    }
+}
+
+impl std::convert::From<SignUpResponse> for Session {
+    fn from(resp: SignUpResponse) -> Self {
+        Session {
+            user_id: resp.user_id,
+            token: resp.token,
+            email: resp.email,
+            name: resp.name,
         }
         }
     }
     }
+}
 
 
+impl Session {
     pub fn into_part(self) -> (String, String) { (self.user_id, self.token) }
     pub fn into_part(self) -> (String, String) { (self.user_id, self.token) }
 }
 }
 
 

+ 6 - 2
shared-lib/flowy-ast/src/ast.rs

@@ -115,7 +115,7 @@ impl<'a> ASTField<'a> {
         let mut bracket_ty = None;
         let mut bracket_ty = None;
         let mut bracket_category = Some(BracketCategory::Other);
         let mut bracket_category = Some(BracketCategory::Other);
         match parse_ty(cx, &field.ty) {
         match parse_ty(cx, &field.ty) {
-            Some(inner) => {
+            Ok(Some(inner)) => {
                 match inner.primitive_ty {
                 match inner.primitive_ty {
                     PrimitiveTy::Map(map_info) => {
                     PrimitiveTy::Map(map_info) => {
                         bracket_category = Some(BracketCategory::Map((map_info.key.clone(), map_info.value)))
                         bracket_category = Some(BracketCategory::Map((map_info.key.clone(), map_info.value)))
@@ -141,9 +141,13 @@ impl<'a> ASTField<'a> {
                     },
                     },
                 }
                 }
             },
             },
-            None => {
+            Ok(None) => {
                 cx.error_spanned_by(&field.ty, "fail to get the ty inner type");
                 cx.error_spanned_by(&field.ty, "fail to get the ty inner type");
             },
             },
+            Err(e) => {
+                eprintln!("ASTField parser failed: {:?} with error: {}", field, e);
+                panic!()
+            },
         }
         }
 
 
         ASTField {
         ASTField {

+ 24 - 24
shared-lib/flowy-ast/src/ty_ext.rs

@@ -39,17 +39,17 @@ impl<'a> TyInfo<'a> {
     }
     }
 }
 }
 
 
-pub fn parse_ty<'a>(ctxt: &Ctxt, ty: &'a syn::Type) -> Option<TyInfo<'a>> {
+pub fn parse_ty<'a>(ctxt: &Ctxt, ty: &'a syn::Type) -> Result<Option<TyInfo<'a>>, String> {
     // Type -> TypePath -> Path -> PathSegment -> PathArguments ->
     // Type -> TypePath -> Path -> PathSegment -> PathArguments ->
     // AngleBracketedGenericArguments -> GenericArgument -> Type.
     // AngleBracketedGenericArguments -> GenericArgument -> Type.
     if let syn::Type::Path(ref p) = ty {
     if let syn::Type::Path(ref p) = ty {
         if p.path.segments.len() != 1 {
         if p.path.segments.len() != 1 {
-            return None;
+            return Ok(None);
         }
         }
 
 
         let seg = match p.path.segments.last() {
         let seg = match p.path.segments.last() {
             Some(seg) => seg,
             Some(seg) => seg,
-            None => return None,
+            None => return Ok(None),
         };
         };
 
 
         let _is_option = seg.ident == "Option";
         let _is_option = seg.ident == "Option";
@@ -60,20 +60,20 @@ pub fn parse_ty<'a>(ctxt: &Ctxt, ty: &'a syn::Type) -> Option<TyInfo<'a>> {
                 "Vec" => generate_vec_ty_info(ctxt, seg, bracketed),
                 "Vec" => generate_vec_ty_info(ctxt, seg, bracketed),
                 "Option" => generate_option_ty_info(ctxt, ty, seg, bracketed),
                 "Option" => generate_option_ty_info(ctxt, ty, seg, bracketed),
                 _ => {
                 _ => {
-                    panic!("Unsupported ty {}", seg.ident.to_string())
+                    return Err(format!("Unsupported ty {}", seg.ident.to_string()));
                 },
                 },
             }
             }
         } else {
         } else {
-            return Some(TyInfo {
+            return Ok(Some(TyInfo {
                 ident: &seg.ident,
                 ident: &seg.ident,
                 ty,
                 ty,
                 primitive_ty: PrimitiveTy::Other,
                 primitive_ty: PrimitiveTy::Other,
                 bracket_ty_info: Box::new(None),
                 bracket_ty_info: Box::new(None),
-            });
+            }));
         };
         };
     }
     }
     ctxt.error_spanned_by(ty, "Unsupported inner type, get inner type fail".to_string());
     ctxt.error_spanned_by(ty, "Unsupported inner type, get inner type fail".to_string());
-    None
+    Ok(None)
 }
 }
 
 
 fn parse_bracketed(bracketed: &AngleBracketedGenericArguments) -> Vec<&syn::Type> {
 fn parse_bracketed(bracketed: &AngleBracketedGenericArguments) -> Vec<&syn::Type> {
@@ -95,21 +95,21 @@ pub fn generate_hashmap_ty_info<'a>(
     ty: &'a syn::Type,
     ty: &'a syn::Type,
     path_segment: &'a PathSegment,
     path_segment: &'a PathSegment,
     bracketed: &'a AngleBracketedGenericArguments,
     bracketed: &'a AngleBracketedGenericArguments,
-) -> Option<TyInfo<'a>> {
+) -> Result<Option<TyInfo<'a>>, String> {
     // The args of map must greater than 2
     // The args of map must greater than 2
     if bracketed.args.len() != 2 {
     if bracketed.args.len() != 2 {
-        return None;
+        return Ok(None);
     }
     }
     let types = parse_bracketed(bracketed);
     let types = parse_bracketed(bracketed);
-    let key = parse_ty(ctxt, types[0]).unwrap().ident.to_string();
-    let value = parse_ty(ctxt, types[1]).unwrap().ident.to_string();
-    let bracket_ty_info = Box::new(parse_ty(ctxt, &types[1]));
-    Some(TyInfo {
+    let key = parse_ty(ctxt, types[0])?.unwrap().ident.to_string();
+    let value = parse_ty(ctxt, types[1])?.unwrap().ident.to_string();
+    let bracket_ty_info = Box::new(parse_ty(ctxt, &types[1])?);
+    Ok(Some(TyInfo {
         ident: &path_segment.ident,
         ident: &path_segment.ident,
         ty,
         ty,
         primitive_ty: PrimitiveTy::Map(MapInfo::new(key, value)),
         primitive_ty: PrimitiveTy::Map(MapInfo::new(key, value)),
         bracket_ty_info,
         bracket_ty_info,
-    })
+    }))
 }
 }
 
 
 fn generate_option_ty_info<'a>(
 fn generate_option_ty_info<'a>(
@@ -117,34 +117,34 @@ fn generate_option_ty_info<'a>(
     ty: &'a syn::Type,
     ty: &'a syn::Type,
     path_segment: &'a PathSegment,
     path_segment: &'a PathSegment,
     bracketed: &'a AngleBracketedGenericArguments,
     bracketed: &'a AngleBracketedGenericArguments,
-) -> Option<TyInfo<'a>> {
+) -> Result<Option<TyInfo<'a>>, String> {
     assert_eq!(path_segment.ident.to_string(), "Option".to_string());
     assert_eq!(path_segment.ident.to_string(), "Option".to_string());
     let types = parse_bracketed(bracketed);
     let types = parse_bracketed(bracketed);
-    let bracket_ty_info = Box::new(parse_ty(ctxt, &types[0]));
-    Some(TyInfo {
+    let bracket_ty_info = Box::new(parse_ty(ctxt, &types[0])?);
+    Ok(Some(TyInfo {
         ident: &path_segment.ident,
         ident: &path_segment.ident,
         ty,
         ty,
         primitive_ty: PrimitiveTy::Opt,
         primitive_ty: PrimitiveTy::Opt,
         bracket_ty_info,
         bracket_ty_info,
-    })
+    }))
 }
 }
 
 
 fn generate_vec_ty_info<'a>(
 fn generate_vec_ty_info<'a>(
     ctxt: &Ctxt,
     ctxt: &Ctxt,
     path_segment: &'a PathSegment,
     path_segment: &'a PathSegment,
     bracketed: &'a AngleBracketedGenericArguments,
     bracketed: &'a AngleBracketedGenericArguments,
-) -> Option<TyInfo<'a>> {
+) -> Result<Option<TyInfo<'a>>, String> {
     if bracketed.args.len() != 1 {
     if bracketed.args.len() != 1 {
-        return None;
+        return Ok(None);
     }
     }
     if let syn::GenericArgument::Type(ref bracketed_type) = bracketed.args.first().unwrap() {
     if let syn::GenericArgument::Type(ref bracketed_type) = bracketed.args.first().unwrap() {
-        let bracketed_ty_info = Box::new(parse_ty(ctxt, &bracketed_type));
-        return Some(TyInfo {
+        let bracketed_ty_info = Box::new(parse_ty(ctxt, &bracketed_type)?);
+        return Ok(Some(TyInfo {
             ident: &path_segment.ident,
             ident: &path_segment.ident,
             ty: bracketed_type,
             ty: bracketed_type,
             primitive_ty: PrimitiveTy::Vec,
             primitive_ty: PrimitiveTy::Vec,
             bracket_ty_info: bracketed_ty_info,
             bracket_ty_info: bracketed_ty_info,
-        });
+        }));
     }
     }
-    None
+    Ok(None)
 }
 }

+ 7 - 1
shared-lib/flowy-derive/src/proto_buf/deserialize.rs

@@ -103,7 +103,13 @@ fn token_stream_for_one_of(ctxt: &Ctxt, field: &ASTField) -> Option<TokenStream>
 
 
 fn token_stream_for_field(ctxt: &Ctxt, member: &syn::Member, ty: &syn::Type, is_option: bool) -> Option<TokenStream> {
 fn token_stream_for_field(ctxt: &Ctxt, member: &syn::Member, ty: &syn::Type, is_option: bool) -> Option<TokenStream> {
     let ident = get_member_ident(ctxt, member)?;
     let ident = get_member_ident(ctxt, member)?;
-    let ty_info = parse_ty(ctxt, ty)?;
+    let ty_info = match parse_ty(ctxt, ty) {
+        Ok(ty_info) => ty_info,
+        Err(e) => {
+            eprintln!("token_stream_for_field: {:?} with error: {}", member, e);
+            panic!()
+        },
+    }?;
     match ident_category(ty_info.ident) {
     match ident_category(ty_info.ident) {
         TypeCategory::Array => {
         TypeCategory::Array => {
             assert_bracket_ty_is_some(ctxt, &ty_info);
             assert_bracket_ty_is_some(ctxt, &ty_info);

+ 30 - 4
shared-lib/flowy-derive/src/proto_buf/serialize.rs

@@ -54,7 +54,14 @@ fn se_token_stream_for_field(ctxt: &Ctxt, field: &ASTField, _take: bool) -> Opti
 fn token_stream_for_one_of(ctxt: &Ctxt, field: &ASTField) -> Option<TokenStream> {
 fn token_stream_for_one_of(ctxt: &Ctxt, field: &ASTField) -> Option<TokenStream> {
     let member = &field.member;
     let member = &field.member;
     let ident = get_member_ident(ctxt, member)?;
     let ident = get_member_ident(ctxt, member)?;
-    let ty_info = parse_ty(ctxt, &field.ty)?;
+    let ty_info = match parse_ty(ctxt, &field.ty) {
+        Ok(ty_info) => ty_info,
+        Err(e) => {
+            eprintln!("token_stream_for_one_of failed: {:?} with error: {}", member, e);
+            panic!();
+        },
+    }?;
+
     let bracketed_ty_info = ty_info.bracket_ty_info.as_ref().as_ref();
     let bracketed_ty_info = ty_info.bracket_ty_info.as_ref().as_ref();
 
 
     let set_func = format_ident!("set_{}", ident.to_string());
     let set_func = format_ident!("set_{}", ident.to_string());
@@ -76,7 +83,13 @@ fn token_stream_for_one_of(ctxt: &Ctxt, field: &ASTField) -> Option<TokenStream>
 }
 }
 
 
 fn gen_token_stream(ctxt: &Ctxt, member: &syn::Member, ty: &syn::Type, is_option: bool) -> Option<TokenStream> {
 fn gen_token_stream(ctxt: &Ctxt, member: &syn::Member, ty: &syn::Type, is_option: bool) -> Option<TokenStream> {
-    let ty_info = parse_ty(ctxt, ty)?;
+    let ty_info = match parse_ty(ctxt, ty) {
+        Ok(ty_info) => ty_info,
+        Err(e) => {
+            eprintln!("gen_token_stream failed: {:?} with error: {}", member, e);
+            panic!();
+        },
+    }?;
     match ident_category(ty_info.ident) {
     match ident_category(ty_info.ident) {
         TypeCategory::Array => token_stream_for_vec(ctxt, &member, &ty_info.ty),
         TypeCategory::Array => token_stream_for_vec(ctxt, &member, &ty_info.ty),
         TypeCategory::Map => token_stream_for_map(ctxt, &member, &ty_info.bracket_ty_info.unwrap().ty),
         TypeCategory::Map => token_stream_for_map(ctxt, &member, &ty_info.bracket_ty_info.unwrap().ty),
@@ -111,7 +124,14 @@ fn gen_token_stream(ctxt: &Ctxt, member: &syn::Member, ty: &syn::Type, is_option
 
 
 // e.g. pub cells: Vec<CellData>, the memeber will be cells, ty would be Vec
 // e.g. pub cells: Vec<CellData>, the memeber will be cells, ty would be Vec
 fn token_stream_for_vec(ctxt: &Ctxt, member: &syn::Member, ty: &syn::Type) -> Option<TokenStream> {
 fn token_stream_for_vec(ctxt: &Ctxt, member: &syn::Member, ty: &syn::Type) -> Option<TokenStream> {
-    let ty_info = parse_ty(ctxt, ty)?;
+    let ty_info = match parse_ty(ctxt, ty) {
+        Ok(ty_info) => ty_info,
+        Err(e) => {
+            eprintln!("token_stream_for_vec failed: {:?} with error: {}", member, e);
+            panic!();
+        },
+    }?;
+
     match ident_category(ty_info.ident) {
     match ident_category(ty_info.ident) {
         TypeCategory::Protobuf => Some(quote! {
         TypeCategory::Protobuf => Some(quote! {
             pb.#member = ::protobuf::RepeatedField::from_vec(
             pb.#member = ::protobuf::RepeatedField::from_vec(
@@ -132,7 +152,13 @@ fn token_stream_for_vec(ctxt: &Ctxt, member: &syn::Member, ty: &syn::Type) -> Op
 fn token_stream_for_map(ctxt: &Ctxt, member: &syn::Member, ty: &syn::Type) -> Option<TokenStream> {
 fn token_stream_for_map(ctxt: &Ctxt, member: &syn::Member, ty: &syn::Type) -> Option<TokenStream> {
     // The key of the hashmap must be string
     // The key of the hashmap must be string
     let flowy_protobuf = format_ident!("flowy_protobuf");
     let flowy_protobuf = format_ident!("flowy_protobuf");
-    let ty_info = parse_ty(ctxt, ty)?;
+    let ty_info = match parse_ty(ctxt, ty) {
+        Ok(ty_info) => ty_info,
+        Err(e) => {
+            eprintln!("token_stream_for_map failed: {:?} with error: {}", member, e);
+            panic!();
+        },
+    }?;
     match ident_category(ty_info.ident) {
     match ident_category(ty_info.ident) {
         TypeCategory::Protobuf => {
         TypeCategory::Protobuf => {
             let value_type = ty_info.ident;
             let value_type = ty_info.ident;

+ 11 - 0
shared-lib/flowy-document-infra/src/entities/ws/ws.rs

@@ -29,6 +29,15 @@ impl std::default::Default for WsDataType {
     fn default() -> Self { WsDataType::Acked }
     fn default() -> Self { WsDataType::Acked }
 }
 }
 
 
+// #[derive(ProtoBuf, Default, Debug, Clone)]
+// pub struct WsDocumentUser {
+//     #[pb(index = 1)]
+//     pub user_id: String,
+//
+//     #[pb(index = 2)]
+//     pub name: String,
+// }
+
 #[derive(ProtoBuf, Default, Debug, Clone)]
 #[derive(ProtoBuf, Default, Debug, Clone)]
 pub struct WsDocumentData {
 pub struct WsDocumentData {
     #[pb(index = 1)]
     #[pb(index = 1)]
@@ -39,6 +48,8 @@ pub struct WsDocumentData {
 
 
     #[pb(index = 3)]
     #[pb(index = 3)]
     pub data: Vec<u8>,
     pub data: Vec<u8>,
+    /* #[pb(index = 4)]
+     * pub user: WsDocumentUser, */
 }
 }
 
 
 impl std::convert::From<Revision> for WsDocumentData {
 impl std::convert::From<Revision> for WsDocumentData {

+ 1 - 1
shared-lib/lib-ot/Flowy.toml

@@ -1,3 +1,3 @@
 
 
-proto_crates = ["src/revision"]
+proto_crates = ["src/revision/model.rs"]
 event_files = []
 event_files = []

+ 97 - 0
shared-lib/lib-ot/src/protobuf/model/cache.rs

@@ -0,0 +1,97 @@
+// 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 `cache.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 RevState {
+    Local = 0,
+    Acked = 1,
+}
+
+impl ::protobuf::ProtobufEnum for RevState {
+    fn value(&self) -> i32 {
+        *self as i32
+    }
+
+    fn from_i32(value: i32) -> ::std::option::Option<RevState> {
+        match value {
+            0 => ::std::option::Option::Some(RevState::Local),
+            1 => ::std::option::Option::Some(RevState::Acked),
+            _ => ::std::option::Option::None
+        }
+    }
+
+    fn values() -> &'static [Self] {
+        static values: &'static [RevState] = &[
+            RevState::Local,
+            RevState::Acked,
+        ];
+        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::<RevState>("RevState", file_descriptor_proto())
+        })
+    }
+}
+
+impl ::std::marker::Copy for RevState {
+}
+
+impl ::std::default::Default for RevState {
+    fn default() -> Self {
+        RevState::Local
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for RevState {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
+    }
+}
+
+static file_descriptor_proto_data: &'static [u8] = b"\
+    \n\x0bcache.proto*\x20\n\x08RevState\x12\t\n\x05Local\x10\0\x12\t\n\x05A\
+    cked\x10\x01J|\n\x06\x12\x04\0\0\x05\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\
+    \n\n\n\x02\x05\0\x12\x04\x02\0\x05\x01\n\n\n\x03\x05\0\x01\x12\x03\x02\
+    \x05\r\n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\x04\x0e\n\x0c\n\x05\x05\0\x02\
+    \0\x01\x12\x03\x03\x04\t\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x03\x0c\r\n\
+    \x0b\n\x04\x05\0\x02\x01\x12\x03\x04\x04\x0e\n\x0c\n\x05\x05\0\x02\x01\
+    \x01\x12\x03\x04\x04\t\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x04\x0c\rb\
+    \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
shared-lib/lib-ot/src/protobuf/model/mod.rs

@@ -1,5 +1,8 @@
 #![cfg_attr(rustfmt, rustfmt::skip)]
 #![cfg_attr(rustfmt, rustfmt::skip)]
 // Auto-generated, do not edit
 // Auto-generated, do not edit
 
 
+mod cache;
+pub use cache::*;
+
 mod model;
 mod model;
 pub use model::*;
 pub use model::*;

+ 6 - 0
shared-lib/lib-ot/src/protobuf/proto/cache.proto

@@ -0,0 +1,6 @@
+syntax = "proto3";
+
+enum RevState {
+    Local = 0;
+    Acked = 1;
+}

+ 5 - 1
shared-lib/lib-ot/src/rich_text/delta.rs

@@ -1,3 +1,7 @@
-use crate::{core::Delta, rich_text::RichTextAttributes};
+use crate::{
+    core::{Delta, DeltaBuilder},
+    rich_text::RichTextAttributes,
+};
 
 
 pub type RichTextDelta = Delta<RichTextAttributes>;
 pub type RichTextDelta = Delta<RichTextAttributes>;
+pub type RichTextDeltaBuilder = DeltaBuilder<RichTextAttributes>;