Przeglądaj źródła

config protobuf serialize

appflowy 3 lat temu
rodzic
commit
2d9c7ba48e

+ 2 - 1
rust-lib/flowy-derive/src/derive_cache/derive_cache.rs

@@ -15,7 +15,8 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
         "HashMap" => TypeCategory::Map,
         "u8" => TypeCategory::Bytes,
         "String" => TypeCategory::Str,
-        "App"
+        "User"
+        | "UserCheck"
         => TypeCategory::Protobuf,
 
         "Option" => TypeCategory::Opt,

+ 12 - 1
rust-lib/flowy-derive/src/proto_buf/mod.rs

@@ -1,7 +1,13 @@
 mod deserialize;
 mod enum_serde;
+mod serialize;
 mod util;
-use crate::proto_buf::{deserialize::make_de_token_steam, enum_serde::make_enum_token_stream};
+
+use crate::proto_buf::{
+    deserialize::make_de_token_steam,
+    enum_serde::make_enum_token_stream,
+    serialize::make_se_token_stream,
+};
 use flowy_ast::*;
 use proc_macro2::TokenStream;
 
@@ -19,6 +25,11 @@ pub fn expand_derive(input: &syn::DeriveInput) -> Result<TokenStream, Vec<syn::E
         token_stream.extend(de_token_stream.unwrap());
     }
 
+    let se_token_stream = make_se_token_stream(&ctxt, &cont);
+    if se_token_stream.is_some() {
+        token_stream.extend(se_token_stream.unwrap());
+    }
+
     ctxt.check()?;
     Ok(token_stream)
 }

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

@@ -0,0 +1,166 @@
+use crate::{derive_cache::TypeCategory, proto_buf::util::ident_category};
+use flowy_ast::*;
+use proc_macro2::TokenStream;
+
+pub fn make_se_token_stream(ctxt: &Ctxt, ast: &ASTContainer) -> Option<TokenStream> {
+    let pb_ty = ast.attrs.pb_struct_type()?;
+    let struct_ident = &ast.ident;
+
+    let build_set_pb_fields = ast
+        .data
+        .all_fields()
+        .filter(|f| !f.attrs.skip_serializing())
+        .flat_map(|field| se_token_stream_for_field(&ctxt, &field, false));
+
+    let build_set_fields = ast
+        .data
+        .all_fields()
+        .filter(|f| !f.attrs.skip_serializing())
+        .flat_map(|field| se_token_stream_for_field(&ctxt, &field, false));
+
+    let se_token_stream: TokenStream = quote! {
+
+        impl std::convert::TryInto<Vec<u8>> for #struct_ident {
+            type Error = String;
+            fn try_into(self) -> Result<Vec<u8>, Self::Error> {
+                use protobuf::Message;
+                let pb: crate::protobuf::#pb_ty = self.try_into()?;
+                let result: ::protobuf::ProtobufResult<Vec<u8>> = pb.write_to_bytes();
+                match result {
+                    Ok(bytes) => { Ok(bytes) },
+                    Err(e) => { Err(format!("{:?}", e)) }
+                }
+            }
+        }
+
+        impl std::convert::TryInto<crate::protobuf::#pb_ty> for #struct_ident {
+            type Error = String;
+            fn try_into(self) -> Result<crate::protobuf::#pb_ty, Self::Error> {
+                let mut pb = crate::protobuf::#pb_ty::new();
+                #(#build_set_pb_fields)*
+                Ok(pb)
+            }
+        }
+    }
+    .into();
+
+    Some(se_token_stream)
+}
+
+fn se_token_stream_for_field(ctxt: &Ctxt, field: &ASTField, _take: bool) -> Option<TokenStream> {
+    if let Some(func) = &field.attrs.serialize_with() {
+        let member = &field.member;
+        Some(quote! { pb.#member=self.#func(); })
+    } else if field.attrs.is_one_of() {
+        let member = &field.member;
+        match &field.member {
+            syn::Member::Named(ref ident) => {
+                let set_func = format_ident!("set_{}", ident.to_string());
+                Some(quote! {
+                    match self.#member {
+                        Some(ref s) => { pb.#set_func(s.to_protobuf()) }
+                        None => {}
+                    }
+                })
+            },
+            _ => {
+                ctxt.error_spanned_by(member, format!("Unsupported member, get member ident fail"));
+                None
+            },
+        }
+    } else {
+        gen_token_stream(ctxt, &field.member, &field.ty, false)
+    }
+}
+
+fn gen_token_stream(
+    ctxt: &Ctxt,
+    member: &syn::Member,
+    ty: &syn::Type,
+    is_option: bool,
+) -> Option<TokenStream> {
+    let ty_info = parse_ty(ctxt, ty)?;
+    match ident_category(ty_info.ident) {
+        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::Str => {
+            if is_option {
+                Some(quote! {
+                    match self.#member {
+                        Some(ref s) => { pb.#member = s.to_string().clone();  }
+                        None => {  pb.#member = String::new(); }
+                    }
+                })
+            } else {
+                Some(quote! { pb.#member = self.#member.clone(); })
+            }
+        },
+        TypeCategory::Protobuf => Some(
+            quote! { pb.#member =  ::protobuf::SingularPtrField::some(self.#member.to_protobuf()); },
+        ),
+        TypeCategory::Opt => {
+            gen_token_stream(ctxt, member, ty_info.bracket_ty_info.unwrap().ty, true)
+        },
+        TypeCategory::Enum => {
+            // let pb_enum_ident = format_ident!("{}", ty_info.ident.to_string());
+            // Some(quote! {
+            // flowy_protobuf::#pb_enum_ident::from_i32(self.#member.value()).unwrap();
+            // })
+            Some(quote! {
+                pb.#member = self.#member.to_protobuf();
+            })
+        },
+        _ => Some(quote! { pb.#member = self.#member; }),
+    }
+}
+
+// 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> {
+    let ty_info = parse_ty(ctxt, ty)?;
+    match ident_category(ty_info.ident) {
+        TypeCategory::Protobuf => Some(quote! {
+            pb.#member = ::protobuf::RepeatedField::from_vec(
+                self.#member
+                .iter()
+                .map(|m| m.to_protobuf())
+                .collect());
+        }),
+        TypeCategory::Bytes => Some(quote! { pb.#member = self.#member.clone(); }),
+
+        _ => Some(quote! {
+            pb.#member = ::protobuf::RepeatedField::from_vec(self.#member.clone());
+        }),
+    }
+}
+
+// e.g. pub cells: HashMap<xx, xx>
+fn token_stream_for_map(ctxt: &Ctxt, member: &syn::Member, ty: &syn::Type) -> Option<TokenStream> {
+    // The key of the hashmap must be string
+    let flowy_protobuf = format_ident!("flowy_protobuf");
+    let ty_info = parse_ty(ctxt, ty)?;
+    match ident_category(ty_info.ident) {
+        TypeCategory::Protobuf => {
+            let value_type = ty_info.ident;
+            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());
+                });
+                pb.#member = m;
+            })
+        },
+
+        _ => {
+            let value_type = ty_info.ident;
+            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.clone());
+                  });
+                pb.#member = m;
+            })
+        },
+    }
+}

+ 1 - 0
rust-lib/flowy-sys/Cargo.toml

@@ -35,6 +35,7 @@ tokio = { version = "1", features = ["full"] }
 futures-util = "0.3.15"
 
 [features]
+default = ["use_protobuf"]
 use_serde = ["bincode"]
 use_protobuf= ["protobuf"]
 use_tracing= ["tracing"]

+ 6 - 6
rust-lib/flowy-sys/src/request/request.rs

@@ -117,16 +117,16 @@ impl<T> ops::DerefMut for Data<T> {
 }
 
 pub trait FromBytes: Sized {
-    fn parse_from_bytes(bytes: &Vec<u8>) -> Result<Self, SystemError>;
+    fn parse_from_bytes(bytes: &Vec<u8>) -> Result<Self, String>;
 }
 
-#[cfg(not(feature = "use_serde"))]
+#[cfg(feature = "use_protobuf")]
 impl<T> FromBytes for T
 where
     // https://stackoverflow.com/questions/62871045/tryfromu8-trait-bound-in-trait
-    T: for<'a> std::convert::TryFrom<&'a Vec<u8>, Error = SystemError>,
+    T: for<'a> std::convert::TryFrom<&'a Vec<u8>, Error = String>,
 {
-    fn parse_from_bytes(bytes: &Vec<u8>) -> Result<Self, SystemError> { T::try_from(bytes) }
+    fn parse_from_bytes(bytes: &Vec<u8>) -> Result<Self, String> { T::try_from(bytes) }
 }
 
 #[cfg(feature = "use_serde")]
@@ -134,11 +134,11 @@ impl<T> FromBytes for T
 where
     T: serde::de::DeserializeOwned + 'static,
 {
-    fn parse_from_bytes(bytes: &Vec<u8>) -> Result<Self, SystemError> {
+    fn parse_from_bytes(bytes: &Vec<u8>) -> Result<Self, String> {
         let s = String::from_utf8_lossy(bytes);
         match serde_json::from_str::<T>(s.as_ref()) {
             Ok(data) => Ok(data),
-            Err(e) => InternalError::new(format!("{:?}", e)).into(),
+            Err(e) => Err(format!("{:?}", e)),
         }
     }
 }

+ 10 - 7
rust-lib/flowy-sys/src/response/responder.rs

@@ -39,15 +39,15 @@ where
 }
 
 pub trait ToBytes {
-    fn into_bytes(self) -> Result<Vec<u8>, SystemError>;
+    fn into_bytes(self) -> Result<Vec<u8>, String>;
 }
 
-#[cfg(not(feature = "use_serde"))]
+#[cfg(feature = "use_protobuf")]
 impl<T> ToBytes for T
 where
-    T: std::convert::TryInto<Vec<u8>, Error = SystemError>,
+    T: std::convert::TryInto<Vec<u8>, Error = String>,
 {
-    fn into_bytes(self) -> Result<Vec<u8>, SystemError> { self.try_into() }
+    fn into_bytes(self) -> Result<Vec<u8>, String> { self.try_into() }
 }
 
 #[cfg(feature = "use_serde")]
@@ -55,10 +55,10 @@ impl<T> ToBytes for T
 where
     T: serde::Serialize,
 {
-    fn into_bytes(self) -> Result<Vec<u8>, SystemError> {
+    fn into_bytes(self) -> Result<Vec<u8>, String> {
         match serde_json::to_string(&self.0) {
             Ok(s) => Ok(s.into_bytes()),
-            Err(e) => InternalError::new(format!("{:?}", e)).into(),
+            Err(e) => Err(format!("{:?}", e)),
         }
     }
 }
@@ -70,7 +70,10 @@ where
     fn respond_to(self, _request: &EventRequest) -> EventResponse {
         match self.into_inner().into_bytes() {
             Ok(bytes) => ResponseBuilder::Ok().data(bytes.to_vec()).build(),
-            Err(e) => e.into(),
+            Err(e) => {
+                let system_err: SystemError = InternalError::new(format!("{:?}", e)).into();
+                system_err.into()
+            },
         }
     }
 }

+ 43 - 9
rust-lib/flowy-user/src/domain/user.rs

@@ -1,23 +1,57 @@
 use crate::domain::{user_email::UserEmail, user_name::UserName};
 use flowy_derive::ProtoBuf;
+use std::convert::TryInto;
 
+#[derive(ProtoBuf, Default)]
 pub struct User {
-    name: UserName,
-    email: UserEmail,
+    #[pb(index = 1)]
+    name: String,
+
+    #[pb(index = 2)]
+    email: String,
 }
 
 impl User {
-    pub fn new(name: UserName, email: UserEmail) -> Self { Self { name, email } }
+    pub fn new(name: UserName, email: UserEmail) -> Self {
+        Self {
+            name: name.0,
+            email: email.0,
+        }
+    }
 }
 
-#[derive(ProtoBuf, Default)]
-pub struct App {
+// #[derive(serde::Serialize)]
+// pub struct UserStatus {
+//     is_login: bool,
+// }
+//
+// impl FromBytes for UserData {
+//     fn parse_from_bytes(_bytes: &Vec<u8>) -> Result<UserData, SystemError> {
+// unimplemented!() } }
+//
+// impl ToBytes for UserStatus {
+//     fn into_bytes(self) -> Result<Vec<u8>, SystemError> { unimplemented!() }
+// }
+
+#[derive(Debug, ProtoBuf, Default)]
+pub struct UserCheck {
     #[pb(index = 1)]
-    pub id: String,
+    pub name: String,
 
     #[pb(index = 2)]
-    pub workspace_id: String, // equal to #[belongs_to(Workspace, foreign_key = "workspace_id")].
+    pub email: String,
+}
 
-    #[pb(index = 3)]
-    pub name: String,
+impl UserCheck {
+    pub fn new(name: String, email: String) -> Self { Self { name, email } }
+}
+
+impl TryInto<User> for UserCheck {
+    type Error = String;
+
+    fn try_into(self) -> Result<User, Self::Error> {
+        let name = UserName::parse(self.name)?;
+        let email = UserEmail::parse(self.email)?;
+        Ok(User::new(name, email))
+    }
 }

+ 1 - 2
rust-lib/flowy-user/src/domain/user_email.rs

@@ -1,7 +1,7 @@
 use validator::validate_email;
 
 #[derive(Debug)]
-pub struct UserEmail(String);
+pub struct UserEmail(pub String);
 
 impl UserEmail {
     pub fn parse(s: String) -> Result<UserEmail, String> {
@@ -22,7 +22,6 @@ mod tests {
     use super::*;
     use claim::assert_err;
     use fake::{faker::internet::en::SafeEmail, Fake};
-    
 
     #[test]
     fn empty_string_is_rejected() {

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

@@ -1,7 +1,7 @@
 use unicode_segmentation::UnicodeSegmentation;
 
 #[derive(Debug)]
-pub struct UserName(String);
+pub struct UserName(pub String);
 
 impl UserName {
     pub fn parse(s: String) -> Result<UserName, String> {

+ 5 - 39
rust-lib/flowy-user/src/handlers/auth.rs

@@ -1,6 +1,5 @@
-use crate::domain::{User, UserEmail, UserName};
-
-use flowy_sys::prelude::{response_ok, Data, FromBytes, ResponseResult, SystemError, ToBytes};
+use crate::domain::{User, UserCheck, UserEmail, UserName};
+use flowy_sys::prelude::*;
 use std::convert::TryInto;
 
 // tracing instrument 👉🏻 https://docs.rs/tracing/0.1.26/tracing/attr.instrument.html
@@ -12,41 +11,8 @@ use std::convert::TryInto;
         name = %data.name
     )
 )]
-pub async fn user_check(data: Data<UserData>) -> ResponseResult<UserStatus, String> {
-    let _user: User = data.into_inner().try_into()?;
-
-    response_ok(UserStatus { is_login: false })
-}
-
-#[derive(serde::Serialize)]
-pub struct UserStatus {
-    is_login: bool,
-}
-
-impl FromBytes for UserData {
-    fn parse_from_bytes(_bytes: &Vec<u8>) -> Result<UserData, SystemError> { unimplemented!() }
-}
-
-impl ToBytes for UserStatus {
-    fn into_bytes(self) -> Result<Vec<u8>, SystemError> { unimplemented!() }
-}
-
-#[derive(Debug, serde::Deserialize, serde::Serialize)]
-pub struct UserData {
-    name: String,
-    email: String,
-}
-
-impl UserData {
-    pub fn new(name: String, email: String) -> Self { Self { name, email } }
-}
-
-impl TryInto<User> for UserData {
-    type Error = String;
+pub async fn user_check(data: Data<UserCheck>) -> ResponseResult<User, String> {
+    let user: User = data.into_inner().try_into().unwrap();
 
-    fn try_into(self) -> Result<User, Self::Error> {
-        let name = UserName::parse(self.name)?;
-        let email = UserEmail::parse(self.email)?;
-        Ok(User::new(name, email))
-    }
+    response_ok(user)
 }

+ 251 - 87
rust-lib/flowy-user/src/protobuf/model/user.rs

@@ -24,80 +24,228 @@
 // const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_22_1;
 
 #[derive(PartialEq,Clone,Default)]
-pub struct App {
+pub struct User {
     // message fields
-    pub id: ::std::string::String,
-    pub workspace_id: ::std::string::String,
     pub name: ::std::string::String,
+    pub email: ::std::string::String,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
 }
 
-impl<'a> ::std::default::Default for &'a App {
-    fn default() -> &'a App {
-        <App as ::protobuf::Message>::default_instance()
+impl<'a> ::std::default::Default for &'a User {
+    fn default() -> &'a User {
+        <User as ::protobuf::Message>::default_instance()
     }
 }
 
-impl App {
-    pub fn new() -> App {
+impl User {
+    pub fn new() -> User {
         ::std::default::Default::default()
     }
 
-    // string id = 1;
+    // string name = 1;
 
 
-    pub fn get_id(&self) -> &str {
-        &self.id
+    pub fn get_name(&self) -> &str {
+        &self.name
     }
-    pub fn clear_id(&mut self) {
-        self.id.clear();
+    pub fn clear_name(&mut self) {
+        self.name.clear();
     }
 
     // Param is passed by value, moved
-    pub fn set_id(&mut self, v: ::std::string::String) {
-        self.id = v;
+    pub fn set_name(&mut self, v: ::std::string::String) {
+        self.name = v;
     }
 
     // Mutable pointer to the field.
     // If field is not initialized, it is initialized with default value first.
-    pub fn mut_id(&mut self) -> &mut ::std::string::String {
-        &mut self.id
+    pub fn mut_name(&mut self) -> &mut ::std::string::String {
+        &mut self.name
     }
 
     // Take field
-    pub fn take_id(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.id, ::std::string::String::new())
+    pub fn take_name(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.name, ::std::string::String::new())
     }
 
-    // string workspace_id = 2;
+    // string email = 2;
 
 
-    pub fn get_workspace_id(&self) -> &str {
-        &self.workspace_id
+    pub fn get_email(&self) -> &str {
+        &self.email
     }
-    pub fn clear_workspace_id(&mut self) {
-        self.workspace_id.clear();
+    pub fn clear_email(&mut self) {
+        self.email.clear();
     }
 
     // Param is passed by value, moved
-    pub fn set_workspace_id(&mut self, v: ::std::string::String) {
-        self.workspace_id = v;
+    pub fn set_email(&mut self, v: ::std::string::String) {
+        self.email = v;
     }
 
     // Mutable pointer to the field.
     // If field is not initialized, it is initialized with default value first.
-    pub fn mut_workspace_id(&mut self) -> &mut ::std::string::String {
-        &mut self.workspace_id
+    pub fn mut_email(&mut self) -> &mut ::std::string::String {
+        &mut self.email
     }
 
     // Take field
-    pub fn take_workspace_id(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.workspace_id, ::std::string::String::new())
+    pub fn take_email(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.email, ::std::string::String::new())
+    }
+}
+
+impl ::protobuf::Message for User {
+    fn is_initialized(&self) -> bool {
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            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.name)?;
+                },
+                2 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.email)?;
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        if !self.name.is_empty() {
+            my_size += ::protobuf::rt::string_size(1, &self.name);
+        }
+        if !self.email.is_empty() {
+            my_size += ::protobuf::rt::string_size(2, &self.email);
+        }
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        if !self.name.is_empty() {
+            os.write_string(1, &self.name)?;
+        }
+        if !self.email.is_empty() {
+            os.write_string(2, &self.email)?;
+        }
+        os.write_unknown_fields(self.get_unknown_fields())?;
+        ::std::result::Result::Ok(())
+    }
+
+    fn get_cached_size(&self) -> u32 {
+        self.cached_size.get()
+    }
+
+    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
+        &self.unknown_fields
+    }
+
+    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
+        &mut self.unknown_fields
+    }
+
+    fn as_any(&self) -> &dyn (::std::any::Any) {
+        self as &dyn (::std::any::Any)
+    }
+    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
+        self as &mut dyn (::std::any::Any)
+    }
+    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        self
+    }
+
+    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
+        Self::descriptor_static()
     }
 
-    // string name = 3;
+    fn new() -> User {
+        User::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        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>(
+                "name",
+                |m: &User| { &m.name },
+                |m: &mut User| { &mut m.name },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "email",
+                |m: &User| { &m.email },
+                |m: &mut User| { &mut m.email },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<User>(
+                "User",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static User {
+        static instance: ::protobuf::rt::LazyV2<User> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(User::new)
+    }
+}
+
+impl ::protobuf::Clear for User {
+    fn clear(&mut self) {
+        self.name.clear();
+        self.email.clear();
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for User {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for User {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
+#[derive(PartialEq,Clone,Default)]
+pub struct UserCheck {
+    // message fields
+    pub name: ::std::string::String,
+    pub email: ::std::string::String,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a UserCheck {
+    fn default() -> &'a UserCheck {
+        <UserCheck as ::protobuf::Message>::default_instance()
+    }
+}
+
+impl UserCheck {
+    pub fn new() -> UserCheck {
+        ::std::default::Default::default()
+    }
+
+    // string name = 1;
 
 
     pub fn get_name(&self) -> &str {
@@ -122,9 +270,35 @@ impl App {
     pub fn take_name(&mut self) -> ::std::string::String {
         ::std::mem::replace(&mut self.name, ::std::string::String::new())
     }
+
+    // string email = 2;
+
+
+    pub fn get_email(&self) -> &str {
+        &self.email
+    }
+    pub fn clear_email(&mut self) {
+        self.email.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_email(&mut self, v: ::std::string::String) {
+        self.email = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_email(&mut self) -> &mut ::std::string::String {
+        &mut self.email
+    }
+
+    // Take field
+    pub fn take_email(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.email, ::std::string::String::new())
+    }
 }
 
-impl ::protobuf::Message for App {
+impl ::protobuf::Message for UserCheck {
     fn is_initialized(&self) -> bool {
         true
     }
@@ -134,13 +308,10 @@ impl ::protobuf::Message for App {
             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.id)?;
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.name)?;
                 },
                 2 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.workspace_id)?;
-                },
-                3 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.name)?;
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.email)?;
                 },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@@ -154,14 +325,11 @@ impl ::protobuf::Message for App {
     #[allow(unused_variables)]
     fn compute_size(&self) -> u32 {
         let mut my_size = 0;
-        if !self.id.is_empty() {
-            my_size += ::protobuf::rt::string_size(1, &self.id);
-        }
-        if !self.workspace_id.is_empty() {
-            my_size += ::protobuf::rt::string_size(2, &self.workspace_id);
-        }
         if !self.name.is_empty() {
-            my_size += ::protobuf::rt::string_size(3, &self.name);
+            my_size += ::protobuf::rt::string_size(1, &self.name);
+        }
+        if !self.email.is_empty() {
+            my_size += ::protobuf::rt::string_size(2, &self.email);
         }
         my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
         self.cached_size.set(my_size);
@@ -169,14 +337,11 @@ impl ::protobuf::Message for App {
     }
 
     fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        if !self.id.is_empty() {
-            os.write_string(1, &self.id)?;
-        }
-        if !self.workspace_id.is_empty() {
-            os.write_string(2, &self.workspace_id)?;
-        }
         if !self.name.is_empty() {
-            os.write_string(3, &self.name)?;
+            os.write_string(1, &self.name)?;
+        }
+        if !self.email.is_empty() {
+            os.write_string(2, &self.email)?;
         }
         os.write_unknown_fields(self.get_unknown_fields())?;
         ::std::result::Result::Ok(())
@@ -208,8 +373,8 @@ impl ::protobuf::Message for App {
         Self::descriptor_static()
     }
 
-    fn new() -> App {
-        App::new()
+    fn new() -> UserCheck {
+        UserCheck::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@@ -217,69 +382,68 @@ impl ::protobuf::Message for App {
         descriptor.get(|| {
             let mut fields = ::std::vec::Vec::new();
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
-                "id",
-                |m: &App| { &m.id },
-                |m: &mut App| { &mut m.id },
-            ));
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
-                "workspace_id",
-                |m: &App| { &m.workspace_id },
-                |m: &mut App| { &mut m.workspace_id },
+                "name",
+                |m: &UserCheck| { &m.name },
+                |m: &mut UserCheck| { &mut m.name },
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
-                "name",
-                |m: &App| { &m.name },
-                |m: &mut App| { &mut m.name },
+                "email",
+                |m: &UserCheck| { &m.email },
+                |m: &mut UserCheck| { &mut m.email },
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<App>(
-                "App",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<UserCheck>(
+                "UserCheck",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static App {
-        static instance: ::protobuf::rt::LazyV2<App> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(App::new)
+    fn default_instance() -> &'static UserCheck {
+        static instance: ::protobuf::rt::LazyV2<UserCheck> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(UserCheck::new)
     }
 }
 
-impl ::protobuf::Clear for App {
+impl ::protobuf::Clear for UserCheck {
     fn clear(&mut self) {
-        self.id.clear();
-        self.workspace_id.clear();
         self.name.clear();
+        self.email.clear();
         self.unknown_fields.clear();
     }
 }
 
-impl ::std::fmt::Debug for App {
+impl ::std::fmt::Debug for UserCheck {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for App {
+impl ::protobuf::reflect::ProtobufValue for UserCheck {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\nuser.proto\"L\n\x03App\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\
-    !\n\x0cworkspace_id\x18\x02\x20\x01(\tR\x0bworkspaceId\x12\x12\n\x04name\
-    \x18\x03\x20\x01(\tR\x04nameJ\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\x0b\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\
-    \x12\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\r\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x10\x11\
-    \n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\x1c\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\x17\
-    \n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x1a\x1b\n\x0b\n\x04\x04\0\x02\
-    \x02\x12\x03\x05\x04\x14\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\x0f\n\x0c\n\x05\x04\0\x02\
-    \x02\x03\x12\x03\x05\x12\x13b\x06proto3\
+    \n\nuser.proto\"0\n\x04User\x12\x12\n\x04name\x18\x01\x20\x01(\tR\x04nam\
+    e\x12\x14\n\x05email\x18\x02\x20\x01(\tR\x05email\"5\n\tUserCheck\x12\
+    \x12\n\x04name\x18\x01\x20\x01(\tR\x04name\x12\x14\n\x05email\x18\x02\
+    \x20\x01(\tR\x05emailJ\x9e\x02\n\x06\x12\x04\0\0\t\x01\n\x08\n\x01\x0c\
+    \x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x05\x01\n\n\n\x03\x04\0\
+    \x01\x12\x03\x02\x08\x0c\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x14\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\x0f\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x12\x13\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\n\n\x02\x04\x01\x12\
+    \x04\x06\0\t\x01\n\n\n\x03\x04\x01\x01\x12\x03\x06\x08\x11\n\x0b\n\x04\
+    \x04\x01\x02\0\x12\x03\x07\x04\x14\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\
+    \x07\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x07\x0b\x0f\n\x0c\n\x05\
+    \x04\x01\x02\0\x03\x12\x03\x07\x12\x13\n\x0b\n\x04\x04\x01\x02\x01\x12\
+    \x03\x08\x04\x15\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\x08\x04\n\n\x0c\
+    \n\x05\x04\x01\x02\x01\x01\x12\x03\x08\x0b\x10\n\x0c\n\x05\x04\x01\x02\
+    \x01\x03\x12\x03\x08\x13\x14b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 7 - 4
rust-lib/flowy-user/src/protobuf/proto/user.proto

@@ -1,7 +1,10 @@
 syntax = "proto3";
 
-message App {
-    string id = 1;
-    string workspace_id = 2;
-    string name = 3;
+message User {
+    string name = 1;
+    string email = 2;
+}
+message UserCheck {
+    string name = 1;
+    string email = 2;
 }