add pb generation log

This commit is contained in:
appflowy 2021-12-09 21:39:53 +08:00
parent 909406bf08
commit 3fa0f97e74
20 changed files with 353 additions and 80 deletions

View File

@ -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';

View File

@ -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);
}

View File

@ -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=');

View File

@ -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';

View File

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

View File

@ -41,31 +41,31 @@ impl EditCommandQueue {
};
stream
.for_each(|msg| async {
match self.handle_message(msg).await {
Ok(_) => {},
Err(e) => log::error!("{:?}", e),
}
self.handle_message(msg).await;
})
.await;
}
async fn handle_message(&self, msg: EditCommand) -> Result<(), DocumentError> {
async fn handle_message(&self, msg: EditCommand) {
match msg {
EditCommand::ComposeDelta { delta, ret } => {
let result = self.composed_delta(delta).await;
let _ = ret.send(result);
},
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 } => {
let delta = self.document.write().await.insert(index, data);
@ -110,7 +110,6 @@ impl EditCommandQueue {
let _ = ret.send(Ok(delta));
},
}
Ok(())
}
#[tracing::instrument(level = "debug", skip(self, delta), fields(compose_result), err)]

View File

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

View File

@ -1,5 +1,5 @@
use flowy_test::editor::{EditorScript::*, *};
use lib_ot::revision::RevState;
use lib_ot::{core::DeltaBuilder, revision::RevState, rich_text::RichTextDeltaBuilder};
#[tokio::test]
async fn doc_rev_state_test1() {
@ -37,3 +37,16 @@ async fn doc_rev_state_test2() {
];
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;
}

View File

@ -1,14 +1,32 @@
use crate::{helper::ViewTest, FlowySDKTest};
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::{
core::Interval,
revision::{RevState, Revision, RevisionRange},
revision::{RevState, RevType, Revision, RevisionRange},
rich_text::RichTextDelta,
};
use std::sync::Arc;
use std::{str::FromStr, sync::Arc};
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 sdk: FlowySDKTest,
pub editor: Arc<ClientDocEditor>,
@ -71,12 +89,23 @@ impl EditorTest {
let next_revision = next_revision.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::SimulateAckedMessage(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) => {
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;
}
}

View File

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

View File

@ -115,7 +115,7 @@ impl<'a> ASTField<'a> {
let mut bracket_ty = None;
let mut bracket_category = Some(BracketCategory::Other);
match parse_ty(cx, &field.ty) {
Some(inner) => {
Ok(Some(inner)) => {
match inner.primitive_ty {
PrimitiveTy::Map(map_info) => {
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");
},
Err(e) => {
eprintln!("ASTField parser failed: {:?} with error: {}", field, e);
panic!()
},
}
ASTField {

View File

@ -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 ->
// AngleBracketedGenericArguments -> GenericArgument -> Type.
if let syn::Type::Path(ref p) = ty {
if p.path.segments.len() != 1 {
return None;
return Ok(None);
}
let seg = match p.path.segments.last() {
Some(seg) => seg,
None => return None,
None => return Ok(None),
};
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),
"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 {
return Some(TyInfo {
return Ok(Some(TyInfo {
ident: &seg.ident,
ty,
primitive_ty: PrimitiveTy::Other,
bracket_ty_info: Box::new(None),
});
}));
};
}
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> {
@ -95,21 +95,21 @@ pub fn generate_hashmap_ty_info<'a>(
ty: &'a syn::Type,
path_segment: &'a PathSegment,
bracketed: &'a AngleBracketedGenericArguments,
) -> Option<TyInfo<'a>> {
) -> Result<Option<TyInfo<'a>>, String> {
// The args of map must greater than 2
if bracketed.args.len() != 2 {
return None;
return Ok(None);
}
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,
ty,
primitive_ty: PrimitiveTy::Map(MapInfo::new(key, value)),
bracket_ty_info,
})
}))
}
fn generate_option_ty_info<'a>(
@ -117,34 +117,34 @@ fn generate_option_ty_info<'a>(
ty: &'a syn::Type,
path_segment: &'a PathSegment,
bracketed: &'a AngleBracketedGenericArguments,
) -> Option<TyInfo<'a>> {
) -> Result<Option<TyInfo<'a>>, String> {
assert_eq!(path_segment.ident.to_string(), "Option".to_string());
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,
ty,
primitive_ty: PrimitiveTy::Opt,
bracket_ty_info,
})
}))
}
fn generate_vec_ty_info<'a>(
ctxt: &Ctxt,
path_segment: &'a PathSegment,
bracketed: &'a AngleBracketedGenericArguments,
) -> Option<TyInfo<'a>> {
) -> Result<Option<TyInfo<'a>>, String> {
if bracketed.args.len() != 1 {
return None;
return Ok(None);
}
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,
ty: bracketed_type,
primitive_ty: PrimitiveTy::Vec,
bracket_ty_info: bracketed_ty_info,
});
}));
}
None
Ok(None)
}

View File

@ -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> {
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) {
TypeCategory::Array => {
assert_bracket_ty_is_some(ctxt, &ty_info);

View File

@ -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> {
let member = &field.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 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> {
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) {
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),
@ -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
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) {
TypeCategory::Protobuf => Some(quote! {
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> {
// The key of the hashmap must be string
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) {
TypeCategory::Protobuf => {
let value_type = ty_info.ident;

View File

@ -29,6 +29,15 @@ impl std::default::Default for WsDataType {
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)]
pub struct WsDocumentData {
#[pb(index = 1)]
@ -39,6 +48,8 @@ pub struct WsDocumentData {
#[pb(index = 3)]
pub data: Vec<u8>,
/* #[pb(index = 4)]
* pub user: WsDocumentUser, */
}
impl std::convert::From<Revision> for WsDocumentData {

View File

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

View File

@ -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()
})
}

View File

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

View File

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

View File

@ -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 RichTextDeltaBuilder = DeltaBuilder<RichTextAttributes>;