diff --git a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/doc.pb.dart b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/doc.pb.dart index 68a242698a..becf72d5e1 100644 --- a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/doc.pb.dart +++ b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/doc.pb.dart @@ -7,6 +7,7 @@ import 'dart:core' as $core; +import 'package:fixnum/fixnum.dart' as $fixnum; import 'package:protobuf/protobuf.dart' as $pb; class CreateDocParams extends $pb.GeneratedMessage { @@ -74,6 +75,7 @@ class Doc extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Doc', createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id') ..a<$core.List<$core.int>>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', $pb.PbFieldType.OY) + ..aInt64(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'revision') ..hasRequiredFields = false ; @@ -81,6 +83,7 @@ class Doc extends $pb.GeneratedMessage { factory Doc({ $core.String? id, $core.List<$core.int>? data, + $fixnum.Int64? revision, }) { final _result = create(); if (id != null) { @@ -89,6 +92,9 @@ class Doc extends $pb.GeneratedMessage { if (data != null) { _result.data = data; } + if (revision != null) { + _result.revision = revision; + } return _result; } factory Doc.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); @@ -129,49 +135,58 @@ class Doc extends $pb.GeneratedMessage { $core.bool hasData() => $_has(1); @$pb.TagNumber(2) void clearData() => clearField(2); + + @$pb.TagNumber(3) + $fixnum.Int64 get revision => $_getI64(2); + @$pb.TagNumber(3) + set revision($fixnum.Int64 v) { $_setInt64(2, v); } + @$pb.TagNumber(3) + $core.bool hasRevision() => $_has(2); + @$pb.TagNumber(3) + void clearRevision() => clearField(3); } -class SaveDocParams extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SaveDocParams', createEmptyInstance: create) +class UpdateDocParams extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UpdateDocParams', createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id') - ..a<$core.List<$core.int>>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'docData', $pb.PbFieldType.OY) ..hasRequiredFields = false ; - SaveDocParams._() : super(); - factory SaveDocParams({ + UpdateDocParams._() : super(); + factory UpdateDocParams({ $core.String? id, - $core.List<$core.int>? data, + $core.List<$core.int>? docData, }) { final _result = create(); if (id != null) { _result.id = id; } - if (data != null) { - _result.data = data; + if (docData != null) { + _result.docData = docData; } return _result; } - factory SaveDocParams.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory SaveDocParams.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + factory UpdateDocParams.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory UpdateDocParams.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') - SaveDocParams clone() => SaveDocParams()..mergeFromMessage(this); + UpdateDocParams clone() => UpdateDocParams()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - SaveDocParams copyWith(void Function(SaveDocParams) updates) => super.copyWith((message) => updates(message as SaveDocParams)) as SaveDocParams; // ignore: deprecated_member_use + UpdateDocParams copyWith(void Function(UpdateDocParams) updates) => super.copyWith((message) => updates(message as UpdateDocParams)) as UpdateDocParams; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') - static SaveDocParams create() => SaveDocParams._(); - SaveDocParams createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); + static UpdateDocParams create() => UpdateDocParams._(); + UpdateDocParams createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') - static SaveDocParams getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static SaveDocParams? _defaultInstance; + static UpdateDocParams getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static UpdateDocParams? _defaultInstance; @$pb.TagNumber(1) $core.String get id => $_getSZ(0); @@ -183,24 +198,24 @@ class SaveDocParams extends $pb.GeneratedMessage { void clearId() => clearField(1); @$pb.TagNumber(2) - $core.List<$core.int> get data => $_getN(1); + $core.List<$core.int> get docData => $_getN(1); @$pb.TagNumber(2) - set data($core.List<$core.int> v) { $_setBytes(1, v); } + set docData($core.List<$core.int> v) { $_setBytes(1, v); } @$pb.TagNumber(2) - $core.bool hasData() => $_has(1); + $core.bool hasDocData() => $_has(1); @$pb.TagNumber(2) - void clearData() => clearField(2); + void clearDocData() => clearField(2); } -class ApplyChangesetParams extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ApplyChangesetParams', createEmptyInstance: create) +class DocChangeset extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'DocChangeset', createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id') ..a<$core.List<$core.int>>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', $pb.PbFieldType.OY) ..hasRequiredFields = false ; - ApplyChangesetParams._() : super(); - factory ApplyChangesetParams({ + DocChangeset._() : super(); + factory DocChangeset({ $core.String? id, $core.List<$core.int>? data, }) { @@ -213,26 +228,26 @@ class ApplyChangesetParams extends $pb.GeneratedMessage { } return _result; } - factory ApplyChangesetParams.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory ApplyChangesetParams.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + factory DocChangeset.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory DocChangeset.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') - ApplyChangesetParams clone() => ApplyChangesetParams()..mergeFromMessage(this); + DocChangeset clone() => DocChangeset()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - ApplyChangesetParams copyWith(void Function(ApplyChangesetParams) updates) => super.copyWith((message) => updates(message as ApplyChangesetParams)) as ApplyChangesetParams; // ignore: deprecated_member_use + DocChangeset copyWith(void Function(DocChangeset) updates) => super.copyWith((message) => updates(message as DocChangeset)) as DocChangeset; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') - static ApplyChangesetParams create() => ApplyChangesetParams._(); - ApplyChangesetParams createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); + static DocChangeset create() => DocChangeset._(); + DocChangeset createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') - static ApplyChangesetParams getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static ApplyChangesetParams? _defaultInstance; + static DocChangeset getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static DocChangeset? _defaultInstance; @$pb.TagNumber(1) $core.String get id => $_getSZ(0); diff --git a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/doc.pbjson.dart b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/doc.pbjson.dart index 995d23f727..26d95e5320 100644 --- a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/doc.pbjson.dart +++ b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/doc.pbjson.dart @@ -25,33 +25,34 @@ const Doc$json = const { '2': const [ const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, const {'1': 'data', '3': 2, '4': 1, '5': 12, '10': 'data'}, + const {'1': 'revision', '3': 3, '4': 1, '5': 3, '10': 'revision'}, ], }; /// Descriptor for `Doc`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List docDescriptor = $convert.base64Decode('CgNEb2MSDgoCaWQYASABKAlSAmlkEhIKBGRhdGEYAiABKAxSBGRhdGE='); -@$core.Deprecated('Use saveDocParamsDescriptor instead') -const SaveDocParams$json = const { - '1': 'SaveDocParams', +final $typed_data.Uint8List docDescriptor = $convert.base64Decode('CgNEb2MSDgoCaWQYASABKAlSAmlkEhIKBGRhdGEYAiABKAxSBGRhdGESGgoIcmV2aXNpb24YAyABKANSCHJldmlzaW9u'); +@$core.Deprecated('Use updateDocParamsDescriptor instead') +const UpdateDocParams$json = const { + '1': 'UpdateDocParams', + '2': const [ + const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, + const {'1': 'doc_data', '3': 2, '4': 1, '5': 12, '10': 'docData'}, + ], +}; + +/// Descriptor for `UpdateDocParams`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List updateDocParamsDescriptor = $convert.base64Decode('Cg9VcGRhdGVEb2NQYXJhbXMSDgoCaWQYASABKAlSAmlkEhkKCGRvY19kYXRhGAIgASgMUgdkb2NEYXRh'); +@$core.Deprecated('Use docChangesetDescriptor instead') +const DocChangeset$json = const { + '1': 'DocChangeset', '2': const [ const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, const {'1': 'data', '3': 2, '4': 1, '5': 12, '10': 'data'}, ], }; -/// Descriptor for `SaveDocParams`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List saveDocParamsDescriptor = $convert.base64Decode('Cg1TYXZlRG9jUGFyYW1zEg4KAmlkGAEgASgJUgJpZBISCgRkYXRhGAIgASgMUgRkYXRh'); -@$core.Deprecated('Use applyChangesetParamsDescriptor instead') -const ApplyChangesetParams$json = const { - '1': 'ApplyChangesetParams', - '2': const [ - const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, - const {'1': 'data', '3': 2, '4': 1, '5': 12, '10': 'data'}, - ], -}; - -/// Descriptor for `ApplyChangesetParams`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List applyChangesetParamsDescriptor = $convert.base64Decode('ChRBcHBseUNoYW5nZXNldFBhcmFtcxIOCgJpZBgBIAEoCVICaWQSEgoEZGF0YRgCIAEoDFIEZGF0YQ=='); +/// Descriptor for `DocChangeset`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List docChangesetDescriptor = $convert.base64Decode('CgxEb2NDaGFuZ2VzZXQSDgoCaWQYASABKAlSAmlkEhIKBGRhdGEYAiABKAxSBGRhdGE='); @$core.Deprecated('Use queryDocParamsDescriptor instead') const QueryDocParams$json = const { '1': 'QueryDocParams', diff --git a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/errors.pbenum.dart b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/errors.pbenum.dart index 55e51b76c7..99417e0ce8 100644 --- a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/errors.pbenum.dart +++ b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/errors.pbenum.dart @@ -13,6 +13,9 @@ class ErrorCode extends $pb.ProtobufEnum { static const ErrorCode DocIdInvalid = ErrorCode._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DocIdInvalid'); static const ErrorCode DocNotfound = ErrorCode._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DocNotfound'); static const ErrorCode WsConnectError = ErrorCode._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WsConnectError'); + static const ErrorCode UndoFail = ErrorCode._(200, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UndoFail'); + static const ErrorCode RedoFail = ErrorCode._(201, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'RedoFail'); + static const ErrorCode OutOfBound = ErrorCode._(202, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'OutOfBound'); static const ErrorCode UserUnauthorized = ErrorCode._(999, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserUnauthorized'); static const ErrorCode InternalError = ErrorCode._(1000, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'InternalError'); @@ -20,6 +23,9 @@ class ErrorCode extends $pb.ProtobufEnum { DocIdInvalid, DocNotfound, WsConnectError, + UndoFail, + RedoFail, + OutOfBound, UserUnauthorized, InternalError, ]; diff --git a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/errors.pbjson.dart b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/errors.pbjson.dart index 67e75c3fcd..d6a4cfcc24 100644 --- a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/errors.pbjson.dart +++ b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/errors.pbjson.dart @@ -15,13 +15,16 @@ const ErrorCode$json = const { const {'1': 'DocIdInvalid', '2': 0}, const {'1': 'DocNotfound', '2': 1}, const {'1': 'WsConnectError', '2': 10}, + const {'1': 'UndoFail', '2': 200}, + const {'1': 'RedoFail', '2': 201}, + const {'1': 'OutOfBound', '2': 202}, const {'1': 'UserUnauthorized', '2': 999}, const {'1': 'InternalError', '2': 1000}, ], }; /// Descriptor for `ErrorCode`. Decode as a `google.protobuf.EnumDescriptorProto`. -final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode('CglFcnJvckNvZGUSEAoMRG9jSWRJbnZhbGlkEAASDwoLRG9jTm90Zm91bmQQARISCg5Xc0Nvbm5lY3RFcnJvchAKEhUKEFVzZXJVbmF1dGhvcml6ZWQQ5wcSEgoNSW50ZXJuYWxFcnJvchDoBw=='); +final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode('CglFcnJvckNvZGUSEAoMRG9jSWRJbnZhbGlkEAASDwoLRG9jTm90Zm91bmQQARISCg5Xc0Nvbm5lY3RFcnJvchAKEg0KCFVuZG9GYWlsEMgBEg0KCFJlZG9GYWlsEMkBEg8KCk91dE9mQm91bmQQygESFQoQVXNlclVuYXV0aG9yaXplZBDnBxISCg1JbnRlcm5hbEVycm9yEOgH'); @$core.Deprecated('Use docErrorDescriptor instead') const DocError$json = const { '1': 'DocError', diff --git a/rust-lib/flowy-database/migrations/2021-07-22-234458_flowy-editor/up.sql b/rust-lib/flowy-database/migrations/2021-07-22-234458_flowy-editor/up.sql index a7d4df0651..d3247e7880 100644 --- a/rust-lib/flowy-database/migrations/2021-07-22-234458_flowy-editor/up.sql +++ b/rust-lib/flowy-database/migrations/2021-07-22-234458_flowy-editor/up.sql @@ -2,5 +2,5 @@ CREATE TABLE doc_table ( id TEXT NOT NULL PRIMARY KEY, data BLOB NOT NULL DEFAULT (x''), - version BIGINT NOT NULL DEFAULT 0 + revision BIGINT NOT NULL DEFAULT 0 ); \ No newline at end of file diff --git a/rust-lib/flowy-database/migrations/2021-09-22-074638_flowy-doc-op/down.sql b/rust-lib/flowy-database/migrations/2021-09-22-074638_flowy-doc-op/down.sql new file mode 100644 index 0000000000..291a97c5ce --- /dev/null +++ b/rust-lib/flowy-database/migrations/2021-09-22-074638_flowy-doc-op/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/rust-lib/flowy-database/migrations/2021-09-22-074638_flowy-doc-op/up.sql b/rust-lib/flowy-database/migrations/2021-09-22-074638_flowy-doc-op/up.sql new file mode 100644 index 0000000000..33cf74b578 --- /dev/null +++ b/rust-lib/flowy-database/migrations/2021-09-22-074638_flowy-doc-op/up.sql @@ -0,0 +1 @@ +-- Your SQL goes here \ No newline at end of file diff --git a/rust-lib/flowy-database/src/schema.rs b/rust-lib/flowy-database/src/schema.rs index ee2f8a4a18..4f2b9cae9f 100644 --- a/rust-lib/flowy-database/src/schema.rs +++ b/rust-lib/flowy-database/src/schema.rs @@ -17,7 +17,7 @@ table! { doc_table (id) { id -> Text, data -> Binary, - version -> BigInt, + revision -> BigInt, } } @@ -58,4 +58,10 @@ table! { } } -allow_tables_to_appear_in_same_query!(app_table, doc_table, user_table, view_table, workspace_table,); +allow_tables_to_appear_in_same_query!( + app_table, + doc_table, + user_table, + view_table, + workspace_table, +); diff --git a/rust-lib/flowy-derive/src/derive_cache/derive_cache.rs b/rust-lib/flowy-derive/src/derive_cache/derive_cache.rs index 1e492bf0c6..dd54731a3e 100644 --- a/rust-lib/flowy-derive/src/derive_cache/derive_cache.rs +++ b/rust-lib/flowy-derive/src/derive_cache/derive_cache.rs @@ -57,8 +57,8 @@ pub fn category_from_str(type_str: &str) -> TypeCategory { | "WsMessage" | "CreateDocParams" | "Doc" - | "SaveDocParams" - | "ApplyChangesetParams" + | "UpdateDocParams" + | "DocChangeset" | "QueryDocParams" | "WsDocumentData" | "DocError" diff --git a/rust-lib/flowy-document/Cargo.toml b/rust-lib/flowy-document/Cargo.toml index 374035f7a8..3f5ad07d94 100644 --- a/rust-lib/flowy-document/Cargo.toml +++ b/rust-lib/flowy-document/Cargo.toml @@ -30,9 +30,19 @@ strum = "0.21" strum_macros = "0.21" dashmap = "4.0" parking_lot = "0.11" +bytecount = "0.6.0" +url = "2.2" +serde = { version = "1.0", features = ["derive"] } +serde_json = {version = "1.0"} +chrono = "0.4.19" [dev-dependencies] flowy-test = { path = "../flowy-test" } +color-eyre = { version = "0.5", default-features = false } +criterion = "0.3" +rand = "0.7.3" +env_logger = "0.8.2" + [features] http_server = [] \ No newline at end of file diff --git a/rust-lib/flowy-document/src/entities/doc/doc.rs b/rust-lib/flowy-document/src/entities/doc/doc.rs index 5ee1ff7b4a..c8e17c1216 100644 --- a/rust-lib/flowy-document/src/entities/doc/doc.rs +++ b/rust-lib/flowy-document/src/entities/doc/doc.rs @@ -20,19 +20,22 @@ pub struct Doc { #[pb(index = 2)] pub data: Vec, + + #[pb(index = 3)] + pub revision: i64, } #[derive(ProtoBuf, Default, Debug, Clone)] -pub struct SaveDocParams { +pub struct UpdateDocParams { #[pb(index = 1)] pub id: String, #[pb(index = 2)] - pub data: Vec, + pub doc_data: Vec, } #[derive(ProtoBuf, Default, Debug, Clone)] -pub struct ApplyChangesetParams { +pub struct DocChangeset { #[pb(index = 1)] pub id: String, diff --git a/rust-lib/flowy-document/src/errors.rs b/rust-lib/flowy-document/src/errors.rs index 599d73ac5e..45d3015b57 100644 --- a/rust-lib/flowy-document/src/errors.rs +++ b/rust-lib/flowy-document/src/errors.rs @@ -41,6 +41,9 @@ impl DocError { static_doc_error!(not_found, ErrorCode::DocNotfound); static_doc_error!(unauthorized, ErrorCode::UserUnauthorized); static_doc_error!(ws, ErrorCode::WsConnectError); + static_doc_error!(undo, ErrorCode::UndoFail); + static_doc_error!(redo, ErrorCode::RedoFail); + static_doc_error!(out_of_bound, ErrorCode::OutOfBound); } pub fn internal_error(e: T) -> DocError @@ -61,6 +64,14 @@ pub enum ErrorCode { #[display(fmt = "Document websocket error")] WsConnectError = 10, + #[display(fmt = "Undo failed")] + UndoFail = 200, + #[display(fmt = "Redo failed")] + RedoFail = 201, + + #[display(fmt = "Interval out of bound")] + OutOfBound = 202, + #[display(fmt = "UserUnauthorized")] UserUnauthorized = 999, @@ -89,6 +100,10 @@ impl std::convert::From for DocError { fn from(error: std::io::Error) -> Self { DocError::internal().context(error) } } +impl std::convert::From for DocError { + fn from(error: serde_json::Error) -> Self { DocError::internal().context(error) } +} + // impl std::convert::From<::r2d2::Error> for DocError { // fn from(error: r2d2::Error) -> Self { // ErrorBuilder::new(ErrorCode::InternalError).error(error).build() } } diff --git a/rust-lib/flowy-document/src/lib.rs b/rust-lib/flowy-document/src/lib.rs index a62c9488a9..470f52a80e 100644 --- a/rust-lib/flowy-document/src/lib.rs +++ b/rust-lib/flowy-document/src/lib.rs @@ -3,7 +3,7 @@ pub mod errors; pub mod module; mod observable; pub mod protobuf; -mod services; +pub mod services; mod sql_tables; #[macro_use] diff --git a/rust-lib/flowy-document/src/module.rs b/rust-lib/flowy-document/src/module.rs index 2f722f3f73..97282ce74d 100644 --- a/rust-lib/flowy-document/src/module.rs +++ b/rust-lib/flowy-document/src/module.rs @@ -1,7 +1,7 @@ use crate::{ - entities::doc::{ApplyChangesetParams, CreateDocParams, Doc, QueryDocParams}, + entities::doc::{CreateDocParams, Doc, DocChangeset, QueryDocParams}, errors::DocError, - services::{doc_controller::DocController, open_doc::OpenedDocManager, server::construct_doc_server, ws::WsManager}, + services::{doc_controller::DocController, server::construct_doc_server, ws::WsManager}, }; use bytes::Bytes; use diesel::SqliteConnection; @@ -17,16 +17,13 @@ pub trait DocumentUser: Send + Sync { pub struct FlowyDocument { controller: Arc, - doc_manager: Arc, } impl FlowyDocument { pub fn new(user: Arc, ws_manager: Arc>) -> FlowyDocument { let server = construct_doc_server(); - let controller = Arc::new(DocController::new(server.clone(), user.clone())); - let doc_manager = Arc::new(OpenedDocManager::new(ws_manager, controller.clone())); - - Self { controller, doc_manager } + let controller = Arc::new(DocController::new(server.clone(), user.clone(), ws_manager.clone())); + Self { controller } } pub fn create(&self, params: CreateDocParams, conn: &SqliteConnection) -> Result<(), DocError> { @@ -35,31 +32,23 @@ impl FlowyDocument { } pub fn delete(&self, params: QueryDocParams, conn: &SqliteConnection) -> Result<(), DocError> { - let _ = self.doc_manager.close(¶ms.doc_id)?; - let _ = self.controller.delete(params.into(), conn)?; + let _ = self.controller.delete(params, conn)?; Ok(()) } pub async fn open(&self, params: QueryDocParams, pool: Arc) -> Result { - let doc = match self.doc_manager.is_opened(¶ms.doc_id) { - true => { - let data = self.doc_manager.read_doc(¶ms.doc_id).await?; - Doc { id: params.doc_id, data } - }, - false => { - let doc = self.controller.open(params, pool).await?; - let _ = self.doc_manager.open(&doc.id, doc.data.clone())?; - doc - }, - }; - - Ok(doc) + let open_doc = self.controller.open(params, pool).await?; + Ok(open_doc.doc()) } - pub async fn apply_changeset(&self, params: ApplyChangesetParams, pool: Arc) -> Result { - let _ = self.doc_manager.apply_changeset(¶ms.id, Bytes::from(params.data), pool).await?; - let data = self.doc_manager.read_doc(¶ms.id).await?; - let doc = Doc { id: params.id, data }; - Ok(doc) + pub async fn apply_changeset(&self, params: DocChangeset, pool: Arc) -> Result { + // let _ = self.doc_manager.apply_changeset(¶ms.id, + // Bytes::from(params.data), pool).await?; + // + // // workaround: compare the rust's delta with flutter's delta. Will be removed + // // very soon + // let doc = self.doc_manager.read_doc(¶ms.id)?; + // Ok(doc) + unimplemented!() } } diff --git a/rust-lib/flowy-document/src/protobuf/model/doc.rs b/rust-lib/flowy-document/src/protobuf/model/doc.rs index d6b25da555..d917d3c0bd 100644 --- a/rust-lib/flowy-document/src/protobuf/model/doc.rs +++ b/rust-lib/flowy-document/src/protobuf/model/doc.rs @@ -229,6 +229,7 @@ pub struct Doc { // message fields pub id: ::std::string::String, pub data: ::std::vec::Vec, + pub revision: i64, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, @@ -296,6 +297,21 @@ impl Doc { pub fn take_data(&mut self) -> ::std::vec::Vec { ::std::mem::replace(&mut self.data, ::std::vec::Vec::new()) } + + // int64 revision = 3; + + + pub fn get_revision(&self) -> i64 { + self.revision + } + pub fn clear_revision(&mut self) { + self.revision = 0; + } + + // Param is passed by value, moved + pub fn set_revision(&mut self, v: i64) { + self.revision = v; + } } impl ::protobuf::Message for Doc { @@ -313,6 +329,13 @@ impl ::protobuf::Message for Doc { 2 => { ::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.data)?; }, + 3 => { + if wire_type != ::protobuf::wire_format::WireTypeVarint { + return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); + } + let tmp = is.read_int64()?; + self.revision = tmp; + }, _ => { ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; }, @@ -331,6 +354,9 @@ impl ::protobuf::Message for Doc { if !self.data.is_empty() { my_size += ::protobuf::rt::bytes_size(2, &self.data); } + if self.revision != 0 { + my_size += ::protobuf::rt::value_size(3, self.revision, ::protobuf::wire_format::WireTypeVarint); + } my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); my_size @@ -343,6 +369,9 @@ impl ::protobuf::Message for Doc { if !self.data.is_empty() { os.write_bytes(2, &self.data)?; } + if self.revision != 0 { + os.write_int64(3, self.revision)?; + } os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) } @@ -391,6 +420,11 @@ impl ::protobuf::Message for Doc { |m: &Doc| { &m.data }, |m: &mut Doc| { &mut m.data }, )); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt64>( + "revision", + |m: &Doc| { &m.revision }, + |m: &mut Doc| { &mut m.revision }, + )); ::protobuf::reflect::MessageDescriptor::new_pb_name::( "Doc", fields, @@ -409,6 +443,7 @@ impl ::protobuf::Clear for Doc { fn clear(&mut self) { self.id.clear(); self.data.clear(); + self.revision = 0; self.unknown_fields.clear(); } } @@ -426,23 +461,23 @@ impl ::protobuf::reflect::ProtobufValue for Doc { } #[derive(PartialEq,Clone,Default)] -pub struct SaveDocParams { +pub struct UpdateDocParams { // message fields pub id: ::std::string::String, - pub data: ::std::vec::Vec, + pub doc_data: ::std::vec::Vec, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, } -impl<'a> ::std::default::Default for &'a SaveDocParams { - fn default() -> &'a SaveDocParams { - ::default_instance() +impl<'a> ::std::default::Default for &'a UpdateDocParams { + fn default() -> &'a UpdateDocParams { + ::default_instance() } } -impl SaveDocParams { - pub fn new() -> SaveDocParams { +impl UpdateDocParams { + pub fn new() -> UpdateDocParams { ::std::default::Default::default() } @@ -472,34 +507,34 @@ impl SaveDocParams { ::std::mem::replace(&mut self.id, ::std::string::String::new()) } - // bytes data = 2; + // bytes doc_data = 2; - pub fn get_data(&self) -> &[u8] { - &self.data + pub fn get_doc_data(&self) -> &[u8] { + &self.doc_data } - pub fn clear_data(&mut self) { - self.data.clear(); + pub fn clear_doc_data(&mut self) { + self.doc_data.clear(); } // Param is passed by value, moved - pub fn set_data(&mut self, v: ::std::vec::Vec) { - self.data = v; + pub fn set_doc_data(&mut self, v: ::std::vec::Vec) { + self.doc_data = v; } // Mutable pointer to the field. // If field is not initialized, it is initialized with default value first. - pub fn mut_data(&mut self) -> &mut ::std::vec::Vec { - &mut self.data + pub fn mut_doc_data(&mut self) -> &mut ::std::vec::Vec { + &mut self.doc_data } // Take field - pub fn take_data(&mut self) -> ::std::vec::Vec { - ::std::mem::replace(&mut self.data, ::std::vec::Vec::new()) + pub fn take_doc_data(&mut self) -> ::std::vec::Vec { + ::std::mem::replace(&mut self.doc_data, ::std::vec::Vec::new()) } } -impl ::protobuf::Message for SaveDocParams { +impl ::protobuf::Message for UpdateDocParams { fn is_initialized(&self) -> bool { true } @@ -512,7 +547,7 @@ impl ::protobuf::Message for SaveDocParams { ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.id)?; }, 2 => { - ::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.data)?; + ::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.doc_data)?; }, _ => { ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; @@ -529,8 +564,8 @@ impl ::protobuf::Message for SaveDocParams { if !self.id.is_empty() { my_size += ::protobuf::rt::string_size(1, &self.id); } - if !self.data.is_empty() { - my_size += ::protobuf::rt::bytes_size(2, &self.data); + if !self.doc_data.is_empty() { + my_size += ::protobuf::rt::bytes_size(2, &self.doc_data); } my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); @@ -541,8 +576,8 @@ impl ::protobuf::Message for SaveDocParams { if !self.id.is_empty() { os.write_string(1, &self.id)?; } - if !self.data.is_empty() { - os.write_bytes(2, &self.data)?; + if !self.doc_data.is_empty() { + os.write_bytes(2, &self.doc_data)?; } os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) @@ -574,8 +609,8 @@ impl ::protobuf::Message for SaveDocParams { Self::descriptor_static() } - fn new() -> SaveDocParams { - SaveDocParams::new() + fn new() -> UpdateDocParams { + UpdateDocParams::new() } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { @@ -584,50 +619,50 @@ impl ::protobuf::Message for SaveDocParams { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( "id", - |m: &SaveDocParams| { &m.id }, - |m: &mut SaveDocParams| { &mut m.id }, + |m: &UpdateDocParams| { &m.id }, + |m: &mut UpdateDocParams| { &mut m.id }, )); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>( - "data", - |m: &SaveDocParams| { &m.data }, - |m: &mut SaveDocParams| { &mut m.data }, + "doc_data", + |m: &UpdateDocParams| { &m.doc_data }, + |m: &mut UpdateDocParams| { &mut m.doc_data }, )); - ::protobuf::reflect::MessageDescriptor::new_pb_name::( - "SaveDocParams", + ::protobuf::reflect::MessageDescriptor::new_pb_name::( + "UpdateDocParams", fields, file_descriptor_proto() ) }) } - fn default_instance() -> &'static SaveDocParams { - static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; - instance.get(SaveDocParams::new) + fn default_instance() -> &'static UpdateDocParams { + static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; + instance.get(UpdateDocParams::new) } } -impl ::protobuf::Clear for SaveDocParams { +impl ::protobuf::Clear for UpdateDocParams { fn clear(&mut self) { self.id.clear(); - self.data.clear(); + self.doc_data.clear(); self.unknown_fields.clear(); } } -impl ::std::fmt::Debug for SaveDocParams { +impl ::std::fmt::Debug for UpdateDocParams { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } -impl ::protobuf::reflect::ProtobufValue for SaveDocParams { +impl ::protobuf::reflect::ProtobufValue for UpdateDocParams { fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { ::protobuf::reflect::ReflectValueRef::Message(self) } } #[derive(PartialEq,Clone,Default)] -pub struct ApplyChangesetParams { +pub struct DocChangeset { // message fields pub id: ::std::string::String, pub data: ::std::vec::Vec, @@ -636,14 +671,14 @@ pub struct ApplyChangesetParams { pub cached_size: ::protobuf::CachedSize, } -impl<'a> ::std::default::Default for &'a ApplyChangesetParams { - fn default() -> &'a ApplyChangesetParams { - ::default_instance() +impl<'a> ::std::default::Default for &'a DocChangeset { + fn default() -> &'a DocChangeset { + ::default_instance() } } -impl ApplyChangesetParams { - pub fn new() -> ApplyChangesetParams { +impl DocChangeset { + pub fn new() -> DocChangeset { ::std::default::Default::default() } @@ -700,7 +735,7 @@ impl ApplyChangesetParams { } } -impl ::protobuf::Message for ApplyChangesetParams { +impl ::protobuf::Message for DocChangeset { fn is_initialized(&self) -> bool { true } @@ -775,8 +810,8 @@ impl ::protobuf::Message for ApplyChangesetParams { Self::descriptor_static() } - fn new() -> ApplyChangesetParams { - ApplyChangesetParams::new() + fn new() -> DocChangeset { + DocChangeset::new() } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { @@ -785,29 +820,29 @@ impl ::protobuf::Message for ApplyChangesetParams { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( "id", - |m: &ApplyChangesetParams| { &m.id }, - |m: &mut ApplyChangesetParams| { &mut m.id }, + |m: &DocChangeset| { &m.id }, + |m: &mut DocChangeset| { &mut m.id }, )); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>( "data", - |m: &ApplyChangesetParams| { &m.data }, - |m: &mut ApplyChangesetParams| { &mut m.data }, + |m: &DocChangeset| { &m.data }, + |m: &mut DocChangeset| { &mut m.data }, )); - ::protobuf::reflect::MessageDescriptor::new_pb_name::( - "ApplyChangesetParams", + ::protobuf::reflect::MessageDescriptor::new_pb_name::( + "DocChangeset", fields, file_descriptor_proto() ) }) } - fn default_instance() -> &'static ApplyChangesetParams { - static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; - instance.get(ApplyChangesetParams::new) + fn default_instance() -> &'static DocChangeset { + static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; + instance.get(DocChangeset::new) } } -impl ::protobuf::Clear for ApplyChangesetParams { +impl ::protobuf::Clear for DocChangeset { fn clear(&mut self) { self.id.clear(); self.data.clear(); @@ -815,13 +850,13 @@ impl ::protobuf::Clear for ApplyChangesetParams { } } -impl ::std::fmt::Debug for ApplyChangesetParams { +impl ::std::fmt::Debug for DocChangeset { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } -impl ::protobuf::reflect::ProtobufValue for ApplyChangesetParams { +impl ::protobuf::reflect::ProtobufValue for DocChangeset { fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { ::protobuf::reflect::ReflectValueRef::Message(self) } @@ -988,44 +1023,48 @@ impl ::protobuf::reflect::ProtobufValue for QueryDocParams { static file_descriptor_proto_data: &'static [u8] = b"\ \n\tdoc.proto\"5\n\x0fCreateDocParams\x12\x0e\n\x02id\x18\x01\x20\x01(\t\ - R\x02id\x12\x12\n\x04data\x18\x02\x20\x01(\x0cR\x04data\")\n\x03Doc\x12\ + R\x02id\x12\x12\n\x04data\x18\x02\x20\x01(\x0cR\x04data\"E\n\x03Doc\x12\ \x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x12\n\x04data\x18\x02\x20\x01\ - (\x0cR\x04data\"3\n\rSaveDocParams\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\ - \x02id\x12\x12\n\x04data\x18\x02\x20\x01(\x0cR\x04data\":\n\x14ApplyChan\ - gesetParams\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x12\n\x04data\ - \x18\x02\x20\x01(\x0cR\x04data\"'\n\x0eQueryDocParams\x12\x15\n\x06doc_i\ - d\x18\x01\x20\x01(\tR\x05docIdJ\xf9\x04\n\x06\x12\x04\0\0\x14\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\x17\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\x13\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\ - \x0e\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x11\x12\n\n\n\x02\x04\x01\ - \x12\x04\x06\0\t\x01\n\n\n\x03\x04\x01\x01\x12\x03\x06\x08\x0b\n\x0b\n\ - \x04\x04\x01\x02\0\x12\x03\x07\x04\x12\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\r\n\x0c\n\ - \x05\x04\x01\x02\0\x03\x12\x03\x07\x10\x11\n\x0b\n\x04\x04\x01\x02\x01\ - \x12\x03\x08\x04\x13\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\x08\x04\t\n\ - \x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\x08\n\x0e\n\x0c\n\x05\x04\x01\x02\ - \x01\x03\x12\x03\x08\x11\x12\n\n\n\x02\x04\x02\x12\x04\n\0\r\x01\n\n\n\ - \x03\x04\x02\x01\x12\x03\n\x08\x15\n\x0b\n\x04\x04\x02\x02\0\x12\x03\x0b\ - \x04\x12\n\x0c\n\x05\x04\x02\x02\0\x05\x12\x03\x0b\x04\n\n\x0c\n\x05\x04\ - \x02\x02\0\x01\x12\x03\x0b\x0b\r\n\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\ - \x0b\x10\x11\n\x0b\n\x04\x04\x02\x02\x01\x12\x03\x0c\x04\x13\n\x0c\n\x05\ - \x04\x02\x02\x01\x05\x12\x03\x0c\x04\t\n\x0c\n\x05\x04\x02\x02\x01\x01\ - \x12\x03\x0c\n\x0e\n\x0c\n\x05\x04\x02\x02\x01\x03\x12\x03\x0c\x11\x12\n\ - \n\n\x02\x04\x03\x12\x04\x0e\0\x11\x01\n\n\n\x03\x04\x03\x01\x12\x03\x0e\ - \x08\x1c\n\x0b\n\x04\x04\x03\x02\0\x12\x03\x0f\x04\x12\n\x0c\n\x05\x04\ - \x03\x02\0\x05\x12\x03\x0f\x04\n\n\x0c\n\x05\x04\x03\x02\0\x01\x12\x03\ - \x0f\x0b\r\n\x0c\n\x05\x04\x03\x02\0\x03\x12\x03\x0f\x10\x11\n\x0b\n\x04\ - \x04\x03\x02\x01\x12\x03\x10\x04\x13\n\x0c\n\x05\x04\x03\x02\x01\x05\x12\ - \x03\x10\x04\t\n\x0c\n\x05\x04\x03\x02\x01\x01\x12\x03\x10\n\x0e\n\x0c\n\ - \x05\x04\x03\x02\x01\x03\x12\x03\x10\x11\x12\n\n\n\x02\x04\x04\x12\x04\ - \x12\0\x14\x01\n\n\n\x03\x04\x04\x01\x12\x03\x12\x08\x16\n\x0b\n\x04\x04\ - \x04\x02\0\x12\x03\x13\x04\x16\n\x0c\n\x05\x04\x04\x02\0\x05\x12\x03\x13\ - \x04\n\n\x0c\n\x05\x04\x04\x02\0\x01\x12\x03\x13\x0b\x11\n\x0c\n\x05\x04\ - \x04\x02\0\x03\x12\x03\x13\x14\x15b\x06proto3\ + (\x0cR\x04data\x12\x1a\n\x08revision\x18\x03\x20\x01(\x03R\x08revision\"\ + <\n\x0fUpdateDocParams\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x19\ + \n\x08doc_data\x18\x02\x20\x01(\x0cR\x07docData\"2\n\x0cDocChangeset\x12\ + \x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x12\n\x04data\x18\x02\x20\x01\ + (\x0cR\x04data\"'\n\x0eQueryDocParams\x12\x15\n\x06doc_id\x18\x01\x20\ + \x01(\tR\x05docIdJ\xb0\x05\n\x06\x12\x04\0\0\x15\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\x17\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\x13\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\x0e\n\x0c\n\ + \x05\x04\0\x02\x01\x03\x12\x03\x04\x11\x12\n\n\n\x02\x04\x01\x12\x04\x06\ + \0\n\x01\n\n\n\x03\x04\x01\x01\x12\x03\x06\x08\x0b\n\x0b\n\x04\x04\x01\ + \x02\0\x12\x03\x07\x04\x12\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\r\n\x0c\n\x05\x04\x01\ + \x02\0\x03\x12\x03\x07\x10\x11\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\x08\ + \x04\x13\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\x08\x04\t\n\x0c\n\x05\ + \x04\x01\x02\x01\x01\x12\x03\x08\n\x0e\n\x0c\n\x05\x04\x01\x02\x01\x03\ + \x12\x03\x08\x11\x12\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\t\x04\x17\n\x0c\ + \n\x05\x04\x01\x02\x02\x05\x12\x03\t\x04\t\n\x0c\n\x05\x04\x01\x02\x02\ + \x01\x12\x03\t\n\x12\n\x0c\n\x05\x04\x01\x02\x02\x03\x12\x03\t\x15\x16\n\ + \n\n\x02\x04\x02\x12\x04\x0b\0\x0e\x01\n\n\n\x03\x04\x02\x01\x12\x03\x0b\ + \x08\x17\n\x0b\n\x04\x04\x02\x02\0\x12\x03\x0c\x04\x12\n\x0c\n\x05\x04\ + \x02\x02\0\x05\x12\x03\x0c\x04\n\n\x0c\n\x05\x04\x02\x02\0\x01\x12\x03\ + \x0c\x0b\r\n\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\x0c\x10\x11\n\x0b\n\x04\ + \x04\x02\x02\x01\x12\x03\r\x04\x17\n\x0c\n\x05\x04\x02\x02\x01\x05\x12\ + \x03\r\x04\t\n\x0c\n\x05\x04\x02\x02\x01\x01\x12\x03\r\n\x12\n\x0c\n\x05\ + \x04\x02\x02\x01\x03\x12\x03\r\x15\x16\n\n\n\x02\x04\x03\x12\x04\x0f\0\ + \x12\x01\n\n\n\x03\x04\x03\x01\x12\x03\x0f\x08\x14\n\x0b\n\x04\x04\x03\ + \x02\0\x12\x03\x10\x04\x12\n\x0c\n\x05\x04\x03\x02\0\x05\x12\x03\x10\x04\ + \n\n\x0c\n\x05\x04\x03\x02\0\x01\x12\x03\x10\x0b\r\n\x0c\n\x05\x04\x03\ + \x02\0\x03\x12\x03\x10\x10\x11\n\x0b\n\x04\x04\x03\x02\x01\x12\x03\x11\ + \x04\x13\n\x0c\n\x05\x04\x03\x02\x01\x05\x12\x03\x11\x04\t\n\x0c\n\x05\ + \x04\x03\x02\x01\x01\x12\x03\x11\n\x0e\n\x0c\n\x05\x04\x03\x02\x01\x03\ + \x12\x03\x11\x11\x12\n\n\n\x02\x04\x04\x12\x04\x13\0\x15\x01\n\n\n\x03\ + \x04\x04\x01\x12\x03\x13\x08\x16\n\x0b\n\x04\x04\x04\x02\0\x12\x03\x14\ + \x04\x16\n\x0c\n\x05\x04\x04\x02\0\x05\x12\x03\x14\x04\n\n\x0c\n\x05\x04\ + \x04\x02\0\x01\x12\x03\x14\x0b\x11\n\x0c\n\x05\x04\x04\x02\0\x03\x12\x03\ + \x14\x14\x15b\x06proto3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/rust-lib/flowy-document/src/protobuf/model/errors.rs b/rust-lib/flowy-document/src/protobuf/model/errors.rs index 9cc9c119ca..e034737dcc 100644 --- a/rust-lib/flowy-document/src/protobuf/model/errors.rs +++ b/rust-lib/flowy-document/src/protobuf/model/errors.rs @@ -218,6 +218,9 @@ pub enum ErrorCode { DocIdInvalid = 0, DocNotfound = 1, WsConnectError = 10, + UndoFail = 200, + RedoFail = 201, + OutOfBound = 202, UserUnauthorized = 999, InternalError = 1000, } @@ -232,6 +235,9 @@ impl ::protobuf::ProtobufEnum for ErrorCode { 0 => ::std::option::Option::Some(ErrorCode::DocIdInvalid), 1 => ::std::option::Option::Some(ErrorCode::DocNotfound), 10 => ::std::option::Option::Some(ErrorCode::WsConnectError), + 200 => ::std::option::Option::Some(ErrorCode::UndoFail), + 201 => ::std::option::Option::Some(ErrorCode::RedoFail), + 202 => ::std::option::Option::Some(ErrorCode::OutOfBound), 999 => ::std::option::Option::Some(ErrorCode::UserUnauthorized), 1000 => ::std::option::Option::Some(ErrorCode::InternalError), _ => ::std::option::Option::None @@ -243,6 +249,9 @@ impl ::protobuf::ProtobufEnum for ErrorCode { ErrorCode::DocIdInvalid, ErrorCode::DocNotfound, ErrorCode::WsConnectError, + ErrorCode::UndoFail, + ErrorCode::RedoFail, + ErrorCode::OutOfBound, ErrorCode::UserUnauthorized, ErrorCode::InternalError, ]; @@ -274,29 +283,36 @@ impl ::protobuf::reflect::ProtobufValue for ErrorCode { static file_descriptor_proto_data: &'static [u8] = b"\ \n\x0cerrors.proto\"<\n\x08DocError\x12\x1e\n\x04code\x18\x01\x20\x01(\ - \x0e2\n.ErrorCodeR\x04code\x12\x10\n\x03msg\x18\x02\x20\x01(\tR\x03msg*m\ - \n\tErrorCode\x12\x10\n\x0cDocIdInvalid\x10\0\x12\x0f\n\x0bDocNotfound\ - \x10\x01\x12\x12\n\x0eWsConnectError\x10\n\x12\x15\n\x10UserUnauthorized\ - \x10\xe7\x07\x12\x12\n\rInternalError\x10\xe8\x07J\xfd\x02\n\x06\x12\x04\ - \0\0\x0c\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\x10\n\x0b\n\x04\x04\0\x02\ - \0\x12\x03\x03\x04\x17\n\x0c\n\x05\x04\0\x02\0\x06\x12\x03\x03\x04\r\n\ - \x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x0e\x12\n\x0c\n\x05\x04\0\x02\0\ - \x03\x12\x03\x03\x15\x16\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\x13\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\x0e\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x11\ - \x12\n\n\n\x02\x05\0\x12\x04\x06\0\x0c\x01\n\n\n\x03\x05\0\x01\x12\x03\ - \x06\x05\x0e\n\x0b\n\x04\x05\0\x02\0\x12\x03\x07\x04\x15\n\x0c\n\x05\x05\ - \0\x02\0\x01\x12\x03\x07\x04\x10\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x07\ - \x13\x14\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x08\x04\x14\n\x0c\n\x05\x05\0\ - \x02\x01\x01\x12\x03\x08\x04\x0f\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\ - \x08\x12\x13\n\x0b\n\x04\x05\0\x02\x02\x12\x03\t\x04\x18\n\x0c\n\x05\x05\ - \0\x02\x02\x01\x12\x03\t\x04\x12\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\t\ - \x15\x17\n\x0b\n\x04\x05\0\x02\x03\x12\x03\n\x04\x1b\n\x0c\n\x05\x05\0\ - \x02\x03\x01\x12\x03\n\x04\x14\n\x0c\n\x05\x05\0\x02\x03\x02\x12\x03\n\ - \x17\x1a\n\x0b\n\x04\x05\0\x02\x04\x12\x03\x0b\x04\x19\n\x0c\n\x05\x05\0\ - \x02\x04\x01\x12\x03\x0b\x04\x11\n\x0c\n\x05\x05\0\x02\x04\x02\x12\x03\ - \x0b\x14\x18b\x06proto3\ + \x0e2\n.ErrorCodeR\x04code\x12\x10\n\x03msg\x18\x02\x20\x01(\tR\x03msg*\ + \x9c\x01\n\tErrorCode\x12\x10\n\x0cDocIdInvalid\x10\0\x12\x0f\n\x0bDocNo\ + tfound\x10\x01\x12\x12\n\x0eWsConnectError\x10\n\x12\r\n\x08UndoFail\x10\ + \xc8\x01\x12\r\n\x08RedoFail\x10\xc9\x01\x12\x0f\n\nOutOfBound\x10\xca\ + \x01\x12\x15\n\x10UserUnauthorized\x10\xe7\x07\x12\x12\n\rInternalError\ + \x10\xe8\x07J\xf8\x03\n\x06\x12\x04\0\0\x0f\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\x10\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x17\n\x0c\n\x05\ + \x04\0\x02\0\x06\x12\x03\x03\x04\r\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\ + \x03\x0e\x12\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x15\x16\n\x0b\n\x04\ + \x04\0\x02\x01\x12\x03\x04\x04\x13\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\x0e\n\x0c\n\x05\ + \x04\0\x02\x01\x03\x12\x03\x04\x11\x12\n\n\n\x02\x05\0\x12\x04\x06\0\x0f\ + \x01\n\n\n\x03\x05\0\x01\x12\x03\x06\x05\x0e\n\x0b\n\x04\x05\0\x02\0\x12\ + \x03\x07\x04\x15\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x07\x04\x10\n\x0c\n\ + \x05\x05\0\x02\0\x02\x12\x03\x07\x13\x14\n\x0b\n\x04\x05\0\x02\x01\x12\ + \x03\x08\x04\x14\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x08\x04\x0f\n\x0c\ + \n\x05\x05\0\x02\x01\x02\x12\x03\x08\x12\x13\n\x0b\n\x04\x05\0\x02\x02\ + \x12\x03\t\x04\x18\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\t\x04\x12\n\x0c\ + \n\x05\x05\0\x02\x02\x02\x12\x03\t\x15\x17\n\x0b\n\x04\x05\0\x02\x03\x12\ + \x03\n\x04\x13\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\n\x04\x0c\n\x0c\n\ + \x05\x05\0\x02\x03\x02\x12\x03\n\x0f\x12\n\x0b\n\x04\x05\0\x02\x04\x12\ + \x03\x0b\x04\x13\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x0b\x04\x0c\n\x0c\ + \n\x05\x05\0\x02\x04\x02\x12\x03\x0b\x0f\x12\n\x0b\n\x04\x05\0\x02\x05\ + \x12\x03\x0c\x04\x15\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\x0c\x04\x0e\n\ + \x0c\n\x05\x05\0\x02\x05\x02\x12\x03\x0c\x11\x14\n\x0b\n\x04\x05\0\x02\ + \x06\x12\x03\r\x04\x1b\n\x0c\n\x05\x05\0\x02\x06\x01\x12\x03\r\x04\x14\n\ + \x0c\n\x05\x05\0\x02\x06\x02\x12\x03\r\x17\x1a\n\x0b\n\x04\x05\0\x02\x07\ + \x12\x03\x0e\x04\x19\n\x0c\n\x05\x05\0\x02\x07\x01\x12\x03\x0e\x04\x11\n\ + \x0c\n\x05\x05\0\x02\x07\x02\x12\x03\x0e\x14\x18b\x06proto3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/rust-lib/flowy-document/src/protobuf/proto/doc.proto b/rust-lib/flowy-document/src/protobuf/proto/doc.proto index 5a6fffcd7c..c81534a93c 100644 --- a/rust-lib/flowy-document/src/protobuf/proto/doc.proto +++ b/rust-lib/flowy-document/src/protobuf/proto/doc.proto @@ -7,12 +7,13 @@ message CreateDocParams { message Doc { string id = 1; bytes data = 2; + int64 revision = 3; } -message SaveDocParams { +message UpdateDocParams { string id = 1; - bytes data = 2; + bytes doc_data = 2; } -message ApplyChangesetParams { +message DocChangeset { string id = 1; bytes data = 2; } diff --git a/rust-lib/flowy-document/src/protobuf/proto/errors.proto b/rust-lib/flowy-document/src/protobuf/proto/errors.proto index 85dcd67b40..354bf3ce1f 100644 --- a/rust-lib/flowy-document/src/protobuf/proto/errors.proto +++ b/rust-lib/flowy-document/src/protobuf/proto/errors.proto @@ -8,6 +8,9 @@ enum ErrorCode { DocIdInvalid = 0; DocNotfound = 1; WsConnectError = 10; + UndoFail = 200; + RedoFail = 201; + OutOfBound = 202; UserUnauthorized = 999; InternalError = 1000; } diff --git a/rust-lib/flowy-document/src/services/cache.rs b/rust-lib/flowy-document/src/services/cache.rs new file mode 100644 index 0000000000..f59974c7a7 --- /dev/null +++ b/rust-lib/flowy-document/src/services/cache.rs @@ -0,0 +1,48 @@ +use crate::{ + entities::doc::Doc, + errors::DocError, + services::{ + open_doc::{DocId, OpenedDoc}, + ws::WsManager, + }, +}; +use bytes::Bytes; +use dashmap::DashMap; +use flowy_database::ConnectionPool; +use flowy_ot::{core::Delta, errors::OTError}; +use parking_lot::RwLock; +use std::{convert::TryInto, fmt::Debug, sync::Arc}; + +pub(crate) struct DocCache { + doc_map: DashMap>, +} + +impl DocCache { + pub(crate) fn new() -> Self { Self { doc_map: DashMap::new() } } + + pub(crate) fn set(&self, doc: Arc) -> Result<(), DocError> { + self.doc_map.insert(doc.id.clone(), doc); + Ok(()) + } + + pub(crate) fn is_opened(&self, doc_id: &str) -> bool { + let doc_id: DocId = doc_id.into(); + self.doc_map.get(&doc_id).is_some() + } + + pub(crate) fn get(&self, doc_id: &str) -> Result, DocError> { + if !self.is_opened(&doc_id) { + return Err(doc_not_found()); + } + let doc_id: DocId = doc_id.into(); + let opened_doc = self.doc_map.get(&doc_id).unwrap(); + Ok(opened_doc.clone()) + } + + pub(crate) fn remove(&self, id: &str) { + let doc_id: DocId = id.into(); + self.doc_map.remove(&doc_id); + } +} + +fn doc_not_found() -> DocError { DocError::not_found().context("Doc is close or you should call open first") } diff --git a/rust-lib/flowy-ot/src/client/document/data.rs b/rust-lib/flowy-document/src/services/doc/document/data.rs similarity index 57% rename from rust-lib/flowy-ot/src/client/document/data.rs rename to rust-lib/flowy-document/src/services/doc/document/data.rs index df5e005307..e3796534f3 100644 --- a/rust-lib/flowy-ot/src/client/document/data.rs +++ b/rust-lib/flowy-document/src/services/doc/document/data.rs @@ -1,8 +1,8 @@ -use crate::{client::DocumentData, errors::OTError}; +use crate::{errors::DocError, services::doc::DocumentData}; use serde::{Deserialize, Serialize}; impl> DocumentData for T { - fn into_string(self) -> Result { Ok(self.as_ref().to_string()) } + fn into_string(self) -> Result { Ok(self.as_ref().to_string()) } } #[derive(Serialize, Deserialize, Debug)] @@ -11,7 +11,7 @@ pub struct ImageData { } impl DocumentData for ImageData { - fn into_string(self) -> Result { + fn into_string(self) -> Result { let s = serde_json::to_string(&self)?; Ok(s) } diff --git a/rust-lib/flowy-ot/src/client/document/document.rs b/rust-lib/flowy-document/src/services/doc/document/document.rs similarity index 81% rename from rust-lib/flowy-ot/src/client/document/document.rs rename to rust-lib/flowy-document/src/services/doc/document/document.rs index e1329aa3b2..081ed43bc5 100644 --- a/rust-lib/flowy-ot/src/client/document/document.rs +++ b/rust-lib/flowy-document/src/services/doc/document/document.rs @@ -1,12 +1,13 @@ use crate::{ - client::{view::View, History, RevId, UndoResult, RECORD_THRESHOLD}, - core::*, - errors::{ErrorBuilder, OTError, OTErrorCode, OTErrorCode::*}, + errors::DocError, + services::doc::{view::View, History, UndoResult, RECORD_THRESHOLD}, }; +use bytes::Bytes; +use flowy_ot::core::*; use std::convert::TryInto; pub trait DocumentData { - fn into_string(self) -> Result; + fn into_string(self) -> Result; } pub trait CustomDocument { @@ -23,6 +24,19 @@ impl CustomDocument for FlowyDoc { fn init_delta() -> Delta { DeltaBuilder::new().insert("\n").build() } } +#[derive(Debug, Clone)] +pub struct RevId(pub usize); + +#[derive(Debug, Clone)] +pub struct Revision { + rev_id: RevId, + pub delta: Delta, +} + +impl Revision { + pub fn new(rev_id: RevId, delta: Delta) -> Revision { Self { rev_id, delta } } +} + pub struct Document { delta: Delta, history: History, @@ -44,7 +58,7 @@ impl Document { } } - pub fn from_json(json: &str) -> Result { + pub fn from_json(json: &str) -> Result { let delta = Delta::from_json(json)?; Ok(Self::from_delta(delta)) } @@ -55,18 +69,20 @@ impl Document { pub fn to_string(&self) -> String { self.delta.apply("").unwrap() } - pub fn apply_changeset(&mut self, changeset: T) -> Result<(), OTError> - where - T: TryInto, - { - let new_delta: Delta = changeset.try_into()?; - log::debug!("Delta changeset: {}", new_delta); + pub fn apply_delta(&mut self, data: Bytes) -> Result<(), DocError> { + let new_delta = Delta::from_bytes(data.to_vec())?; + + log::debug!("Apply delta: {}", new_delta); + + let rev_id = self.next_rev_id(); + let revision = Revision::new(rev_id, new_delta.clone()); + let _ = self.add_delta(&new_delta)?; log::debug!("Document: {}", self.to_json()); Ok(()) } - pub fn insert(&mut self, index: usize, data: T) -> Result { + pub fn insert(&mut self, index: usize, data: T) -> Result { let interval = Interval::new(index, index); let _ = validate_interval(&self.delta, &interval)?; @@ -77,7 +93,7 @@ impl Document { Ok(delta) } - pub fn delete(&mut self, interval: Interval) -> Result { + pub fn delete(&mut self, interval: Interval) -> Result { let _ = validate_interval(&self.delta, &interval)?; debug_assert_eq!(interval.is_empty(), false); let delete = self.view.delete(&self.delta, interval)?; @@ -88,7 +104,7 @@ impl Document { Ok(delete) } - pub fn format(&mut self, interval: Interval, attribute: Attribute) -> Result<(), OTError> { + pub fn format(&mut self, interval: Interval, attribute: Attribute) -> Result<(), DocError> { let _ = validate_interval(&self.delta, &interval)?; log::trace!("format with {} at {}", attribute, interval); let format_delta = self.view.format(&self.delta, attribute.clone(), interval).unwrap(); @@ -98,7 +114,7 @@ impl Document { Ok(()) } - pub fn replace(&mut self, interval: Interval, data: T) -> Result { + pub fn replace(&mut self, interval: Interval, data: T) -> Result { let _ = validate_interval(&self.delta, &interval)?; let mut delta = Delta::default(); let text = data.into_string()?; @@ -120,9 +136,9 @@ impl Document { pub fn can_redo(&self) -> bool { self.history.can_redo() } - pub fn undo(&mut self) -> Result { + pub fn undo(&mut self) -> Result { match self.history.undo() { - None => Err(ErrorBuilder::new(UndoFail).msg("Undo stack is empty").build()), + None => Err(DocError::undo().context("Undo stack is empty")), Some(undo_delta) => { let (new_delta, inverted_delta) = self.invert_change(&undo_delta)?; let result = UndoResult::success(new_delta.target_len as usize); @@ -134,9 +150,9 @@ impl Document { } } - pub fn redo(&mut self) -> Result { + pub fn redo(&mut self) -> Result { match self.history.redo() { - None => Err(ErrorBuilder::new(RedoFail).build()), + None => Err(DocError::redo()), Some(redo_delta) => { let (new_delta, inverted_delta) = self.invert_change(&redo_delta)?; let result = UndoResult::success(new_delta.target_len as usize); @@ -154,7 +170,7 @@ impl Document { } impl Document { - fn add_delta(&mut self, delta: &Delta) -> Result<(), OTError> { + fn add_delta(&mut self, delta: &Delta) -> Result<(), DocError> { let composed_delta = self.delta.compose(delta)?; let mut undo_delta = delta.invert(&self.delta); self.rev_id_counter += 1; @@ -181,7 +197,7 @@ impl Document { Ok(()) } - fn invert_change(&self, change: &Delta) -> Result<(Delta, Delta), OTError> { + fn invert_change(&self, change: &Delta) -> Result<(Delta, Delta), DocError> { // c = a.compose(b) // d = b.invert(a) // a = c.compose(d) @@ -195,10 +211,10 @@ impl Document { fn next_rev_id(&self) -> RevId { RevId(self.rev_id_counter) } } -fn validate_interval(delta: &Delta, interval: &Interval) -> Result<(), OTError> { +fn validate_interval(delta: &Delta, interval: &Interval) -> Result<(), DocError> { if delta.target_len < interval.end { log::error!("{:?} out of bounds. should 0..{}", interval, delta.target_len); - return Err(ErrorBuilder::new(OTErrorCode::IntervalOutOfBound).build()); + return Err(DocError::out_of_bound()); } Ok(()) } diff --git a/rust-lib/flowy-ot/src/client/document/mod.rs b/rust-lib/flowy-document/src/services/doc/document/mod.rs similarity index 100% rename from rust-lib/flowy-ot/src/client/document/mod.rs rename to rust-lib/flowy-document/src/services/doc/document/mod.rs diff --git a/rust-lib/flowy-ot/src/client/document/selection.rs b/rust-lib/flowy-document/src/services/doc/document/selection.rs similarity index 100% rename from rust-lib/flowy-ot/src/client/document/selection.rs rename to rust-lib/flowy-document/src/services/doc/document/selection.rs diff --git a/rust-lib/flowy-ot/src/client/extensions/delete/default_delete.rs b/rust-lib/flowy-document/src/services/doc/extensions/delete/default_delete.rs similarity index 73% rename from rust-lib/flowy-ot/src/client/extensions/delete/default_delete.rs rename to rust-lib/flowy-document/src/services/doc/extensions/delete/default_delete.rs index 7fb3ce8684..079f0addc1 100644 --- a/rust-lib/flowy-ot/src/client/extensions/delete/default_delete.rs +++ b/rust-lib/flowy-document/src/services/doc/extensions/delete/default_delete.rs @@ -1,7 +1,5 @@ -use crate::{ - client::extensions::DeleteExt, - core::{Delta, DeltaBuilder, Interval}, -}; +use crate::services::doc::extensions::DeleteExt; +use flowy_ot::core::{Delta, DeltaBuilder, Interval}; pub struct DefaultDelete {} impl DeleteExt for DefaultDelete { diff --git a/rust-lib/flowy-ot/src/client/extensions/delete/mod.rs b/rust-lib/flowy-document/src/services/doc/extensions/delete/mod.rs similarity index 100% rename from rust-lib/flowy-ot/src/client/extensions/delete/mod.rs rename to rust-lib/flowy-document/src/services/doc/extensions/delete/mod.rs diff --git a/rust-lib/flowy-ot/src/client/extensions/delete/preserve_line_format_merge.rs b/rust-lib/flowy-document/src/services/doc/extensions/delete/preserve_line_format_merge.rs similarity index 91% rename from rust-lib/flowy-ot/src/client/extensions/delete/preserve_line_format_merge.rs rename to rust-lib/flowy-document/src/services/doc/extensions/delete/preserve_line_format_merge.rs index b0003bdd97..b0cc0f1f48 100644 --- a/rust-lib/flowy-ot/src/client/extensions/delete/preserve_line_format_merge.rs +++ b/rust-lib/flowy-document/src/services/doc/extensions/delete/preserve_line_format_merge.rs @@ -1,7 +1,5 @@ -use crate::{ - client::{extensions::DeleteExt, util::is_newline}, - core::{plain_attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval, NEW_LINE}, -}; +use crate::services::doc::{extensions::DeleteExt, util::is_newline}; +use flowy_ot::core::{plain_attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval, NEW_LINE}; pub struct PreserveLineFormatOnMerge {} impl DeleteExt for PreserveLineFormatOnMerge { diff --git a/rust-lib/flowy-ot/src/client/extensions/format/format_at_position.rs b/rust-lib/flowy-document/src/services/doc/extensions/format/format_at_position.rs similarity index 100% rename from rust-lib/flowy-ot/src/client/extensions/format/format_at_position.rs rename to rust-lib/flowy-document/src/services/doc/extensions/format/format_at_position.rs diff --git a/rust-lib/flowy-ot/src/client/extensions/format/helper.rs b/rust-lib/flowy-document/src/services/doc/extensions/format/helper.rs similarity index 90% rename from rust-lib/flowy-ot/src/client/extensions/format/helper.rs rename to rust-lib/flowy-document/src/services/doc/extensions/format/helper.rs index 21aea5992c..cf6e3fca6a 100644 --- a/rust-lib/flowy-ot/src/client/extensions/format/helper.rs +++ b/rust-lib/flowy-document/src/services/doc/extensions/format/helper.rs @@ -1,7 +1,5 @@ -use crate::{ - client::util::find_newline, - core::{plain_attributes, Attribute, AttributeScope, Delta, Operation}, -}; +use crate::services::doc::util::find_newline; +use flowy_ot::core::{plain_attributes, Attribute, AttributeScope, Delta, Operation}; pub(crate) fn line_break(op: &Operation, attribute: &Attribute, scope: AttributeScope) -> Delta { let mut new_delta = Delta::new(); diff --git a/rust-lib/flowy-ot/src/client/extensions/format/mod.rs b/rust-lib/flowy-document/src/services/doc/extensions/format/mod.rs similarity index 100% rename from rust-lib/flowy-ot/src/client/extensions/format/mod.rs rename to rust-lib/flowy-document/src/services/doc/extensions/format/mod.rs diff --git a/rust-lib/flowy-ot/src/client/extensions/format/resolve_block_format.rs b/rust-lib/flowy-document/src/services/doc/extensions/format/resolve_block_format.rs similarity index 87% rename from rust-lib/flowy-ot/src/client/extensions/format/resolve_block_format.rs rename to rust-lib/flowy-document/src/services/doc/extensions/format/resolve_block_format.rs index 0209f7fe8c..1323314306 100644 --- a/rust-lib/flowy-ot/src/client/extensions/format/resolve_block_format.rs +++ b/rust-lib/flowy-document/src/services/doc/extensions/format/resolve_block_format.rs @@ -1,10 +1,8 @@ -use crate::{ - client::{ - extensions::{format::helper::line_break, FormatExt}, - util::find_newline, - }, - core::{plain_attributes, Attribute, AttributeScope, Delta, DeltaBuilder, DeltaIter, Interval}, +use crate::services::doc::{ + extensions::{format::helper::line_break, FormatExt}, + util::find_newline, }; +use flowy_ot::core::{plain_attributes, Attribute, AttributeScope, Delta, DeltaBuilder, DeltaIter, Interval}; pub struct ResolveBlockFormat {} impl FormatExt for ResolveBlockFormat { diff --git a/rust-lib/flowy-ot/src/client/extensions/format/resolve_inline_format.rs b/rust-lib/flowy-document/src/services/doc/extensions/format/resolve_inline_format.rs similarity index 84% rename from rust-lib/flowy-ot/src/client/extensions/format/resolve_inline_format.rs rename to rust-lib/flowy-document/src/services/doc/extensions/format/resolve_inline_format.rs index 046e7d116e..d66a263747 100644 --- a/rust-lib/flowy-ot/src/client/extensions/format/resolve_inline_format.rs +++ b/rust-lib/flowy-document/src/services/doc/extensions/format/resolve_inline_format.rs @@ -1,10 +1,8 @@ -use crate::{ - client::{ - extensions::{format::helper::line_break, FormatExt}, - util::find_newline, - }, - core::{Attribute, AttributeScope, Delta, DeltaBuilder, DeltaIter, Interval}, +use crate::services::doc::{ + extensions::{format::helper::line_break, FormatExt}, + util::find_newline, }; +use flowy_ot::core::{Attribute, AttributeScope, Delta, DeltaBuilder, DeltaIter, Interval}; pub struct ResolveInlineFormat {} impl FormatExt for ResolveInlineFormat { diff --git a/rust-lib/flowy-ot/src/client/extensions/insert/auto_exit_block.rs b/rust-lib/flowy-document/src/services/doc/extensions/insert/auto_exit_block.rs similarity index 87% rename from rust-lib/flowy-ot/src/client/extensions/insert/auto_exit_block.rs rename to rust-lib/flowy-document/src/services/doc/extensions/insert/auto_exit_block.rs index 24d330c0f4..dcc10270c0 100644 --- a/rust-lib/flowy-ot/src/client/extensions/insert/auto_exit_block.rs +++ b/rust-lib/flowy-document/src/services/doc/extensions/insert/auto_exit_block.rs @@ -1,9 +1,5 @@ -use crate::{ - client::{extensions::InsertExt, util::is_newline}, - core::{AttributeKey, Delta, DeltaBuilder, DeltaIter}, -}; - -use crate::core::{attributes_except_header, is_empty_line_at_index}; +use crate::services::doc::{extensions::InsertExt, util::is_newline}; +use flowy_ot::core::{attributes_except_header, is_empty_line_at_index, AttributeKey, Delta, DeltaBuilder, DeltaIter}; pub struct AutoExitBlock {} diff --git a/rust-lib/flowy-ot/src/client/extensions/insert/auto_format.rs b/rust-lib/flowy-document/src/services/doc/extensions/insert/auto_format.rs similarity index 93% rename from rust-lib/flowy-ot/src/client/extensions/insert/auto_format.rs rename to rust-lib/flowy-document/src/services/doc/extensions/insert/auto_format.rs index 86bc2013ec..1beb7fbdd7 100644 --- a/rust-lib/flowy-ot/src/client/extensions/insert/auto_format.rs +++ b/rust-lib/flowy-document/src/services/doc/extensions/insert/auto_format.rs @@ -1,7 +1,8 @@ -use crate::{ - client::{extensions::InsertExt, util::is_whitespace}, - core::{Delta, DeltaIter}, -}; +use crate::services::doc::{extensions::InsertExt, util::is_whitespace}; +use bytecount::num_chars; +use flowy_ot::core::{plain_attributes, Attribute, Attributes, Delta, DeltaBuilder, DeltaIter}; +use std::cmp::min; +use url::Url; pub struct AutoFormatExt {} impl InsertExt for AutoFormatExt { @@ -50,11 +51,6 @@ impl InsertExt for AutoFormatExt { } } -use crate::core::{plain_attributes, Attribute, Attributes, DeltaBuilder}; -use bytecount::num_chars; -use std::cmp::min; -use url::Url; - pub enum AutoFormatter { Url(Url), } diff --git a/rust-lib/flowy-ot/src/client/extensions/insert/default_insert.rs b/rust-lib/flowy-document/src/services/doc/extensions/insert/default_insert.rs similarity index 89% rename from rust-lib/flowy-ot/src/client/extensions/insert/default_insert.rs rename to rust-lib/flowy-document/src/services/doc/extensions/insert/default_insert.rs index d89bd022be..3638ffd5fb 100644 --- a/rust-lib/flowy-ot/src/client/extensions/insert/default_insert.rs +++ b/rust-lib/flowy-document/src/services/doc/extensions/insert/default_insert.rs @@ -1,7 +1,5 @@ -use crate::{ - client::extensions::InsertExt, - core::{AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, NEW_LINE}, -}; +use crate::services::doc::extensions::InsertExt; +use flowy_ot::core::{AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, NEW_LINE}; pub struct DefaultInsertAttribute {} impl InsertExt for DefaultInsertAttribute { diff --git a/rust-lib/flowy-ot/src/client/extensions/insert/mod.rs b/rust-lib/flowy-document/src/services/doc/extensions/insert/mod.rs similarity index 91% rename from rust-lib/flowy-ot/src/client/extensions/insert/mod.rs rename to rust-lib/flowy-document/src/services/doc/extensions/insert/mod.rs index 09138619ad..e796ba8e97 100644 --- a/rust-lib/flowy-ot/src/client/extensions/insert/mod.rs +++ b/rust-lib/flowy-document/src/services/doc/extensions/insert/mod.rs @@ -1,6 +1,8 @@ +use crate::services::doc::extensions::InsertExt; pub use auto_exit_block::*; pub use auto_format::*; pub use default_insert::*; +use flowy_ot::core::Delta; pub use preserve_block_format::*; pub use preserve_inline_format::*; pub use reset_format_on_new_line::*; @@ -12,8 +14,6 @@ mod preserve_block_format; mod preserve_inline_format; mod reset_format_on_new_line; -use crate::{client::extensions::InsertExt, core::Delta}; - pub struct InsertEmbedsExt {} impl InsertExt for InsertEmbedsExt { fn ext_name(&self) -> &str { "InsertEmbedsExt" } diff --git a/rust-lib/flowy-ot/src/client/extensions/insert/preserve_block_format.rs b/rust-lib/flowy-document/src/services/doc/extensions/insert/preserve_block_format.rs similarity index 89% rename from rust-lib/flowy-ot/src/client/extensions/insert/preserve_block_format.rs rename to rust-lib/flowy-document/src/services/doc/extensions/insert/preserve_block_format.rs index 7613ac3a74..338b919455 100644 --- a/rust-lib/flowy-ot/src/client/extensions/insert/preserve_block_format.rs +++ b/rust-lib/flowy-document/src/services/doc/extensions/insert/preserve_block_format.rs @@ -1,6 +1,14 @@ -use crate::{ - client::{extensions::InsertExt, util::is_newline}, - core::{attributes_except_header, plain_attributes, Attribute, AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, NEW_LINE}, +use crate::services::doc::{extensions::InsertExt, util::is_newline}; +use flowy_ot::core::{ + attributes_except_header, + plain_attributes, + Attribute, + AttributeKey, + Attributes, + Delta, + DeltaBuilder, + DeltaIter, + NEW_LINE, }; pub struct PreserveBlockFormatOnInsert {} diff --git a/rust-lib/flowy-ot/src/client/extensions/insert/preserve_inline_format.rs b/rust-lib/flowy-document/src/services/doc/extensions/insert/preserve_inline_format.rs similarity index 92% rename from rust-lib/flowy-ot/src/client/extensions/insert/preserve_inline_format.rs rename to rust-lib/flowy-document/src/services/doc/extensions/insert/preserve_inline_format.rs index 352bd089b2..432b166d8e 100644 --- a/rust-lib/flowy-ot/src/client/extensions/insert/preserve_inline_format.rs +++ b/rust-lib/flowy-document/src/services/doc/extensions/insert/preserve_inline_format.rs @@ -1,10 +1,8 @@ -use crate::{ - client::{ - extensions::InsertExt, - util::{contain_newline, is_newline}, - }, - core::{plain_attributes, AttributeKey, Delta, DeltaBuilder, DeltaIter, OpNewline, NEW_LINE}, +use crate::services::doc::{ + extensions::InsertExt, + util::{contain_newline, is_newline}, }; +use flowy_ot::core::{plain_attributes, AttributeKey, Delta, DeltaBuilder, DeltaIter, OpNewline, NEW_LINE}; pub struct PreserveInlineFormat {} impl InsertExt for PreserveInlineFormat { diff --git a/rust-lib/flowy-ot/src/client/extensions/insert/reset_format_on_new_line.rs b/rust-lib/flowy-document/src/services/doc/extensions/insert/reset_format_on_new_line.rs similarity index 86% rename from rust-lib/flowy-ot/src/client/extensions/insert/reset_format_on_new_line.rs rename to rust-lib/flowy-document/src/services/doc/extensions/insert/reset_format_on_new_line.rs index 2e69590137..f457a0f01c 100644 --- a/rust-lib/flowy-ot/src/client/extensions/insert/reset_format_on_new_line.rs +++ b/rust-lib/flowy-document/src/services/doc/extensions/insert/reset_format_on_new_line.rs @@ -1,7 +1,5 @@ -use crate::{ - client::{extensions::InsertExt, util::is_newline}, - core::{AttributeKey, Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, NEW_LINE}, -}; +use crate::services::doc::{extensions::InsertExt, util::is_newline}; +use flowy_ot::core::{AttributeKey, Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, NEW_LINE}; pub struct ResetLineFormatOnNewLine {} impl InsertExt for ResetLineFormatOnNewLine { diff --git a/rust-lib/flowy-ot/src/client/extensions/mod.rs b/rust-lib/flowy-document/src/services/doc/extensions/mod.rs similarity index 93% rename from rust-lib/flowy-ot/src/client/extensions/mod.rs rename to rust-lib/flowy-document/src/services/doc/extensions/mod.rs index b52746ffc8..a6c5be1986 100644 --- a/rust-lib/flowy-ot/src/client/extensions/mod.rs +++ b/rust-lib/flowy-document/src/services/doc/extensions/mod.rs @@ -2,7 +2,7 @@ pub use delete::*; pub use format::*; pub use insert::*; -use crate::core::{Attribute, Delta, Interval}; +use flowy_ot::core::{Attribute, Delta, Interval}; mod delete; mod format; diff --git a/rust-lib/flowy-ot/src/client/history.rs b/rust-lib/flowy-document/src/services/doc/history.rs similarity index 85% rename from rust-lib/flowy-ot/src/client/history.rs rename to rust-lib/flowy-document/src/services/doc/history.rs index c017f0e184..6e0d374770 100644 --- a/rust-lib/flowy-ot/src/client/history.rs +++ b/rust-lib/flowy-document/src/services/doc/history.rs @@ -1,20 +1,7 @@ -use crate::core::Delta; +use flowy_ot::core::Delta; const MAX_UNDOS: usize = 20; -#[derive(Debug, Clone)] -pub struct RevId(pub usize); - -#[derive(Debug, Clone)] -pub struct Revision { - rev_id: RevId, - pub delta: Delta, -} - -impl Revision { - pub fn new(rev_id: RevId, delta: Delta) -> Revision { Self { rev_id, delta } } -} - #[derive(Debug, Clone)] pub struct UndoResult { success: bool, diff --git a/rust-lib/flowy-ot/src/client/mod.rs b/rust-lib/flowy-document/src/services/doc/mod.rs similarity index 100% rename from rust-lib/flowy-ot/src/client/mod.rs rename to rust-lib/flowy-document/src/services/doc/mod.rs diff --git a/rust-lib/flowy-ot/src/client/util.rs b/rust-lib/flowy-document/src/services/doc/util.rs similarity index 89% rename from rust-lib/flowy-ot/src/client/util.rs rename to rust-lib/flowy-document/src/services/doc/util.rs index 3c014b07fe..2e918c7540 100644 --- a/rust-lib/flowy-ot/src/client/util.rs +++ b/rust-lib/flowy-document/src/services/doc/util.rs @@ -1,4 +1,4 @@ -use crate::core::{NEW_LINE, WHITESPACE}; +use flowy_ot::core::{NEW_LINE, WHITESPACE}; #[inline] pub fn find_newline(s: &str) -> Option { diff --git a/rust-lib/flowy-ot/src/client/view.rs b/rust-lib/flowy-document/src/services/doc/view.rs similarity index 99% rename from rust-lib/flowy-ot/src/client/view.rs rename to rust-lib/flowy-document/src/services/doc/view.rs index d132b6e1be..aa2effb28a 100644 --- a/rust-lib/flowy-ot/src/client/view.rs +++ b/rust-lib/flowy-document/src/services/doc/view.rs @@ -1,5 +1,5 @@ use super::extensions::*; -use crate::{ +use flowy_ot::{ core::{Attribute, Delta, Interval}, errors::{ErrorBuilder, OTError, OTErrorCode}, }; diff --git a/rust-lib/flowy-document/src/services/doc_controller.rs b/rust-lib/flowy-document/src/services/doc_controller.rs index 42a6f186b7..31a363590c 100644 --- a/rust-lib/flowy-document/src/services/doc_controller.rs +++ b/rust-lib/flowy-document/src/services/doc_controller.rs @@ -1,5 +1,5 @@ use crate::{ - entities::doc::{CreateDocParams, Doc, QueryDocParams, SaveDocParams}, + entities::doc::{CreateDocParams, Doc, QueryDocParams, UpdateDocParams}, errors::DocError, module::DocumentUser, services::server::Server, @@ -7,20 +7,37 @@ use crate::{ }; use flowy_database::{ConnectionPool, SqliteConnection}; -use crate::{errors::internal_error, services::open_doc::OpenedDocPersistence}; +use crate::{ + errors::internal_error, + services::{ + cache::DocCache, + open_doc::{DocId, OpenedDoc, OpenedDocPersistence}, + ws::WsManager, + }, +}; +use parking_lot::RwLock; use std::sync::Arc; use tokio::task::JoinHandle; pub(crate) struct DocController { server: Server, sql: Arc, + ws: Arc>, + cache: Arc, user: Arc, } impl DocController { - pub(crate) fn new(server: Server, user: Arc) -> Self { + pub(crate) fn new(server: Server, user: Arc, ws: Arc>) -> Self { let sql = Arc::new(DocTableSql {}); - Self { sql, server, user } + let cache = Arc::new(DocCache::new()); + Self { + sql, + server, + user, + ws, + cache, + } } #[tracing::instrument(skip(self, conn), err)] @@ -28,22 +45,53 @@ impl DocController { let doc = Doc { id: params.id, data: params.data, + revision: 0, }; let _ = self.sql.create_doc_table(DocTable::new(doc), conn)?; Ok(()) } #[tracing::instrument(level = "debug", skip(self, pool), err)] - pub(crate) async fn open(&self, params: QueryDocParams, pool: Arc) -> Result { - match self._open(params.clone(), pool.clone()) { - Ok(doc_table) => Ok(doc_table.into()), - Err(error) => self.try_read_on_server(params, pool.clone(), error).await, + pub(crate) async fn open(&self, params: QueryDocParams, pool: Arc) -> Result, DocError> { + if self.cache.is_opened(¶ms.doc_id) == false { + return match self._open(params.clone(), pool.clone()) { + Ok(doc) => Ok(doc), + Err(error) => Err(error), + }; } + + let doc = self.cache.get(¶ms.doc_id)?; + Ok(doc) } + pub(crate) fn close(&self, doc_id: &str) -> Result<(), DocError> { + self.cache.remove(doc_id); + self.ws.write().remove_handler(doc_id); + Ok(()) + } + + // #[tracing::instrument(level = "debug", skip(self, changeset, pool), err)] + // pub(crate) async fn apply_changeset(&self, id: T, changeset: Bytes, pool: + // Arc) -> Result<(), DocError> where + // T: Into + Debug, + // { + // let id = id.into(); + // match self.doc_map.get(&id) { + // None => Err(doc_not_found()), + // Some(doc) => { + // let _ = doc.apply_delta(changeset, pool)?; + // Ok(()) + // }, + // } + // } + #[tracing::instrument(level = "debug", skip(self, conn), err)] pub(crate) fn delete(&self, params: QueryDocParams, conn: &SqliteConnection) -> Result<(), DocError> { - let _ = self.sql.delete_doc(¶ms.doc_id, &*conn)?; + let doc_id = ¶ms.doc_id; + let _ = self.sql.delete_doc(doc_id, &*conn)?; + + self.cache.remove(doc_id); + self.ws.write().remove_handler(doc_id); let _ = self.delete_doc_on_server(params)?; Ok(()) } @@ -51,7 +99,7 @@ impl DocController { impl DocController { #[tracing::instrument(level = "debug", skip(self, params), err)] - fn update_doc_on_server(&self, params: SaveDocParams) -> Result<(), DocError> { + fn update_doc_on_server(&self, params: UpdateDocParams) -> Result<(), DocError> { let token = self.user.token()?; let server = self.server.clone(); tokio::spawn(async move { @@ -89,15 +137,6 @@ impl DocController { })) } - #[tracing::instrument(level = "debug", skip(self), err)] - async fn sync_read_doc_from_server(&self, params: QueryDocParams) -> Result { - let token = self.user.token()?; - match self.server.read_doc(&token, params).await? { - None => Err(DocError::not_found()), - Some(doc) => Ok(doc), - } - } - #[tracing::instrument(level = "debug", skip(self), err)] fn delete_doc_on_server(&self, params: QueryDocParams) -> Result<(), DocError> { let token = self.user.token()?; @@ -114,25 +153,30 @@ impl DocController { Ok(()) } - fn _open(&self, params: QueryDocParams, pool: Arc) -> Result { - let doc_table = self.sql.read_doc_table(¶ms.doc_id, &*(pool.get().map_err(internal_error)?))?; - let doc: Doc = doc_table.into(); - let _ = self.read_doc_from_server(params, pool.clone())?; - Ok(doc) - } + fn _open(&self, params: QueryDocParams, pool: Arc) -> Result, DocError> { + match self.sql.read_doc_table(¶ms.doc_id, &*(pool.get().map_err(internal_error)?)) { + Ok(doc_table) => { + let doc = Arc::new(OpenedDoc::new(doc_table.into(), self.ws.read().sender.clone())?); + self.ws.write().register_handler(doc.id.as_ref(), doc.clone()); + self.cache.set(doc.clone()); - async fn try_read_on_server(&self, params: QueryDocParams, pool: Arc, error: DocError) -> Result { - if error.is_record_not_found() { - log::debug!("Doc:{} don't exist, reading from server", params.doc_id); - self.read_doc_from_server(params, pool)?.await.map_err(internal_error)? - } else { - Err(error) + Ok(doc) + }, + Err(error) => { + if error.is_record_not_found() { + log::debug!("Doc:{} don't exist, reading from server", params.doc_id); + // TODO: notify doc update + let _ = self.read_doc_from_server(params, pool); + } + + return Err(error); + }, } } } impl OpenedDocPersistence for DocController { - fn save(&self, params: SaveDocParams, pool: Arc) -> Result<(), DocError> { + fn save(&self, params: UpdateDocParams, pool: Arc) -> Result<(), DocError> { let changeset = DocTableChangeset::new(params.clone()); let _ = self.sql.update_doc_table(changeset, &*(pool.get().map_err(internal_error)?))?; Ok(()) diff --git a/rust-lib/flowy-document/src/services/mod.rs b/rust-lib/flowy-document/src/services/mod.rs index e5981b6cac..fe071e0151 100644 --- a/rust-lib/flowy-document/src/services/mod.rs +++ b/rust-lib/flowy-document/src/services/mod.rs @@ -1,4 +1,6 @@ +mod cache; +pub mod doc; pub(crate) mod doc_controller; -pub(crate) mod open_doc; +mod open_doc; pub mod server; pub mod ws; diff --git a/rust-lib/flowy-document/src/services/open_doc/open_doc.rs b/rust-lib/flowy-document/src/services/open_doc.rs similarity index 60% rename from rust-lib/flowy-document/src/services/open_doc/open_doc.rs rename to rust-lib/flowy-document/src/services/open_doc.rs index 0ca2470530..c8aa76260c 100644 --- a/rust-lib/flowy-document/src/services/open_doc/open_doc.rs +++ b/rust-lib/flowy-document/src/services/open_doc.rs @@ -1,16 +1,19 @@ use crate::{ entities::{ - doc::SaveDocParams, + doc::{Doc, UpdateDocParams}, ws::{WsDocumentData, WsSource}, }, errors::DocError, - services::ws::{WsHandler, WsSender}, + services::{ + doc::Document, + ws::{WsHandler, WsSender}, + }, }; use bytes::Bytes; use flowy_database::ConnectionPool; -use flowy_ot::{client::Document, core::Delta}; +use flowy_ot::core::Delta; use parking_lot::RwLock; -use std::sync::Arc; +use std::{convert::TryInto, sync::Arc}; #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct DocId(pub(crate) String); @@ -25,32 +28,42 @@ where } pub(crate) trait OpenedDocPersistence: Send + Sync { - fn save(&self, params: SaveDocParams, pool: Arc) -> Result<(), DocError>; + fn save(&self, params: UpdateDocParams, pool: Arc) -> Result<(), DocError>; } pub(crate) struct OpenedDoc { pub(crate) id: DocId, + pub(crate) revision: i64, document: RwLock, ws_sender: Arc, - persistence: Arc, } impl OpenedDoc { - pub(crate) fn new(id: DocId, delta: Delta, persistence: Arc, ws_sender: Arc) -> Self { + pub(crate) fn new(doc: Doc, ws_sender: Arc) -> Result { + let id: DocId = doc.id.into(); + let revision = doc.revision; + let delta: Delta = doc.data.try_into()?; let document = RwLock::new(Document::from_delta(delta)); - Self { + + Ok(Self { id, + revision, document, ws_sender, - persistence, + }) + } + + pub(crate) fn doc(&self) -> Doc { + Doc { + id: self.id.0.clone(), + data: self.document.read().to_bytes(), + revision: self.revision, } } - pub(crate) fn data(&self) -> Vec { self.document.read().to_bytes() } - pub(crate) fn apply_delta(&self, data: Bytes, pool: Arc) -> Result<(), DocError> { let mut write_guard = self.document.write(); - let _ = write_guard.apply_changeset(data.clone())?; + let _ = write_guard.apply_delta(data.clone())?; match self.ws_sender.send_data(data) { Ok(_) => {}, @@ -61,11 +74,11 @@ impl OpenedDoc { } // Opti: strategy to save the document - let save = SaveDocParams { + let save = UpdateDocParams { id: self.id.0.clone(), - data: write_guard.to_bytes(), + doc_data: write_guard.to_bytes(), }; - let _ = self.persistence.save(save, pool)?; + // let _ = self.persistence.save(save, pool)?; Ok(()) } diff --git a/rust-lib/flowy-document/src/services/open_doc/manager.rs b/rust-lib/flowy-document/src/services/open_doc/manager.rs deleted file mode 100644 index 43e0b4ae4f..0000000000 --- a/rust-lib/flowy-document/src/services/open_doc/manager.rs +++ /dev/null @@ -1,94 +0,0 @@ -use crate::{ - errors::DocError, - services::{ - open_doc::{DocId, OpenedDoc, OpenedDocPersistence}, - ws::WsManager, - }, -}; -use bytes::Bytes; -use dashmap::DashMap; -use flowy_database::ConnectionPool; -use flowy_ot::{core::Delta, errors::OTError}; -use parking_lot::RwLock; -use std::{convert::TryInto, fmt::Debug, sync::Arc}; - -pub(crate) struct OpenedDocManager { - doc_map: DashMap>, - ws_manager: Arc>, - persistence: Arc, -} - -impl OpenedDocManager { - pub(crate) fn new(ws_manager: Arc>, persistence: Arc) -> Self { - Self { - doc_map: DashMap::new(), - ws_manager, - persistence, - } - } - - #[tracing::instrument(level = "debug", skip(self, data), err)] - pub(crate) fn open(&self, id: T, data: D) -> Result<(), DocError> - where - T: Into + Debug, - D: TryInto, - { - let doc = Arc::new(OpenedDoc::new( - id.into(), - data.try_into()?, - self.persistence.clone(), - self.ws_manager.read().sender.clone(), - )); - self.ws_manager.write().register_handler(doc.id.as_ref(), doc.clone()); - self.doc_map.insert(doc.id.clone(), doc.clone()); - Ok(()) - } - - pub(crate) fn is_opened(&self, id: T) -> bool - where - T: Into, - { - let doc_id = id.into(); - self.doc_map.get(&doc_id).is_some() - } - - #[tracing::instrument(level = "debug", skip(self, changeset, pool), err)] - pub(crate) async fn apply_changeset(&self, id: T, changeset: Bytes, pool: Arc) -> Result<(), DocError> - where - T: Into + Debug, - { - let id = id.into(); - match self.doc_map.get(&id) { - None => Err(doc_not_found()), - Some(doc) => { - let _ = doc.apply_delta(changeset, pool)?; - Ok(()) - }, - } - } - - pub(crate) async fn read_doc(&self, id: T) -> Result, DocError> - where - T: Into + Clone, - { - if !self.is_opened(id.clone()) { - return Err(doc_not_found()); - } - - let doc_id = id.into(); - let doc = self.doc_map.get(&doc_id).unwrap(); - Ok(doc.data()) - } - - pub(crate) fn close(&self, id: T) -> Result<(), DocError> - where - T: Into, - { - let doc_id = id.into(); - self.doc_map.remove(&doc_id); - self.ws_manager.write().remove_handler(doc_id.as_ref()); - Ok(()) - } -} - -fn doc_not_found() -> DocError { DocError::not_found().context("Doc is close or you should call open first") } diff --git a/rust-lib/flowy-document/src/services/open_doc/mod.rs b/rust-lib/flowy-document/src/services/open_doc/mod.rs deleted file mode 100644 index 89d56ba668..0000000000 --- a/rust-lib/flowy-document/src/services/open_doc/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod manager; -mod open_doc; - -pub(crate) use manager::*; -pub use open_doc::*; diff --git a/rust-lib/flowy-document/src/services/server/mod.rs b/rust-lib/flowy-document/src/services/server/mod.rs index 7ae81962cd..02d1e46d9a 100644 --- a/rust-lib/flowy-document/src/services/server/mod.rs +++ b/rust-lib/flowy-document/src/services/server/mod.rs @@ -5,7 +5,7 @@ mod server_api_mock; pub use server_api::*; // TODO: ignore mock files in production use crate::{ - entities::doc::{CreateDocParams, Doc, QueryDocParams, SaveDocParams}, + entities::doc::{CreateDocParams, Doc, QueryDocParams, UpdateDocParams}, errors::DocError, }; use flowy_infra::future::ResultFuture; @@ -18,7 +18,7 @@ pub trait DocumentServerAPI { fn read_doc(&self, token: &str, params: QueryDocParams) -> ResultFuture, DocError>; - fn update_doc(&self, token: &str, params: SaveDocParams) -> ResultFuture<(), DocError>; + fn update_doc(&self, token: &str, params: UpdateDocParams) -> ResultFuture<(), DocError>; fn delete_doc(&self, token: &str, params: QueryDocParams) -> ResultFuture<(), DocError>; } diff --git a/rust-lib/flowy-document/src/services/server/server_api.rs b/rust-lib/flowy-document/src/services/server/server_api.rs index ae77a9264d..2bfbe6c50f 100644 --- a/rust-lib/flowy-document/src/services/server/server_api.rs +++ b/rust-lib/flowy-document/src/services/server/server_api.rs @@ -1,5 +1,5 @@ use crate::{ - entities::doc::{CreateDocParams, Doc, QueryDocParams, SaveDocParams}, + entities::doc::{CreateDocParams, Doc, QueryDocParams, UpdateDocParams}, errors::DocError, services::server::DocumentServerAPI, }; @@ -19,7 +19,7 @@ impl DocumentServerAPI for DocServer { ResultFuture::new(async move { read_doc_request(&token, params, DOC_URL.as_ref()).await }) } - fn update_doc(&self, token: &str, params: SaveDocParams) -> ResultFuture<(), DocError> { + fn update_doc(&self, token: &str, params: UpdateDocParams) -> ResultFuture<(), DocError> { let token = token.to_owned(); ResultFuture::new(async move { update_doc_request(&token, params, DOC_URL.as_ref()).await }) } @@ -53,7 +53,7 @@ pub async fn read_doc_request(token: &str, params: QueryDocParams, url: &str) -> Ok(doc) } -pub async fn update_doc_request(token: &str, params: SaveDocParams, url: &str) -> Result<(), DocError> { +pub async fn update_doc_request(token: &str, params: UpdateDocParams, url: &str) -> Result<(), DocError> { let _ = request_builder() .patch(&url.to_owned()) .header(HEADER_TOKEN, token) diff --git a/rust-lib/flowy-document/src/services/server/server_api_mock.rs b/rust-lib/flowy-document/src/services/server/server_api_mock.rs index f1f4dc99ea..880d5c68e2 100644 --- a/rust-lib/flowy-document/src/services/server/server_api_mock.rs +++ b/rust-lib/flowy-document/src/services/server/server_api_mock.rs @@ -1,5 +1,5 @@ use crate::{ - entities::doc::{CreateDocParams, Doc, QueryDocParams, SaveDocParams}, + entities::doc::{CreateDocParams, Doc, QueryDocParams, UpdateDocParams}, errors::DocError, services::server::DocumentServerAPI, }; @@ -13,7 +13,7 @@ impl DocumentServerAPI for DocServerMock { ResultFuture::new(async { Ok(None) }) } - fn update_doc(&self, _token: &str, _params: SaveDocParams) -> ResultFuture<(), DocError> { ResultFuture::new(async { Ok(()) }) } + fn update_doc(&self, _token: &str, _params: UpdateDocParams) -> ResultFuture<(), DocError> { ResultFuture::new(async { Ok(()) }) } fn delete_doc(&self, _token: &str, _params: QueryDocParams) -> ResultFuture<(), DocError> { ResultFuture::new(async { Ok(()) }) } } diff --git a/rust-lib/flowy-document/src/sql_tables/doc/doc_table.rs b/rust-lib/flowy-document/src/sql_tables/doc/doc_table.rs index 82ae6ca228..a9cdfd5aba 100644 --- a/rust-lib/flowy-document/src/sql_tables/doc/doc_table.rs +++ b/rust-lib/flowy-document/src/sql_tables/doc/doc_table.rs @@ -1,4 +1,4 @@ -use crate::entities::doc::{Doc, SaveDocParams}; +use crate::entities::doc::{Doc, UpdateDocParams}; use flowy_database::schema::doc_table; #[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)] @@ -6,7 +6,7 @@ use flowy_database::schema::doc_table; pub(crate) struct DocTable { pub id: String, pub data: Vec, - pub version: i64, + pub revision: i64, } impl DocTable { @@ -14,7 +14,7 @@ impl DocTable { Self { id: doc.id, data: doc.data, - version: 0, + revision: 0, } } } @@ -27,10 +27,10 @@ pub(crate) struct DocTableChangeset { } impl DocTableChangeset { - pub(crate) fn new(params: SaveDocParams) -> Self { + pub(crate) fn new(params: UpdateDocParams) -> Self { Self { id: params.id, - data: params.data, + data: params.doc_data, } } } @@ -40,6 +40,7 @@ impl std::convert::Into for DocTable { Doc { id: self.id, data: self.data, + revision: self.revision, } } } diff --git a/rust-lib/flowy-ot/tests/attribute_test.rs b/rust-lib/flowy-document/tests/editor/attribute_test.rs similarity index 99% rename from rust-lib/flowy-ot/tests/attribute_test.rs rename to rust-lib/flowy-document/tests/editor/attribute_test.rs index 13f1002f8e..6d4e77153d 100644 --- a/rust-lib/flowy-ot/tests/attribute_test.rs +++ b/rust-lib/flowy-document/tests/editor/attribute_test.rs @@ -1,10 +1,6 @@ -pub mod helper; - -use crate::helper::{TestOp::*, *}; -use flowy_ot::{ - client::{FlowyDoc, PlainDoc}, - core::{Interval, NEW_LINE, WHITESPACE}, -}; +use crate::editor::{TestBuilder, TestOp::*}; +use flowy_document::services::doc::{FlowyDoc, PlainDoc}; +use flowy_ot::core::{Interval, NEW_LINE, WHITESPACE}; #[test] fn attributes_bold_added() { diff --git a/rust-lib/flowy-document/tests/editor/main.rs b/rust-lib/flowy-document/tests/editor/main.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/rust-lib/flowy-document/tests/editor/main.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/rust-lib/flowy-ot/tests/helper/mod.rs b/rust-lib/flowy-document/tests/editor/mod.rs similarity index 98% rename from rust-lib/flowy-ot/tests/helper/mod.rs rename to rust-lib/flowy-document/tests/editor/mod.rs index 32e0ceaaa7..d8ff30d1c6 100644 --- a/rust-lib/flowy-ot/tests/helper/mod.rs +++ b/rust-lib/flowy-document/tests/editor/mod.rs @@ -1,8 +1,11 @@ +mod attribute_test; +mod op_test; +mod serde_test; +mod undo_redo_test; + use derive_more::Display; -use flowy_ot::{ - client::{CustomDocument, Document}, - core::*, -}; +use flowy_document::services::doc::{CustomDocument, Document}; +use flowy_ot::core::*; use rand::{prelude::*, Rng as WrappedRng}; use std::{sync::Once, time::Duration}; diff --git a/rust-lib/flowy-ot/tests/op_test.rs b/rust-lib/flowy-document/tests/editor/op_test.rs similarity index 99% rename from rust-lib/flowy-ot/tests/op_test.rs rename to rust-lib/flowy-document/tests/editor/op_test.rs index c02decdfb2..14dc6f4eaa 100644 --- a/rust-lib/flowy-ot/tests/op_test.rs +++ b/rust-lib/flowy-document/tests/editor/op_test.rs @@ -1,9 +1,7 @@ -pub mod helper; - -use crate::helper::TestOp::*; +use crate::editor::{Rng, TestBuilder, TestOp::*}; use bytecount::num_chars; -use flowy_ot::{client::PlainDoc, core::*}; -use helper::*; +use flowy_document::services::doc::PlainDoc; +use flowy_ot::core::*; #[test] fn attributes_insert_text() { diff --git a/rust-lib/flowy-ot/tests/serde_test.rs b/rust-lib/flowy-document/tests/editor/serde_test.rs similarity index 97% rename from rust-lib/flowy-ot/tests/serde_test.rs rename to rust-lib/flowy-document/tests/editor/serde_test.rs index 2f98db7886..a7959b9b7e 100644 --- a/rust-lib/flowy-ot/tests/serde_test.rs +++ b/rust-lib/flowy-document/tests/editor/serde_test.rs @@ -1,7 +1,5 @@ -use flowy_ot::{ - client::{Document, PlainDoc}, - core::*, -}; +use flowy_document::services::doc::{Document, PlainDoc}; +use flowy_ot::core::*; #[test] fn operation_insert_serialize_test() { diff --git a/rust-lib/flowy-ot/tests/undo_redo_test.rs b/rust-lib/flowy-document/tests/editor/undo_redo_test.rs similarity index 98% rename from rust-lib/flowy-ot/tests/undo_redo_test.rs rename to rust-lib/flowy-document/tests/editor/undo_redo_test.rs index 7426896f68..cb7e5fbea6 100644 --- a/rust-lib/flowy-ot/tests/undo_redo_test.rs +++ b/rust-lib/flowy-document/tests/editor/undo_redo_test.rs @@ -1,10 +1,6 @@ -pub mod helper; - -use crate::helper::{TestOp::*, *}; -use flowy_ot::{ - client::{FlowyDoc, PlainDoc, RECORD_THRESHOLD}, - core::{Interval, NEW_LINE, WHITESPACE}, -}; +use crate::editor::{TestBuilder, TestOp::*}; +use flowy_document::services::doc::{FlowyDoc, PlainDoc, RECORD_THRESHOLD}; +use flowy_ot::core::{Interval, NEW_LINE, WHITESPACE}; #[test] fn history_insert_undo() { diff --git a/rust-lib/flowy-document/tests/main.rs b/rust-lib/flowy-document/tests/main.rs new file mode 100644 index 0000000000..00fa029a5a --- /dev/null +++ b/rust-lib/flowy-document/tests/main.rs @@ -0,0 +1 @@ +mod editor; diff --git a/rust-lib/flowy-ot/Cargo.toml b/rust-lib/flowy-ot/Cargo.toml index d511bb9385..ef5ac88ed9 100644 --- a/rust-lib/flowy-ot/Cargo.toml +++ b/rust-lib/flowy-ot/Cargo.toml @@ -11,17 +11,10 @@ serde = { version = "1.0", features = ["derive"] } serde_json = {version = "1.0"} derive_more = {version = "0.99", features = ["display"]} log = "0.4" -color-eyre = { version = "0.5", default-features = false } -chrono = "0.4.19" lazy_static = "1.4.0" -url = "2.2" strum = "0.21" strum_macros = "0.21" bytes = "1.0" -[dev-dependencies] -criterion = "0.3" -rand = "0.7.3" -env_logger = "0.8.2" diff --git a/rust-lib/flowy-ot/src/lib.rs b/rust-lib/flowy-ot/src/lib.rs index f71d8666cf..fec82dd9cf 100644 --- a/rust-lib/flowy-ot/src/lib.rs +++ b/rust-lib/flowy-ot/src/lib.rs @@ -1,4 +1,2 @@ -pub mod client; pub mod core; pub mod errors; -pub mod server; diff --git a/rust-lib/flowy-ot/src/server/mod.rs b/rust-lib/flowy-ot/src/server/mod.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/rust-lib/flowy-ot/src/server/mod.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/rust-lib/flowy-workspace/src/entities/view/view_update.rs b/rust-lib/flowy-workspace/src/entities/view/view_update.rs index 3632594ff3..d57c13c790 100644 --- a/rust-lib/flowy-workspace/src/entities/view/view_update.rs +++ b/rust-lib/flowy-workspace/src/entities/view/view_update.rs @@ -3,7 +3,7 @@ use crate::{ errors::WorkspaceError, }; use flowy_derive::ProtoBuf; -use flowy_document::entities::doc::{ApplyChangesetParams, SaveDocParams}; +use flowy_document::entities::doc::{DocChangeset, UpdateDocParams}; use std::convert::TryInto; #[derive(Default, ProtoBuf)] @@ -109,16 +109,19 @@ pub struct SaveViewDataRequest { pub data: Vec, } -impl TryInto for SaveViewDataRequest { +impl TryInto for SaveViewDataRequest { type Error = WorkspaceError; - fn try_into(self) -> Result { + fn try_into(self) -> Result { let view_id = ViewId::parse(self.view_id).map_err(|e| WorkspaceError::view_id().context(e))?.0; // Opti: Vec -> Delta -> Vec let data = DeltaData::parse(self.data).map_err(|e| WorkspaceError::view_data().context(e))?.0; - Ok(SaveDocParams { id: view_id, data }) + Ok(UpdateDocParams { + id: view_id, + doc_data: data, + }) } } @@ -131,15 +134,15 @@ pub struct ApplyChangesetRequest { pub data: Vec, } -impl TryInto for ApplyChangesetRequest { +impl TryInto for ApplyChangesetRequest { type Error = WorkspaceError; - fn try_into(self) -> Result { + fn try_into(self) -> Result { let view_id = ViewId::parse(self.view_id).map_err(|e| WorkspaceError::view_id().context(e))?.0; // Opti: Vec -> Delta -> Vec let data = DeltaData::parse(self.data).map_err(|e| WorkspaceError::view_data().context(e))?.0; - Ok(ApplyChangesetParams { id: view_id, data }) + Ok(DocChangeset { id: view_id, data }) } } diff --git a/rust-lib/flowy-workspace/src/event.rs b/rust-lib/flowy-workspace/src/event.rs index 34796afcb3..b46a67178a 100644 --- a/rust-lib/flowy-workspace/src/event.rs +++ b/rust-lib/flowy-workspace/src/event.rs @@ -49,6 +49,6 @@ pub enum WorkspaceEvent { #[event(input = "OpenViewRequest", output = "Doc")] OpenView = 205, - #[event(input = "ApplyChangesetRequest", output = "Doc")] + #[event(input = "DocChangeset", output = "Doc")] ApplyChangeset = 206, } diff --git a/rust-lib/flowy-workspace/src/handlers/view_handler.rs b/rust-lib/flowy-workspace/src/handlers/view_handler.rs index 816bdd5328..44ced6e8b2 100644 --- a/rust-lib/flowy-workspace/src/handlers/view_handler.rs +++ b/rust-lib/flowy-workspace/src/handlers/view_handler.rs @@ -16,7 +16,7 @@ use crate::{ services::ViewController, }; use flowy_dispatch::prelude::{data_result, Data, DataResult, Unit}; -use flowy_document::entities::doc::{ApplyChangesetParams, Doc, QueryDocParams}; +use flowy_document::entities::doc::{Doc, DocChangeset, QueryDocParams}; use std::{convert::TryInto, sync::Arc}; #[tracing::instrument(skip(data, controller), err)] @@ -60,7 +60,7 @@ pub(crate) async fn apply_changeset_handler( data: Data, controller: Unit>, ) -> DataResult { - let params: ApplyChangesetParams = data.into_inner().try_into()?; + let params: DocChangeset = data.into_inner().try_into()?; let doc = controller.apply_changeset(params).await?; data_result(doc) } diff --git a/rust-lib/flowy-workspace/src/services/view_controller.rs b/rust-lib/flowy-workspace/src/services/view_controller.rs index acdd899a2c..f6867a44a8 100644 --- a/rust-lib/flowy-workspace/src/services/view_controller.rs +++ b/rust-lib/flowy-workspace/src/services/view_controller.rs @@ -14,7 +14,7 @@ use crate::{ }; use flowy_database::SqliteConnection; use flowy_document::{ - entities::doc::{ApplyChangesetParams, CreateDocParams, Doc, QueryDocParams}, + entities::doc::{CreateDocParams, Doc, DocChangeset, QueryDocParams}, module::FlowyDocument, }; use std::sync::Arc; @@ -125,7 +125,7 @@ impl ViewController { Ok(()) } - pub(crate) async fn apply_changeset(&self, params: ApplyChangesetParams) -> Result { + pub(crate) async fn apply_changeset(&self, params: DocChangeset) -> Result { let pool = self.database.db_pool()?; let doc = self.document.apply_changeset(params, pool).await?; Ok(doc)