move flowy-ot document to flowy-document

This commit is contained in:
appflowy 2021-09-22 23:21:44 +08:00
parent 47f3a187c4
commit 2b9bc7a0cd
66 changed files with 611 additions and 522 deletions

View File

@ -7,6 +7,7 @@
import 'dart:core' as $core; import 'dart:core' as $core;
import 'package:fixnum/fixnum.dart' as $fixnum;
import 'package:protobuf/protobuf.dart' as $pb; import 'package:protobuf/protobuf.dart' as $pb;
class CreateDocParams extends $pb.GeneratedMessage { 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) 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') ..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') ? '' : 'data', $pb.PbFieldType.OY)
..aInt64(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'revision')
..hasRequiredFields = false ..hasRequiredFields = false
; ;
@ -81,6 +83,7 @@ class Doc extends $pb.GeneratedMessage {
factory Doc({ factory Doc({
$core.String? id, $core.String? id,
$core.List<$core.int>? data, $core.List<$core.int>? data,
$fixnum.Int64? revision,
}) { }) {
final _result = create(); final _result = create();
if (id != null) { if (id != null) {
@ -89,6 +92,9 @@ class Doc extends $pb.GeneratedMessage {
if (data != null) { if (data != null) {
_result.data = data; _result.data = data;
} }
if (revision != null) {
_result.revision = revision;
}
return _result; return _result;
} }
factory Doc.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); 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); $core.bool hasData() => $_has(1);
@$pb.TagNumber(2) @$pb.TagNumber(2)
void clearData() => clearField(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 { class UpdateDocParams extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SaveDocParams', createEmptyInstance: create) 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') ..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 ..hasRequiredFields = false
; ;
SaveDocParams._() : super(); UpdateDocParams._() : super();
factory SaveDocParams({ factory UpdateDocParams({
$core.String? id, $core.String? id,
$core.List<$core.int>? data, $core.List<$core.int>? docData,
}) { }) {
final _result = create(); final _result = create();
if (id != null) { if (id != null) {
_result.id = id; _result.id = id;
} }
if (data != null) { if (docData != null) {
_result.data = data; _result.docData = docData;
} }
return _result; return _result;
} }
factory SaveDocParams.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory UpdateDocParams.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.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
@$core.Deprecated( @$core.Deprecated(
'Using this can add significant overhead to your binary. ' 'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version') 'Will be removed in next major version')
SaveDocParams clone() => SaveDocParams()..mergeFromMessage(this); UpdateDocParams clone() => UpdateDocParams()..mergeFromMessage(this);
@$core.Deprecated( @$core.Deprecated(
'Using this can add significant overhead to your binary. ' 'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version') '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; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static SaveDocParams create() => SaveDocParams._(); static UpdateDocParams create() => UpdateDocParams._();
SaveDocParams createEmptyInstance() => create(); UpdateDocParams createEmptyInstance() => create();
static $pb.PbList<SaveDocParams> createRepeated() => $pb.PbList<SaveDocParams>(); static $pb.PbList<UpdateDocParams> createRepeated() => $pb.PbList<UpdateDocParams>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static SaveDocParams getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SaveDocParams>(create); static UpdateDocParams getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<UpdateDocParams>(create);
static SaveDocParams? _defaultInstance; static UpdateDocParams? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.String get id => $_getSZ(0); $core.String get id => $_getSZ(0);
@ -183,24 +198,24 @@ class SaveDocParams extends $pb.GeneratedMessage {
void clearId() => clearField(1); void clearId() => clearField(1);
@$pb.TagNumber(2) @$pb.TagNumber(2)
$core.List<$core.int> get data => $_getN(1); $core.List<$core.int> get docData => $_getN(1);
@$pb.TagNumber(2) @$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) @$pb.TagNumber(2)
$core.bool hasData() => $_has(1); $core.bool hasDocData() => $_has(1);
@$pb.TagNumber(2) @$pb.TagNumber(2)
void clearData() => clearField(2); void clearDocData() => clearField(2);
} }
class ApplyChangesetParams extends $pb.GeneratedMessage { class DocChangeset extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ApplyChangesetParams', createEmptyInstance: create) 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') ..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') ? '' : 'data', $pb.PbFieldType.OY)
..hasRequiredFields = false ..hasRequiredFields = false
; ;
ApplyChangesetParams._() : super(); DocChangeset._() : super();
factory ApplyChangesetParams({ factory DocChangeset({
$core.String? id, $core.String? id,
$core.List<$core.int>? data, $core.List<$core.int>? data,
}) { }) {
@ -213,26 +228,26 @@ class ApplyChangesetParams extends $pb.GeneratedMessage {
} }
return _result; return _result;
} }
factory ApplyChangesetParams.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory DocChangeset.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.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
@$core.Deprecated( @$core.Deprecated(
'Using this can add significant overhead to your binary. ' 'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version') 'Will be removed in next major version')
ApplyChangesetParams clone() => ApplyChangesetParams()..mergeFromMessage(this); DocChangeset clone() => DocChangeset()..mergeFromMessage(this);
@$core.Deprecated( @$core.Deprecated(
'Using this can add significant overhead to your binary. ' 'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version') '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; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static ApplyChangesetParams create() => ApplyChangesetParams._(); static DocChangeset create() => DocChangeset._();
ApplyChangesetParams createEmptyInstance() => create(); DocChangeset createEmptyInstance() => create();
static $pb.PbList<ApplyChangesetParams> createRepeated() => $pb.PbList<ApplyChangesetParams>(); static $pb.PbList<DocChangeset> createRepeated() => $pb.PbList<DocChangeset>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static ApplyChangesetParams getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ApplyChangesetParams>(create); static DocChangeset getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<DocChangeset>(create);
static ApplyChangesetParams? _defaultInstance; static DocChangeset? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.String get id => $_getSZ(0); $core.String get id => $_getSZ(0);

View File

@ -25,33 +25,34 @@ const Doc$json = const {
'2': const [ '2': const [
const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
const {'1': 'data', '3': 2, '4': 1, '5': 12, '10': 'data'}, 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`. /// Descriptor for `Doc`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List docDescriptor = $convert.base64Decode('CgNEb2MSDgoCaWQYASABKAlSAmlkEhIKBGRhdGEYAiABKAxSBGRhdGE='); final $typed_data.Uint8List docDescriptor = $convert.base64Decode('CgNEb2MSDgoCaWQYASABKAlSAmlkEhIKBGRhdGEYAiABKAxSBGRhdGESGgoIcmV2aXNpb24YAyABKANSCHJldmlzaW9u');
@$core.Deprecated('Use saveDocParamsDescriptor instead') @$core.Deprecated('Use updateDocParamsDescriptor instead')
const SaveDocParams$json = const { const UpdateDocParams$json = const {
'1': 'SaveDocParams', '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 [ '2': const [
const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
const {'1': 'data', '3': 2, '4': 1, '5': 12, '10': 'data'}, const {'1': 'data', '3': 2, '4': 1, '5': 12, '10': 'data'},
], ],
}; };
/// Descriptor for `SaveDocParams`. Decode as a `google.protobuf.DescriptorProto`. /// Descriptor for `DocChangeset`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List saveDocParamsDescriptor = $convert.base64Decode('Cg1TYXZlRG9jUGFyYW1zEg4KAmlkGAEgASgJUgJpZBISCgRkYXRhGAIgASgMUgRkYXRh'); final $typed_data.Uint8List docChangesetDescriptor = $convert.base64Decode('CgxEb2NDaGFuZ2VzZXQSDgoCaWQYASABKAlSAmlkEhIKBGRhdGEYAiABKAxSBGRhdGE=');
@$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==');
@$core.Deprecated('Use queryDocParamsDescriptor instead') @$core.Deprecated('Use queryDocParamsDescriptor instead')
const QueryDocParams$json = const { const QueryDocParams$json = const {
'1': 'QueryDocParams', '1': 'QueryDocParams',

View File

@ -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 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 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 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 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'); 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, DocIdInvalid,
DocNotfound, DocNotfound,
WsConnectError, WsConnectError,
UndoFail,
RedoFail,
OutOfBound,
UserUnauthorized, UserUnauthorized,
InternalError, InternalError,
]; ];

View File

@ -15,13 +15,16 @@ const ErrorCode$json = const {
const {'1': 'DocIdInvalid', '2': 0}, const {'1': 'DocIdInvalid', '2': 0},
const {'1': 'DocNotfound', '2': 1}, const {'1': 'DocNotfound', '2': 1},
const {'1': 'WsConnectError', '2': 10}, 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': 'UserUnauthorized', '2': 999},
const {'1': 'InternalError', '2': 1000}, const {'1': 'InternalError', '2': 1000},
], ],
}; };
/// Descriptor for `ErrorCode`. Decode as a `google.protobuf.EnumDescriptorProto`. /// 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') @$core.Deprecated('Use docErrorDescriptor instead')
const DocError$json = const { const DocError$json = const {
'1': 'DocError', '1': 'DocError',

View File

@ -2,5 +2,5 @@
CREATE TABLE doc_table ( CREATE TABLE doc_table (
id TEXT NOT NULL PRIMARY KEY, id TEXT NOT NULL PRIMARY KEY,
data BLOB NOT NULL DEFAULT (x''), data BLOB NOT NULL DEFAULT (x''),
version BIGINT NOT NULL DEFAULT 0 revision BIGINT NOT NULL DEFAULT 0
); );

View File

@ -0,0 +1 @@
-- This file should undo anything in `up.sql`

View File

@ -0,0 +1 @@
-- Your SQL goes here

View File

@ -17,7 +17,7 @@ table! {
doc_table (id) { doc_table (id) {
id -> Text, id -> Text,
data -> Binary, 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,
);

View File

@ -57,8 +57,8 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
| "WsMessage" | "WsMessage"
| "CreateDocParams" | "CreateDocParams"
| "Doc" | "Doc"
| "SaveDocParams" | "UpdateDocParams"
| "ApplyChangesetParams" | "DocChangeset"
| "QueryDocParams" | "QueryDocParams"
| "WsDocumentData" | "WsDocumentData"
| "DocError" | "DocError"

View File

@ -30,9 +30,19 @@ strum = "0.21"
strum_macros = "0.21" strum_macros = "0.21"
dashmap = "4.0" dashmap = "4.0"
parking_lot = "0.11" 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] [dev-dependencies]
flowy-test = { path = "../flowy-test" } 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] [features]
http_server = [] http_server = []

View File

@ -20,19 +20,22 @@ pub struct Doc {
#[pb(index = 2)] #[pb(index = 2)]
pub data: Vec<u8>, pub data: Vec<u8>,
#[pb(index = 3)]
pub revision: i64,
} }
#[derive(ProtoBuf, Default, Debug, Clone)] #[derive(ProtoBuf, Default, Debug, Clone)]
pub struct SaveDocParams { pub struct UpdateDocParams {
#[pb(index = 1)] #[pb(index = 1)]
pub id: String, pub id: String,
#[pb(index = 2)] #[pb(index = 2)]
pub data: Vec<u8>, pub doc_data: Vec<u8>,
} }
#[derive(ProtoBuf, Default, Debug, Clone)] #[derive(ProtoBuf, Default, Debug, Clone)]
pub struct ApplyChangesetParams { pub struct DocChangeset {
#[pb(index = 1)] #[pb(index = 1)]
pub id: String, pub id: String,

View File

@ -41,6 +41,9 @@ impl DocError {
static_doc_error!(not_found, ErrorCode::DocNotfound); static_doc_error!(not_found, ErrorCode::DocNotfound);
static_doc_error!(unauthorized, ErrorCode::UserUnauthorized); static_doc_error!(unauthorized, ErrorCode::UserUnauthorized);
static_doc_error!(ws, ErrorCode::WsConnectError); 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<T>(e: T) -> DocError pub fn internal_error<T>(e: T) -> DocError
@ -61,6 +64,14 @@ pub enum ErrorCode {
#[display(fmt = "Document websocket error")] #[display(fmt = "Document websocket error")]
WsConnectError = 10, 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")] #[display(fmt = "UserUnauthorized")]
UserUnauthorized = 999, UserUnauthorized = 999,
@ -89,6 +100,10 @@ impl std::convert::From<std::io::Error> for DocError {
fn from(error: std::io::Error) -> Self { DocError::internal().context(error) } fn from(error: std::io::Error) -> Self { DocError::internal().context(error) }
} }
impl std::convert::From<serde_json::Error> for DocError {
fn from(error: serde_json::Error) -> Self { DocError::internal().context(error) }
}
// impl std::convert::From<::r2d2::Error> for DocError { // impl std::convert::From<::r2d2::Error> for DocError {
// fn from(error: r2d2::Error) -> Self { // fn from(error: r2d2::Error) -> Self {
// ErrorBuilder::new(ErrorCode::InternalError).error(error).build() } } // ErrorBuilder::new(ErrorCode::InternalError).error(error).build() } }

View File

@ -3,7 +3,7 @@ pub mod errors;
pub mod module; pub mod module;
mod observable; mod observable;
pub mod protobuf; pub mod protobuf;
mod services; pub mod services;
mod sql_tables; mod sql_tables;
#[macro_use] #[macro_use]

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
entities::doc::{ApplyChangesetParams, CreateDocParams, Doc, QueryDocParams}, entities::doc::{CreateDocParams, Doc, DocChangeset, QueryDocParams},
errors::DocError, 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 bytes::Bytes;
use diesel::SqliteConnection; use diesel::SqliteConnection;
@ -17,16 +17,13 @@ pub trait DocumentUser: Send + Sync {
pub struct FlowyDocument { pub struct FlowyDocument {
controller: Arc<DocController>, controller: Arc<DocController>,
doc_manager: Arc<OpenedDocManager>,
} }
impl FlowyDocument { impl FlowyDocument {
pub fn new(user: Arc<dyn DocumentUser>, ws_manager: Arc<RwLock<WsManager>>) -> FlowyDocument { pub fn new(user: Arc<dyn DocumentUser>, ws_manager: Arc<RwLock<WsManager>>) -> FlowyDocument {
let server = construct_doc_server(); let server = construct_doc_server();
let controller = Arc::new(DocController::new(server.clone(), user.clone())); let controller = Arc::new(DocController::new(server.clone(), user.clone(), ws_manager.clone()));
let doc_manager = Arc::new(OpenedDocManager::new(ws_manager, controller.clone())); Self { controller }
Self { controller, doc_manager }
} }
pub fn create(&self, params: CreateDocParams, conn: &SqliteConnection) -> Result<(), DocError> { 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> { pub fn delete(&self, params: QueryDocParams, conn: &SqliteConnection) -> Result<(), DocError> {
let _ = self.doc_manager.close(&params.doc_id)?; let _ = self.controller.delete(params, conn)?;
let _ = self.controller.delete(params.into(), conn)?;
Ok(()) Ok(())
} }
pub async fn open(&self, params: QueryDocParams, pool: Arc<ConnectionPool>) -> Result<Doc, DocError> { pub async fn open(&self, params: QueryDocParams, pool: Arc<ConnectionPool>) -> Result<Doc, DocError> {
let doc = match self.doc_manager.is_opened(&params.doc_id) { let open_doc = self.controller.open(params, pool).await?;
true => { Ok(open_doc.doc())
let data = self.doc_manager.read_doc(&params.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)
} }
pub async fn apply_changeset(&self, params: ApplyChangesetParams, pool: Arc<ConnectionPool>) -> Result<Doc, DocError> { pub async fn apply_changeset(&self, params: DocChangeset, pool: Arc<ConnectionPool>) -> Result<Doc, DocError> {
let _ = self.doc_manager.apply_changeset(&params.id, Bytes::from(params.data), pool).await?; // let _ = self.doc_manager.apply_changeset(&params.id,
let data = self.doc_manager.read_doc(&params.id).await?; // Bytes::from(params.data), pool).await?;
let doc = Doc { id: params.id, data }; //
Ok(doc) // // workaround: compare the rust's delta with flutter's delta. Will be removed
// // very soon
// let doc = self.doc_manager.read_doc(&params.id)?;
// Ok(doc)
unimplemented!()
} }
} }

View File

@ -229,6 +229,7 @@ pub struct Doc {
// message fields // message fields
pub id: ::std::string::String, pub id: ::std::string::String,
pub data: ::std::vec::Vec<u8>, pub data: ::std::vec::Vec<u8>,
pub revision: i64,
// special fields // special fields
pub unknown_fields: ::protobuf::UnknownFields, pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize, pub cached_size: ::protobuf::CachedSize,
@ -296,6 +297,21 @@ impl Doc {
pub fn take_data(&mut self) -> ::std::vec::Vec<u8> { pub fn take_data(&mut self) -> ::std::vec::Vec<u8> {
::std::mem::replace(&mut self.data, ::std::vec::Vec::new()) ::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 { impl ::protobuf::Message for Doc {
@ -313,6 +329,13 @@ impl ::protobuf::Message for Doc {
2 => { 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.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())?; ::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() { if !self.data.is_empty() {
my_size += ::protobuf::rt::bytes_size(2, &self.data); 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()); my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size); self.cached_size.set(my_size);
my_size my_size
@ -343,6 +369,9 @@ impl ::protobuf::Message for Doc {
if !self.data.is_empty() { if !self.data.is_empty() {
os.write_bytes(2, &self.data)?; os.write_bytes(2, &self.data)?;
} }
if self.revision != 0 {
os.write_int64(3, self.revision)?;
}
os.write_unknown_fields(self.get_unknown_fields())?; os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(()) ::std::result::Result::Ok(())
} }
@ -391,6 +420,11 @@ impl ::protobuf::Message for Doc {
|m: &Doc| { &m.data }, |m: &Doc| { &m.data },
|m: &mut Doc| { &mut 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>( ::protobuf::reflect::MessageDescriptor::new_pb_name::<Doc>(
"Doc", "Doc",
fields, fields,
@ -409,6 +443,7 @@ impl ::protobuf::Clear for Doc {
fn clear(&mut self) { fn clear(&mut self) {
self.id.clear(); self.id.clear();
self.data.clear(); self.data.clear();
self.revision = 0;
self.unknown_fields.clear(); self.unknown_fields.clear();
} }
} }
@ -426,23 +461,23 @@ impl ::protobuf::reflect::ProtobufValue for Doc {
} }
#[derive(PartialEq,Clone,Default)] #[derive(PartialEq,Clone,Default)]
pub struct SaveDocParams { pub struct UpdateDocParams {
// message fields // message fields
pub id: ::std::string::String, pub id: ::std::string::String,
pub data: ::std::vec::Vec<u8>, pub doc_data: ::std::vec::Vec<u8>,
// special fields // special fields
pub unknown_fields: ::protobuf::UnknownFields, pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize, pub cached_size: ::protobuf::CachedSize,
} }
impl<'a> ::std::default::Default for &'a SaveDocParams { impl<'a> ::std::default::Default for &'a UpdateDocParams {
fn default() -> &'a SaveDocParams { fn default() -> &'a UpdateDocParams {
<SaveDocParams as ::protobuf::Message>::default_instance() <UpdateDocParams as ::protobuf::Message>::default_instance()
} }
} }
impl SaveDocParams { impl UpdateDocParams {
pub fn new() -> SaveDocParams { pub fn new() -> UpdateDocParams {
::std::default::Default::default() ::std::default::Default::default()
} }
@ -472,34 +507,34 @@ impl SaveDocParams {
::std::mem::replace(&mut self.id, ::std::string::String::new()) ::std::mem::replace(&mut self.id, ::std::string::String::new())
} }
// bytes data = 2; // bytes doc_data = 2;
pub fn get_data(&self) -> &[u8] { pub fn get_doc_data(&self) -> &[u8] {
&self.data &self.doc_data
} }
pub fn clear_data(&mut self) { pub fn clear_doc_data(&mut self) {
self.data.clear(); self.doc_data.clear();
} }
// Param is passed by value, moved // Param is passed by value, moved
pub fn set_data(&mut self, v: ::std::vec::Vec<u8>) { pub fn set_doc_data(&mut self, v: ::std::vec::Vec<u8>) {
self.data = v; self.doc_data = v;
} }
// Mutable pointer to the field. // Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first. // If field is not initialized, it is initialized with default value first.
pub fn mut_data(&mut self) -> &mut ::std::vec::Vec<u8> { pub fn mut_doc_data(&mut self) -> &mut ::std::vec::Vec<u8> {
&mut self.data &mut self.doc_data
} }
// Take field // Take field
pub fn take_data(&mut self) -> ::std::vec::Vec<u8> { pub fn take_doc_data(&mut self) -> ::std::vec::Vec<u8> {
::std::mem::replace(&mut self.data, ::std::vec::Vec::new()) ::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 { fn is_initialized(&self) -> bool {
true true
} }
@ -512,7 +547,7 @@ impl ::protobuf::Message for SaveDocParams {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.id)?; ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.id)?;
}, },
2 => { 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())?; ::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() { if !self.id.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.id); my_size += ::protobuf::rt::string_size(1, &self.id);
} }
if !self.data.is_empty() { if !self.doc_data.is_empty() {
my_size += ::protobuf::rt::bytes_size(2, &self.data); my_size += ::protobuf::rt::bytes_size(2, &self.doc_data);
} }
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size); self.cached_size.set(my_size);
@ -541,8 +576,8 @@ impl ::protobuf::Message for SaveDocParams {
if !self.id.is_empty() { if !self.id.is_empty() {
os.write_string(1, &self.id)?; os.write_string(1, &self.id)?;
} }
if !self.data.is_empty() { if !self.doc_data.is_empty() {
os.write_bytes(2, &self.data)?; os.write_bytes(2, &self.doc_data)?;
} }
os.write_unknown_fields(self.get_unknown_fields())?; os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(()) ::std::result::Result::Ok(())
@ -574,8 +609,8 @@ impl ::protobuf::Message for SaveDocParams {
Self::descriptor_static() Self::descriptor_static()
} }
fn new() -> SaveDocParams { fn new() -> UpdateDocParams {
SaveDocParams::new() UpdateDocParams::new()
} }
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@ -584,50 +619,50 @@ impl ::protobuf::Message for SaveDocParams {
let mut fields = ::std::vec::Vec::new(); let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"id", "id",
|m: &SaveDocParams| { &m.id }, |m: &UpdateDocParams| { &m.id },
|m: &mut SaveDocParams| { &mut m.id }, |m: &mut UpdateDocParams| { &mut m.id },
)); ));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>( fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>(
"data", "doc_data",
|m: &SaveDocParams| { &m.data }, |m: &UpdateDocParams| { &m.doc_data },
|m: &mut SaveDocParams| { &mut m.data }, |m: &mut UpdateDocParams| { &mut m.doc_data },
)); ));
::protobuf::reflect::MessageDescriptor::new_pb_name::<SaveDocParams>( ::protobuf::reflect::MessageDescriptor::new_pb_name::<UpdateDocParams>(
"SaveDocParams", "UpdateDocParams",
fields, fields,
file_descriptor_proto() file_descriptor_proto()
) )
}) })
} }
fn default_instance() -> &'static SaveDocParams { fn default_instance() -> &'static UpdateDocParams {
static instance: ::protobuf::rt::LazyV2<SaveDocParams> = ::protobuf::rt::LazyV2::INIT; static instance: ::protobuf::rt::LazyV2<UpdateDocParams> = ::protobuf::rt::LazyV2::INIT;
instance.get(SaveDocParams::new) instance.get(UpdateDocParams::new)
} }
} }
impl ::protobuf::Clear for SaveDocParams { impl ::protobuf::Clear for UpdateDocParams {
fn clear(&mut self) { fn clear(&mut self) {
self.id.clear(); self.id.clear();
self.data.clear(); self.doc_data.clear();
self.unknown_fields.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 { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f) ::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 { fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self) ::protobuf::reflect::ReflectValueRef::Message(self)
} }
} }
#[derive(PartialEq,Clone,Default)] #[derive(PartialEq,Clone,Default)]
pub struct ApplyChangesetParams { pub struct DocChangeset {
// message fields // message fields
pub id: ::std::string::String, pub id: ::std::string::String,
pub data: ::std::vec::Vec<u8>, pub data: ::std::vec::Vec<u8>,
@ -636,14 +671,14 @@ pub struct ApplyChangesetParams {
pub cached_size: ::protobuf::CachedSize, pub cached_size: ::protobuf::CachedSize,
} }
impl<'a> ::std::default::Default for &'a ApplyChangesetParams { impl<'a> ::std::default::Default for &'a DocChangeset {
fn default() -> &'a ApplyChangesetParams { fn default() -> &'a DocChangeset {
<ApplyChangesetParams as ::protobuf::Message>::default_instance() <DocChangeset as ::protobuf::Message>::default_instance()
} }
} }
impl ApplyChangesetParams { impl DocChangeset {
pub fn new() -> ApplyChangesetParams { pub fn new() -> DocChangeset {
::std::default::Default::default() ::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 { fn is_initialized(&self) -> bool {
true true
} }
@ -775,8 +810,8 @@ impl ::protobuf::Message for ApplyChangesetParams {
Self::descriptor_static() Self::descriptor_static()
} }
fn new() -> ApplyChangesetParams { fn new() -> DocChangeset {
ApplyChangesetParams::new() DocChangeset::new()
} }
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@ -785,29 +820,29 @@ impl ::protobuf::Message for ApplyChangesetParams {
let mut fields = ::std::vec::Vec::new(); let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"id", "id",
|m: &ApplyChangesetParams| { &m.id }, |m: &DocChangeset| { &m.id },
|m: &mut ApplyChangesetParams| { &mut m.id }, |m: &mut DocChangeset| { &mut m.id },
)); ));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>( fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>(
"data", "data",
|m: &ApplyChangesetParams| { &m.data }, |m: &DocChangeset| { &m.data },
|m: &mut ApplyChangesetParams| { &mut m.data }, |m: &mut DocChangeset| { &mut m.data },
)); ));
::protobuf::reflect::MessageDescriptor::new_pb_name::<ApplyChangesetParams>( ::protobuf::reflect::MessageDescriptor::new_pb_name::<DocChangeset>(
"ApplyChangesetParams", "DocChangeset",
fields, fields,
file_descriptor_proto() file_descriptor_proto()
) )
}) })
} }
fn default_instance() -> &'static ApplyChangesetParams { fn default_instance() -> &'static DocChangeset {
static instance: ::protobuf::rt::LazyV2<ApplyChangesetParams> = ::protobuf::rt::LazyV2::INIT; static instance: ::protobuf::rt::LazyV2<DocChangeset> = ::protobuf::rt::LazyV2::INIT;
instance.get(ApplyChangesetParams::new) instance.get(DocChangeset::new)
} }
} }
impl ::protobuf::Clear for ApplyChangesetParams { impl ::protobuf::Clear for DocChangeset {
fn clear(&mut self) { fn clear(&mut self) {
self.id.clear(); self.id.clear();
self.data.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 { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f) ::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 { fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self) ::protobuf::reflect::ReflectValueRef::Message(self)
} }
@ -988,44 +1023,48 @@ impl ::protobuf::reflect::ProtobufValue for QueryDocParams {
static file_descriptor_proto_data: &'static [u8] = b"\ static file_descriptor_proto_data: &'static [u8] = b"\
\n\tdoc.proto\"5\n\x0fCreateDocParams\x12\x0e\n\x02id\x18\x01\x20\x01(\t\ \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\ \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\ (\x0cR\x04data\x12\x1a\n\x08revision\x18\x03\x20\x01(\x03R\x08revision\"\
\x02id\x12\x12\n\x04data\x18\x02\x20\x01(\x0cR\x04data\":\n\x14ApplyChan\ <\n\x0fUpdateDocParams\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x19\
gesetParams\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x12\n\x04data\ \n\x08doc_data\x18\x02\x20\x01(\x0cR\x07docData\"2\n\x0cDocChangeset\x12\
\x18\x02\x20\x01(\x0cR\x04data\"'\n\x0eQueryDocParams\x12\x15\n\x06doc_i\ \x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x12\n\x04data\x18\x02\x20\x01\
d\x18\x01\x20\x01(\tR\x05docIdJ\xf9\x04\n\x06\x12\x04\0\0\x14\x01\n\x08\ (\x0cR\x04data\"'\n\x0eQueryDocParams\x12\x15\n\x06doc_id\x18\x01\x20\
\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x05\x01\n\n\n\ \x01(\tR\x05docIdJ\xb0\x05\n\x06\x12\x04\0\0\x15\x01\n\x08\n\x01\x0c\x12\
\x03\x04\0\x01\x12\x03\x02\x08\x17\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\ \x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x05\x01\n\n\n\x03\x04\0\x01\
\x04\x12\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\x03\x04\n\n\x0c\n\x05\x04\0\ \x12\x03\x02\x08\x17\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x12\n\x0c\n\
\x02\0\x01\x12\x03\x03\x0b\r\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x10\ \x05\x04\0\x02\0\x05\x12\x03\x03\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\
\x11\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\x13\n\x0c\n\x05\x04\0\x02\ \x03\x03\x0b\r\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x10\x11\n\x0b\n\
\x01\x05\x12\x03\x04\x04\t\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\n\ \x04\x04\0\x02\x01\x12\x03\x04\x04\x13\n\x0c\n\x05\x04\0\x02\x01\x05\x12\
\x0e\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x11\x12\n\n\n\x02\x04\x01\ \x03\x04\x04\t\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\n\x0e\n\x0c\n\
\x12\x04\x06\0\t\x01\n\n\n\x03\x04\x01\x01\x12\x03\x06\x08\x0b\n\x0b\n\ \x05\x04\0\x02\x01\x03\x12\x03\x04\x11\x12\n\n\n\x02\x04\x01\x12\x04\x06\
\x04\x04\x01\x02\0\x12\x03\x07\x04\x12\n\x0c\n\x05\x04\x01\x02\0\x05\x12\ \0\n\x01\n\n\n\x03\x04\x01\x01\x12\x03\x06\x08\x0b\n\x0b\n\x04\x04\x01\
\x03\x07\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x07\x0b\r\n\x0c\n\ \x02\0\x12\x03\x07\x04\x12\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\x07\x04\
\x05\x04\x01\x02\0\x03\x12\x03\x07\x10\x11\n\x0b\n\x04\x04\x01\x02\x01\ \n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x07\x0b\r\n\x0c\n\x05\x04\x01\
\x12\x03\x08\x04\x13\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\x08\x04\t\n\ \x02\0\x03\x12\x03\x07\x10\x11\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\x08\
\x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\x08\n\x0e\n\x0c\n\x05\x04\x01\x02\ \x04\x13\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\x08\x04\t\n\x0c\n\x05\
\x01\x03\x12\x03\x08\x11\x12\n\n\n\x02\x04\x02\x12\x04\n\0\r\x01\n\n\n\ \x04\x01\x02\x01\x01\x12\x03\x08\n\x0e\n\x0c\n\x05\x04\x01\x02\x01\x03\
\x03\x04\x02\x01\x12\x03\n\x08\x15\n\x0b\n\x04\x04\x02\x02\0\x12\x03\x0b\ \x12\x03\x08\x11\x12\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\t\x04\x17\n\x0c\
\x04\x12\n\x0c\n\x05\x04\x02\x02\0\x05\x12\x03\x0b\x04\n\n\x0c\n\x05\x04\ \n\x05\x04\x01\x02\x02\x05\x12\x03\t\x04\t\n\x0c\n\x05\x04\x01\x02\x02\
\x02\x02\0\x01\x12\x03\x0b\x0b\r\n\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\ \x01\x12\x03\t\n\x12\n\x0c\n\x05\x04\x01\x02\x02\x03\x12\x03\t\x15\x16\n\
\x0b\x10\x11\n\x0b\n\x04\x04\x02\x02\x01\x12\x03\x0c\x04\x13\n\x0c\n\x05\ \n\n\x02\x04\x02\x12\x04\x0b\0\x0e\x01\n\n\n\x03\x04\x02\x01\x12\x03\x0b\
\x04\x02\x02\x01\x05\x12\x03\x0c\x04\t\n\x0c\n\x05\x04\x02\x02\x01\x01\ \x08\x17\n\x0b\n\x04\x04\x02\x02\0\x12\x03\x0c\x04\x12\n\x0c\n\x05\x04\
\x12\x03\x0c\n\x0e\n\x0c\n\x05\x04\x02\x02\x01\x03\x12\x03\x0c\x11\x12\n\ \x02\x02\0\x05\x12\x03\x0c\x04\n\n\x0c\n\x05\x04\x02\x02\0\x01\x12\x03\
\n\n\x02\x04\x03\x12\x04\x0e\0\x11\x01\n\n\n\x03\x04\x03\x01\x12\x03\x0e\ \x0c\x0b\r\n\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\x0c\x10\x11\n\x0b\n\x04\
\x08\x1c\n\x0b\n\x04\x04\x03\x02\0\x12\x03\x0f\x04\x12\n\x0c\n\x05\x04\ \x04\x02\x02\x01\x12\x03\r\x04\x17\n\x0c\n\x05\x04\x02\x02\x01\x05\x12\
\x03\x02\0\x05\x12\x03\x0f\x04\n\n\x0c\n\x05\x04\x03\x02\0\x01\x12\x03\ \x03\r\x04\t\n\x0c\n\x05\x04\x02\x02\x01\x01\x12\x03\r\n\x12\n\x0c\n\x05\
\x0f\x0b\r\n\x0c\n\x05\x04\x03\x02\0\x03\x12\x03\x0f\x10\x11\n\x0b\n\x04\ \x04\x02\x02\x01\x03\x12\x03\r\x15\x16\n\n\n\x02\x04\x03\x12\x04\x0f\0\
\x04\x03\x02\x01\x12\x03\x10\x04\x13\n\x0c\n\x05\x04\x03\x02\x01\x05\x12\ \x12\x01\n\n\n\x03\x04\x03\x01\x12\x03\x0f\x08\x14\n\x0b\n\x04\x04\x03\
\x03\x10\x04\t\n\x0c\n\x05\x04\x03\x02\x01\x01\x12\x03\x10\n\x0e\n\x0c\n\ \x02\0\x12\x03\x10\x04\x12\n\x0c\n\x05\x04\x03\x02\0\x05\x12\x03\x10\x04\
\x05\x04\x03\x02\x01\x03\x12\x03\x10\x11\x12\n\n\n\x02\x04\x04\x12\x04\ \n\n\x0c\n\x05\x04\x03\x02\0\x01\x12\x03\x10\x0b\r\n\x0c\n\x05\x04\x03\
\x12\0\x14\x01\n\n\n\x03\x04\x04\x01\x12\x03\x12\x08\x16\n\x0b\n\x04\x04\ \x02\0\x03\x12\x03\x10\x10\x11\n\x0b\n\x04\x04\x03\x02\x01\x12\x03\x11\
\x04\x02\0\x12\x03\x13\x04\x16\n\x0c\n\x05\x04\x04\x02\0\x05\x12\x03\x13\ \x04\x13\n\x0c\n\x05\x04\x03\x02\x01\x05\x12\x03\x11\x04\t\n\x0c\n\x05\
\x04\n\n\x0c\n\x05\x04\x04\x02\0\x01\x12\x03\x13\x0b\x11\n\x0c\n\x05\x04\ \x04\x03\x02\x01\x01\x12\x03\x11\n\x0e\n\x0c\n\x05\x04\x03\x02\x01\x03\
\x04\x02\0\x03\x12\x03\x13\x14\x15b\x06proto3\ \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; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -218,6 +218,9 @@ pub enum ErrorCode {
DocIdInvalid = 0, DocIdInvalid = 0,
DocNotfound = 1, DocNotfound = 1,
WsConnectError = 10, WsConnectError = 10,
UndoFail = 200,
RedoFail = 201,
OutOfBound = 202,
UserUnauthorized = 999, UserUnauthorized = 999,
InternalError = 1000, InternalError = 1000,
} }
@ -232,6 +235,9 @@ impl ::protobuf::ProtobufEnum for ErrorCode {
0 => ::std::option::Option::Some(ErrorCode::DocIdInvalid), 0 => ::std::option::Option::Some(ErrorCode::DocIdInvalid),
1 => ::std::option::Option::Some(ErrorCode::DocNotfound), 1 => ::std::option::Option::Some(ErrorCode::DocNotfound),
10 => ::std::option::Option::Some(ErrorCode::WsConnectError), 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), 999 => ::std::option::Option::Some(ErrorCode::UserUnauthorized),
1000 => ::std::option::Option::Some(ErrorCode::InternalError), 1000 => ::std::option::Option::Some(ErrorCode::InternalError),
_ => ::std::option::Option::None _ => ::std::option::Option::None
@ -243,6 +249,9 @@ impl ::protobuf::ProtobufEnum for ErrorCode {
ErrorCode::DocIdInvalid, ErrorCode::DocIdInvalid,
ErrorCode::DocNotfound, ErrorCode::DocNotfound,
ErrorCode::WsConnectError, ErrorCode::WsConnectError,
ErrorCode::UndoFail,
ErrorCode::RedoFail,
ErrorCode::OutOfBound,
ErrorCode::UserUnauthorized, ErrorCode::UserUnauthorized,
ErrorCode::InternalError, ErrorCode::InternalError,
]; ];
@ -274,29 +283,36 @@ impl ::protobuf::reflect::ProtobufValue for ErrorCode {
static file_descriptor_proto_data: &'static [u8] = b"\ static file_descriptor_proto_data: &'static [u8] = b"\
\n\x0cerrors.proto\"<\n\x08DocError\x12\x1e\n\x04code\x18\x01\x20\x01(\ \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\ \x0e2\n.ErrorCodeR\x04code\x12\x10\n\x03msg\x18\x02\x20\x01(\tR\x03msg*\
\n\tErrorCode\x12\x10\n\x0cDocIdInvalid\x10\0\x12\x0f\n\x0bDocNotfound\ \x9c\x01\n\tErrorCode\x12\x10\n\x0cDocIdInvalid\x10\0\x12\x0f\n\x0bDocNo\
\x10\x01\x12\x12\n\x0eWsConnectError\x10\n\x12\x15\n\x10UserUnauthorized\ tfound\x10\x01\x12\x12\n\x0eWsConnectError\x10\n\x12\r\n\x08UndoFail\x10\
\x10\xe7\x07\x12\x12\n\rInternalError\x10\xe8\x07J\xfd\x02\n\x06\x12\x04\ \xc8\x01\x12\r\n\x08RedoFail\x10\xc9\x01\x12\x0f\n\nOutOfBound\x10\xca\
\0\0\x0c\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\ \x01\x12\x15\n\x10UserUnauthorized\x10\xe7\x07\x12\x12\n\rInternalError\
\0\x05\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x10\n\x0b\n\x04\x04\0\x02\ \x10\xe8\x07J\xf8\x03\n\x06\x12\x04\0\0\x0f\x01\n\x08\n\x01\x0c\x12\x03\
\0\x12\x03\x03\x04\x17\n\x0c\n\x05\x04\0\x02\0\x06\x12\x03\x03\x04\r\n\ \0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x05\x01\n\n\n\x03\x04\0\x01\x12\
\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x0e\x12\n\x0c\n\x05\x04\0\x02\0\ \x03\x02\x08\x10\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x17\n\x0c\n\x05\
\x03\x12\x03\x03\x15\x16\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\x13\n\ \x04\0\x02\0\x06\x12\x03\x03\x04\r\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\
\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\x02\x01\ \x03\x0e\x12\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x15\x16\n\x0b\n\x04\
\x01\x12\x03\x04\x0b\x0e\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x11\ \x04\0\x02\x01\x12\x03\x04\x04\x13\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\
\x12\n\n\n\x02\x05\0\x12\x04\x06\0\x0c\x01\n\n\n\x03\x05\0\x01\x12\x03\ \x04\x04\n\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\x0b\x0e\n\x0c\n\x05\
\x06\x05\x0e\n\x0b\n\x04\x05\0\x02\0\x12\x03\x07\x04\x15\n\x0c\n\x05\x05\ \x04\0\x02\x01\x03\x12\x03\x04\x11\x12\n\n\n\x02\x05\0\x12\x04\x06\0\x0f\
\0\x02\0\x01\x12\x03\x07\x04\x10\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x07\ \x01\n\n\n\x03\x05\0\x01\x12\x03\x06\x05\x0e\n\x0b\n\x04\x05\0\x02\0\x12\
\x13\x14\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x08\x04\x14\n\x0c\n\x05\x05\0\ \x03\x07\x04\x15\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x07\x04\x10\n\x0c\n\
\x02\x01\x01\x12\x03\x08\x04\x0f\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\ \x05\x05\0\x02\0\x02\x12\x03\x07\x13\x14\n\x0b\n\x04\x05\0\x02\x01\x12\
\x08\x12\x13\n\x0b\n\x04\x05\0\x02\x02\x12\x03\t\x04\x18\n\x0c\n\x05\x05\ \x03\x08\x04\x14\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x08\x04\x0f\n\x0c\
\0\x02\x02\x01\x12\x03\t\x04\x12\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\t\ \n\x05\x05\0\x02\x01\x02\x12\x03\x08\x12\x13\n\x0b\n\x04\x05\0\x02\x02\
\x15\x17\n\x0b\n\x04\x05\0\x02\x03\x12\x03\n\x04\x1b\n\x0c\n\x05\x05\0\ \x12\x03\t\x04\x18\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\t\x04\x12\n\x0c\
\x02\x03\x01\x12\x03\n\x04\x14\n\x0c\n\x05\x05\0\x02\x03\x02\x12\x03\n\ \n\x05\x05\0\x02\x02\x02\x12\x03\t\x15\x17\n\x0b\n\x04\x05\0\x02\x03\x12\
\x17\x1a\n\x0b\n\x04\x05\0\x02\x04\x12\x03\x0b\x04\x19\n\x0c\n\x05\x05\0\ \x03\n\x04\x13\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\n\x04\x0c\n\x0c\n\
\x02\x04\x01\x12\x03\x0b\x04\x11\n\x0c\n\x05\x05\0\x02\x04\x02\x12\x03\ \x05\x05\0\x02\x03\x02\x12\x03\n\x0f\x12\n\x0b\n\x04\x05\0\x02\x04\x12\
\x0b\x14\x18b\x06proto3\ \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; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -7,12 +7,13 @@ message CreateDocParams {
message Doc { message Doc {
string id = 1; string id = 1;
bytes data = 2; bytes data = 2;
int64 revision = 3;
} }
message SaveDocParams { message UpdateDocParams {
string id = 1; string id = 1;
bytes data = 2; bytes doc_data = 2;
} }
message ApplyChangesetParams { message DocChangeset {
string id = 1; string id = 1;
bytes data = 2; bytes data = 2;
} }

View File

@ -8,6 +8,9 @@ enum ErrorCode {
DocIdInvalid = 0; DocIdInvalid = 0;
DocNotfound = 1; DocNotfound = 1;
WsConnectError = 10; WsConnectError = 10;
UndoFail = 200;
RedoFail = 201;
OutOfBound = 202;
UserUnauthorized = 999; UserUnauthorized = 999;
InternalError = 1000; InternalError = 1000;
} }

View File

@ -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<DocId, Arc<OpenedDoc>>,
}
impl DocCache {
pub(crate) fn new() -> Self { Self { doc_map: DashMap::new() } }
pub(crate) fn set(&self, doc: Arc<OpenedDoc>) -> 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<Arc<OpenedDoc>, 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") }

View File

@ -1,8 +1,8 @@
use crate::{client::DocumentData, errors::OTError}; use crate::{errors::DocError, services::doc::DocumentData};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
impl<T: AsRef<str>> DocumentData for T { impl<T: AsRef<str>> DocumentData for T {
fn into_string(self) -> Result<String, OTError> { Ok(self.as_ref().to_string()) } fn into_string(self) -> Result<String, DocError> { Ok(self.as_ref().to_string()) }
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@ -11,7 +11,7 @@ pub struct ImageData {
} }
impl DocumentData for ImageData { impl DocumentData for ImageData {
fn into_string(self) -> Result<String, OTError> { fn into_string(self) -> Result<String, DocError> {
let s = serde_json::to_string(&self)?; let s = serde_json::to_string(&self)?;
Ok(s) Ok(s)
} }

View File

@ -1,12 +1,13 @@
use crate::{ use crate::{
client::{view::View, History, RevId, UndoResult, RECORD_THRESHOLD}, errors::DocError,
core::*, services::doc::{view::View, History, UndoResult, RECORD_THRESHOLD},
errors::{ErrorBuilder, OTError, OTErrorCode, OTErrorCode::*},
}; };
use bytes::Bytes;
use flowy_ot::core::*;
use std::convert::TryInto; use std::convert::TryInto;
pub trait DocumentData { pub trait DocumentData {
fn into_string(self) -> Result<String, OTError>; fn into_string(self) -> Result<String, DocError>;
} }
pub trait CustomDocument { pub trait CustomDocument {
@ -23,6 +24,19 @@ impl CustomDocument for FlowyDoc {
fn init_delta() -> Delta { DeltaBuilder::new().insert("\n").build() } 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 { pub struct Document {
delta: Delta, delta: Delta,
history: History, history: History,
@ -44,7 +58,7 @@ impl Document {
} }
} }
pub fn from_json(json: &str) -> Result<Self, OTError> { pub fn from_json(json: &str) -> Result<Self, DocError> {
let delta = Delta::from_json(json)?; let delta = Delta::from_json(json)?;
Ok(Self::from_delta(delta)) Ok(Self::from_delta(delta))
} }
@ -55,18 +69,20 @@ impl Document {
pub fn to_string(&self) -> String { self.delta.apply("").unwrap() } pub fn to_string(&self) -> String { self.delta.apply("").unwrap() }
pub fn apply_changeset<T>(&mut self, changeset: T) -> Result<(), OTError> pub fn apply_delta(&mut self, data: Bytes) -> Result<(), DocError> {
where let new_delta = Delta::from_bytes(data.to_vec())?;
T: TryInto<Delta, Error = OTError>,
{ log::debug!("Apply delta: {}", new_delta);
let new_delta: Delta = changeset.try_into()?;
log::debug!("Delta changeset: {}", new_delta); let rev_id = self.next_rev_id();
let revision = Revision::new(rev_id, new_delta.clone());
let _ = self.add_delta(&new_delta)?; let _ = self.add_delta(&new_delta)?;
log::debug!("Document: {}", self.to_json()); log::debug!("Document: {}", self.to_json());
Ok(()) Ok(())
} }
pub fn insert<T: DocumentData>(&mut self, index: usize, data: T) -> Result<Delta, OTError> { pub fn insert<T: DocumentData>(&mut self, index: usize, data: T) -> Result<Delta, DocError> {
let interval = Interval::new(index, index); let interval = Interval::new(index, index);
let _ = validate_interval(&self.delta, &interval)?; let _ = validate_interval(&self.delta, &interval)?;
@ -77,7 +93,7 @@ impl Document {
Ok(delta) Ok(delta)
} }
pub fn delete(&mut self, interval: Interval) -> Result<Delta, OTError> { pub fn delete(&mut self, interval: Interval) -> Result<Delta, DocError> {
let _ = validate_interval(&self.delta, &interval)?; let _ = validate_interval(&self.delta, &interval)?;
debug_assert_eq!(interval.is_empty(), false); debug_assert_eq!(interval.is_empty(), false);
let delete = self.view.delete(&self.delta, interval)?; let delete = self.view.delete(&self.delta, interval)?;
@ -88,7 +104,7 @@ impl Document {
Ok(delete) 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)?; let _ = validate_interval(&self.delta, &interval)?;
log::trace!("format with {} at {}", attribute, interval); log::trace!("format with {} at {}", attribute, interval);
let format_delta = self.view.format(&self.delta, attribute.clone(), interval).unwrap(); let format_delta = self.view.format(&self.delta, attribute.clone(), interval).unwrap();
@ -98,7 +114,7 @@ impl Document {
Ok(()) Ok(())
} }
pub fn replace<T: DocumentData>(&mut self, interval: Interval, data: T) -> Result<Delta, OTError> { pub fn replace<T: DocumentData>(&mut self, interval: Interval, data: T) -> Result<Delta, DocError> {
let _ = validate_interval(&self.delta, &interval)?; let _ = validate_interval(&self.delta, &interval)?;
let mut delta = Delta::default(); let mut delta = Delta::default();
let text = data.into_string()?; let text = data.into_string()?;
@ -120,9 +136,9 @@ impl Document {
pub fn can_redo(&self) -> bool { self.history.can_redo() } pub fn can_redo(&self) -> bool { self.history.can_redo() }
pub fn undo(&mut self) -> Result<UndoResult, OTError> { pub fn undo(&mut self) -> Result<UndoResult, DocError> {
match self.history.undo() { 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) => { Some(undo_delta) => {
let (new_delta, inverted_delta) = self.invert_change(&undo_delta)?; let (new_delta, inverted_delta) = self.invert_change(&undo_delta)?;
let result = UndoResult::success(new_delta.target_len as usize); let result = UndoResult::success(new_delta.target_len as usize);
@ -134,9 +150,9 @@ impl Document {
} }
} }
pub fn redo(&mut self) -> Result<UndoResult, OTError> { pub fn redo(&mut self) -> Result<UndoResult, DocError> {
match self.history.redo() { match self.history.redo() {
None => Err(ErrorBuilder::new(RedoFail).build()), None => Err(DocError::redo()),
Some(redo_delta) => { Some(redo_delta) => {
let (new_delta, inverted_delta) = self.invert_change(&redo_delta)?; let (new_delta, inverted_delta) = self.invert_change(&redo_delta)?;
let result = UndoResult::success(new_delta.target_len as usize); let result = UndoResult::success(new_delta.target_len as usize);
@ -154,7 +170,7 @@ impl Document {
} }
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 composed_delta = self.delta.compose(delta)?;
let mut undo_delta = delta.invert(&self.delta); let mut undo_delta = delta.invert(&self.delta);
self.rev_id_counter += 1; self.rev_id_counter += 1;
@ -181,7 +197,7 @@ impl Document {
Ok(()) 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) // c = a.compose(b)
// d = b.invert(a) // d = b.invert(a)
// a = c.compose(d) // a = c.compose(d)
@ -195,10 +211,10 @@ impl Document {
fn next_rev_id(&self) -> RevId { RevId(self.rev_id_counter) } 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 { if delta.target_len < interval.end {
log::error!("{:?} out of bounds. should 0..{}", interval, delta.target_len); 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(()) Ok(())
} }

View File

@ -1,7 +1,5 @@
use crate::{ use crate::services::doc::extensions::DeleteExt;
client::extensions::DeleteExt, use flowy_ot::core::{Delta, DeltaBuilder, Interval};
core::{Delta, DeltaBuilder, Interval},
};
pub struct DefaultDelete {} pub struct DefaultDelete {}
impl DeleteExt for DefaultDelete { impl DeleteExt for DefaultDelete {

View File

@ -1,7 +1,5 @@
use crate::{ use crate::services::doc::{extensions::DeleteExt, util::is_newline};
client::{extensions::DeleteExt, util::is_newline}, use flowy_ot::core::{plain_attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval, NEW_LINE};
core::{plain_attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval, NEW_LINE},
};
pub struct PreserveLineFormatOnMerge {} pub struct PreserveLineFormatOnMerge {}
impl DeleteExt for PreserveLineFormatOnMerge { impl DeleteExt for PreserveLineFormatOnMerge {

View File

@ -1,7 +1,5 @@
use crate::{ use crate::services::doc::util::find_newline;
client::util::find_newline, use flowy_ot::core::{plain_attributes, Attribute, AttributeScope, Delta, Operation};
core::{plain_attributes, Attribute, AttributeScope, Delta, Operation},
};
pub(crate) fn line_break(op: &Operation, attribute: &Attribute, scope: AttributeScope) -> Delta { pub(crate) fn line_break(op: &Operation, attribute: &Attribute, scope: AttributeScope) -> Delta {
let mut new_delta = Delta::new(); let mut new_delta = Delta::new();

View File

@ -1,10 +1,8 @@
use crate::{ use crate::services::doc::{
client::{ extensions::{format::helper::line_break, FormatExt},
extensions::{format::helper::line_break, FormatExt}, util::find_newline,
util::find_newline,
},
core::{plain_attributes, Attribute, AttributeScope, Delta, DeltaBuilder, DeltaIter, Interval},
}; };
use flowy_ot::core::{plain_attributes, Attribute, AttributeScope, Delta, DeltaBuilder, DeltaIter, Interval};
pub struct ResolveBlockFormat {} pub struct ResolveBlockFormat {}
impl FormatExt for ResolveBlockFormat { impl FormatExt for ResolveBlockFormat {

View File

@ -1,10 +1,8 @@
use crate::{ use crate::services::doc::{
client::{ extensions::{format::helper::line_break, FormatExt},
extensions::{format::helper::line_break, FormatExt}, util::find_newline,
util::find_newline,
},
core::{Attribute, AttributeScope, Delta, DeltaBuilder, DeltaIter, Interval},
}; };
use flowy_ot::core::{Attribute, AttributeScope, Delta, DeltaBuilder, DeltaIter, Interval};
pub struct ResolveInlineFormat {} pub struct ResolveInlineFormat {}
impl FormatExt for ResolveInlineFormat { impl FormatExt for ResolveInlineFormat {

View File

@ -1,9 +1,5 @@
use crate::{ use crate::services::doc::{extensions::InsertExt, util::is_newline};
client::{extensions::InsertExt, util::is_newline}, use flowy_ot::core::{attributes_except_header, is_empty_line_at_index, AttributeKey, Delta, DeltaBuilder, DeltaIter};
core::{AttributeKey, Delta, DeltaBuilder, DeltaIter},
};
use crate::core::{attributes_except_header, is_empty_line_at_index};
pub struct AutoExitBlock {} pub struct AutoExitBlock {}

View File

@ -1,7 +1,8 @@
use crate::{ use crate::services::doc::{extensions::InsertExt, util::is_whitespace};
client::{extensions::InsertExt, util::is_whitespace}, use bytecount::num_chars;
core::{Delta, DeltaIter}, use flowy_ot::core::{plain_attributes, Attribute, Attributes, Delta, DeltaBuilder, DeltaIter};
}; use std::cmp::min;
use url::Url;
pub struct AutoFormatExt {} pub struct AutoFormatExt {}
impl InsertExt for 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 { pub enum AutoFormatter {
Url(Url), Url(Url),
} }

View File

@ -1,7 +1,5 @@
use crate::{ use crate::services::doc::extensions::InsertExt;
client::extensions::InsertExt, use flowy_ot::core::{AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, NEW_LINE};
core::{AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, NEW_LINE},
};
pub struct DefaultInsertAttribute {} pub struct DefaultInsertAttribute {}
impl InsertExt for DefaultInsertAttribute { impl InsertExt for DefaultInsertAttribute {

View File

@ -1,6 +1,8 @@
use crate::services::doc::extensions::InsertExt;
pub use auto_exit_block::*; pub use auto_exit_block::*;
pub use auto_format::*; pub use auto_format::*;
pub use default_insert::*; pub use default_insert::*;
use flowy_ot::core::Delta;
pub use preserve_block_format::*; pub use preserve_block_format::*;
pub use preserve_inline_format::*; pub use preserve_inline_format::*;
pub use reset_format_on_new_line::*; pub use reset_format_on_new_line::*;
@ -12,8 +14,6 @@ mod preserve_block_format;
mod preserve_inline_format; mod preserve_inline_format;
mod reset_format_on_new_line; mod reset_format_on_new_line;
use crate::{client::extensions::InsertExt, core::Delta};
pub struct InsertEmbedsExt {} pub struct InsertEmbedsExt {}
impl InsertExt for InsertEmbedsExt { impl InsertExt for InsertEmbedsExt {
fn ext_name(&self) -> &str { "InsertEmbedsExt" } fn ext_name(&self) -> &str { "InsertEmbedsExt" }

View File

@ -1,6 +1,14 @@
use crate::{ use crate::services::doc::{extensions::InsertExt, util::is_newline};
client::{extensions::InsertExt, util::is_newline}, use flowy_ot::core::{
core::{attributes_except_header, plain_attributes, Attribute, AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, NEW_LINE}, attributes_except_header,
plain_attributes,
Attribute,
AttributeKey,
Attributes,
Delta,
DeltaBuilder,
DeltaIter,
NEW_LINE,
}; };
pub struct PreserveBlockFormatOnInsert {} pub struct PreserveBlockFormatOnInsert {}

View File

@ -1,10 +1,8 @@
use crate::{ use crate::services::doc::{
client::{ extensions::InsertExt,
extensions::InsertExt, util::{contain_newline, is_newline},
util::{contain_newline, is_newline},
},
core::{plain_attributes, AttributeKey, Delta, DeltaBuilder, DeltaIter, OpNewline, NEW_LINE},
}; };
use flowy_ot::core::{plain_attributes, AttributeKey, Delta, DeltaBuilder, DeltaIter, OpNewline, NEW_LINE};
pub struct PreserveInlineFormat {} pub struct PreserveInlineFormat {}
impl InsertExt for PreserveInlineFormat { impl InsertExt for PreserveInlineFormat {

View File

@ -1,7 +1,5 @@
use crate::{ use crate::services::doc::{extensions::InsertExt, util::is_newline};
client::{extensions::InsertExt, util::is_newline}, use flowy_ot::core::{AttributeKey, Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, NEW_LINE};
core::{AttributeKey, Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, NEW_LINE},
};
pub struct ResetLineFormatOnNewLine {} pub struct ResetLineFormatOnNewLine {}
impl InsertExt for ResetLineFormatOnNewLine { impl InsertExt for ResetLineFormatOnNewLine {

View File

@ -2,7 +2,7 @@ pub use delete::*;
pub use format::*; pub use format::*;
pub use insert::*; pub use insert::*;
use crate::core::{Attribute, Delta, Interval}; use flowy_ot::core::{Attribute, Delta, Interval};
mod delete; mod delete;
mod format; mod format;

View File

@ -1,20 +1,7 @@
use crate::core::Delta; use flowy_ot::core::Delta;
const MAX_UNDOS: usize = 20; 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)] #[derive(Debug, Clone)]
pub struct UndoResult { pub struct UndoResult {
success: bool, success: bool,

View File

@ -1,4 +1,4 @@
use crate::core::{NEW_LINE, WHITESPACE}; use flowy_ot::core::{NEW_LINE, WHITESPACE};
#[inline] #[inline]
pub fn find_newline(s: &str) -> Option<usize> { pub fn find_newline(s: &str) -> Option<usize> {

View File

@ -1,5 +1,5 @@
use super::extensions::*; use super::extensions::*;
use crate::{ use flowy_ot::{
core::{Attribute, Delta, Interval}, core::{Attribute, Delta, Interval},
errors::{ErrorBuilder, OTError, OTErrorCode}, errors::{ErrorBuilder, OTError, OTErrorCode},
}; };

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
entities::doc::{CreateDocParams, Doc, QueryDocParams, SaveDocParams}, entities::doc::{CreateDocParams, Doc, QueryDocParams, UpdateDocParams},
errors::DocError, errors::DocError,
module::DocumentUser, module::DocumentUser,
services::server::Server, services::server::Server,
@ -7,20 +7,37 @@ use crate::{
}; };
use flowy_database::{ConnectionPool, SqliteConnection}; 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 std::sync::Arc;
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
pub(crate) struct DocController { pub(crate) struct DocController {
server: Server, server: Server,
sql: Arc<DocTableSql>, sql: Arc<DocTableSql>,
ws: Arc<RwLock<WsManager>>,
cache: Arc<DocCache>,
user: Arc<dyn DocumentUser>, user: Arc<dyn DocumentUser>,
} }
impl DocController { impl DocController {
pub(crate) fn new(server: Server, user: Arc<dyn DocumentUser>) -> Self { pub(crate) fn new(server: Server, user: Arc<dyn DocumentUser>, ws: Arc<RwLock<WsManager>>) -> Self {
let sql = Arc::new(DocTableSql {}); 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)] #[tracing::instrument(skip(self, conn), err)]
@ -28,22 +45,53 @@ impl DocController {
let doc = Doc { let doc = Doc {
id: params.id, id: params.id,
data: params.data, data: params.data,
revision: 0,
}; };
let _ = self.sql.create_doc_table(DocTable::new(doc), conn)?; let _ = self.sql.create_doc_table(DocTable::new(doc), conn)?;
Ok(()) Ok(())
} }
#[tracing::instrument(level = "debug", skip(self, pool), err)] #[tracing::instrument(level = "debug", skip(self, pool), err)]
pub(crate) async fn open(&self, params: QueryDocParams, pool: Arc<ConnectionPool>) -> Result<Doc, DocError> { pub(crate) async fn open(&self, params: QueryDocParams, pool: Arc<ConnectionPool>) -> Result<Arc<OpenedDoc>, DocError> {
match self._open(params.clone(), pool.clone()) { if self.cache.is_opened(&params.doc_id) == false {
Ok(doc_table) => Ok(doc_table.into()), return match self._open(params.clone(), pool.clone()) {
Err(error) => self.try_read_on_server(params, pool.clone(), error).await, Ok(doc) => Ok(doc),
Err(error) => Err(error),
};
} }
let doc = self.cache.get(&params.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<T>(&self, id: T, changeset: Bytes, pool:
// Arc<ConnectionPool>) -> Result<(), DocError> where
// T: Into<DocId> + 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)] #[tracing::instrument(level = "debug", skip(self, conn), err)]
pub(crate) fn delete(&self, params: QueryDocParams, conn: &SqliteConnection) -> Result<(), DocError> { pub(crate) fn delete(&self, params: QueryDocParams, conn: &SqliteConnection) -> Result<(), DocError> {
let _ = self.sql.delete_doc(&params.doc_id, &*conn)?; let doc_id = &params.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)?; let _ = self.delete_doc_on_server(params)?;
Ok(()) Ok(())
} }
@ -51,7 +99,7 @@ impl DocController {
impl DocController { impl DocController {
#[tracing::instrument(level = "debug", skip(self, params), err)] #[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 token = self.user.token()?;
let server = self.server.clone(); let server = self.server.clone();
tokio::spawn(async move { 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<Doc, DocError> {
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)] #[tracing::instrument(level = "debug", skip(self), err)]
fn delete_doc_on_server(&self, params: QueryDocParams) -> Result<(), DocError> { fn delete_doc_on_server(&self, params: QueryDocParams) -> Result<(), DocError> {
let token = self.user.token()?; let token = self.user.token()?;
@ -114,25 +153,30 @@ impl DocController {
Ok(()) Ok(())
} }
fn _open(&self, params: QueryDocParams, pool: Arc<ConnectionPool>) -> Result<Doc, DocError> { fn _open(&self, params: QueryDocParams, pool: Arc<ConnectionPool>) -> Result<Arc<OpenedDoc>, DocError> {
let doc_table = self.sql.read_doc_table(&params.doc_id, &*(pool.get().map_err(internal_error)?))?; match self.sql.read_doc_table(&params.doc_id, &*(pool.get().map_err(internal_error)?)) {
let doc: Doc = doc_table.into(); Ok(doc_table) => {
let _ = self.read_doc_from_server(params, pool.clone())?; let doc = Arc::new(OpenedDoc::new(doc_table.into(), self.ws.read().sender.clone())?);
Ok(doc) 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<ConnectionPool>, error: DocError) -> Result<Doc, DocError> { Ok(doc)
if error.is_record_not_found() { },
log::debug!("Doc:{} don't exist, reading from server", params.doc_id); Err(error) => {
self.read_doc_from_server(params, pool)?.await.map_err(internal_error)? if error.is_record_not_found() {
} else { log::debug!("Doc:{} don't exist, reading from server", params.doc_id);
Err(error) // TODO: notify doc update
let _ = self.read_doc_from_server(params, pool);
}
return Err(error);
},
} }
} }
} }
impl OpenedDocPersistence for DocController { impl OpenedDocPersistence for DocController {
fn save(&self, params: SaveDocParams, pool: Arc<ConnectionPool>) -> Result<(), DocError> { fn save(&self, params: UpdateDocParams, pool: Arc<ConnectionPool>) -> Result<(), DocError> {
let changeset = DocTableChangeset::new(params.clone()); let changeset = DocTableChangeset::new(params.clone());
let _ = self.sql.update_doc_table(changeset, &*(pool.get().map_err(internal_error)?))?; let _ = self.sql.update_doc_table(changeset, &*(pool.get().map_err(internal_error)?))?;
Ok(()) Ok(())

View File

@ -1,4 +1,6 @@
mod cache;
pub mod doc;
pub(crate) mod doc_controller; pub(crate) mod doc_controller;
pub(crate) mod open_doc; mod open_doc;
pub mod server; pub mod server;
pub mod ws; pub mod ws;

View File

@ -1,16 +1,19 @@
use crate::{ use crate::{
entities::{ entities::{
doc::SaveDocParams, doc::{Doc, UpdateDocParams},
ws::{WsDocumentData, WsSource}, ws::{WsDocumentData, WsSource},
}, },
errors::DocError, errors::DocError,
services::ws::{WsHandler, WsSender}, services::{
doc::Document,
ws::{WsHandler, WsSender},
},
}; };
use bytes::Bytes; use bytes::Bytes;
use flowy_database::ConnectionPool; use flowy_database::ConnectionPool;
use flowy_ot::{client::Document, core::Delta}; use flowy_ot::core::Delta;
use parking_lot::RwLock; use parking_lot::RwLock;
use std::sync::Arc; use std::{convert::TryInto, sync::Arc};
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct DocId(pub(crate) String); pub struct DocId(pub(crate) String);
@ -25,32 +28,42 @@ where
} }
pub(crate) trait OpenedDocPersistence: Send + Sync { pub(crate) trait OpenedDocPersistence: Send + Sync {
fn save(&self, params: SaveDocParams, pool: Arc<ConnectionPool>) -> Result<(), DocError>; fn save(&self, params: UpdateDocParams, pool: Arc<ConnectionPool>) -> Result<(), DocError>;
} }
pub(crate) struct OpenedDoc { pub(crate) struct OpenedDoc {
pub(crate) id: DocId, pub(crate) id: DocId,
pub(crate) revision: i64,
document: RwLock<Document>, document: RwLock<Document>,
ws_sender: Arc<dyn WsSender>, ws_sender: Arc<dyn WsSender>,
persistence: Arc<dyn OpenedDocPersistence>,
} }
impl OpenedDoc { impl OpenedDoc {
pub(crate) fn new(id: DocId, delta: Delta, persistence: Arc<dyn OpenedDocPersistence>, ws_sender: Arc<dyn WsSender>) -> Self { pub(crate) fn new(doc: Doc, ws_sender: Arc<dyn WsSender>) -> Result<Self, DocError> {
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)); let document = RwLock::new(Document::from_delta(delta));
Self {
Ok(Self {
id, id,
revision,
document, document,
ws_sender, 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<u8> { self.document.read().to_bytes() }
pub(crate) fn apply_delta(&self, data: Bytes, pool: Arc<ConnectionPool>) -> Result<(), DocError> { pub(crate) fn apply_delta(&self, data: Bytes, pool: Arc<ConnectionPool>) -> Result<(), DocError> {
let mut write_guard = self.document.write(); 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) { match self.ws_sender.send_data(data) {
Ok(_) => {}, Ok(_) => {},
@ -61,11 +74,11 @@ impl OpenedDoc {
} }
// Opti: strategy to save the document // Opti: strategy to save the document
let save = SaveDocParams { let save = UpdateDocParams {
id: self.id.0.clone(), 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(()) Ok(())
} }

View File

@ -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<DocId, Arc<OpenedDoc>>,
ws_manager: Arc<RwLock<WsManager>>,
persistence: Arc<dyn OpenedDocPersistence>,
}
impl OpenedDocManager {
pub(crate) fn new(ws_manager: Arc<RwLock<WsManager>>, persistence: Arc<dyn OpenedDocPersistence>) -> Self {
Self {
doc_map: DashMap::new(),
ws_manager,
persistence,
}
}
#[tracing::instrument(level = "debug", skip(self, data), err)]
pub(crate) fn open<T, D>(&self, id: T, data: D) -> Result<(), DocError>
where
T: Into<DocId> + Debug,
D: TryInto<Delta, Error = OTError>,
{
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<T>(&self, id: T) -> bool
where
T: Into<DocId>,
{
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<T>(&self, id: T, changeset: Bytes, pool: Arc<ConnectionPool>) -> Result<(), DocError>
where
T: Into<DocId> + 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<T>(&self, id: T) -> Result<Vec<u8>, DocError>
where
T: Into<DocId> + 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<T>(&self, id: T) -> Result<(), DocError>
where
T: Into<DocId>,
{
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") }

View File

@ -1,5 +0,0 @@
mod manager;
mod open_doc;
pub(crate) use manager::*;
pub use open_doc::*;

View File

@ -5,7 +5,7 @@ mod server_api_mock;
pub use server_api::*; pub use server_api::*;
// TODO: ignore mock files in production // TODO: ignore mock files in production
use crate::{ use crate::{
entities::doc::{CreateDocParams, Doc, QueryDocParams, SaveDocParams}, entities::doc::{CreateDocParams, Doc, QueryDocParams, UpdateDocParams},
errors::DocError, errors::DocError,
}; };
use flowy_infra::future::ResultFuture; use flowy_infra::future::ResultFuture;
@ -18,7 +18,7 @@ pub trait DocumentServerAPI {
fn read_doc(&self, token: &str, params: QueryDocParams) -> ResultFuture<Option<Doc>, DocError>; fn read_doc(&self, token: &str, params: QueryDocParams) -> ResultFuture<Option<Doc>, 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>; fn delete_doc(&self, token: &str, params: QueryDocParams) -> ResultFuture<(), DocError>;
} }

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
entities::doc::{CreateDocParams, Doc, QueryDocParams, SaveDocParams}, entities::doc::{CreateDocParams, Doc, QueryDocParams, UpdateDocParams},
errors::DocError, errors::DocError,
services::server::DocumentServerAPI, 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 }) 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(); let token = token.to_owned();
ResultFuture::new(async move { update_doc_request(&token, params, DOC_URL.as_ref()).await }) 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) 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() let _ = request_builder()
.patch(&url.to_owned()) .patch(&url.to_owned())
.header(HEADER_TOKEN, token) .header(HEADER_TOKEN, token)

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
entities::doc::{CreateDocParams, Doc, QueryDocParams, SaveDocParams}, entities::doc::{CreateDocParams, Doc, QueryDocParams, UpdateDocParams},
errors::DocError, errors::DocError,
services::server::DocumentServerAPI, services::server::DocumentServerAPI,
}; };
@ -13,7 +13,7 @@ impl DocumentServerAPI for DocServerMock {
ResultFuture::new(async { Ok(None) }) 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(()) }) } fn delete_doc(&self, _token: &str, _params: QueryDocParams) -> ResultFuture<(), DocError> { ResultFuture::new(async { Ok(()) }) }
} }

View File

@ -1,4 +1,4 @@
use crate::entities::doc::{Doc, SaveDocParams}; use crate::entities::doc::{Doc, UpdateDocParams};
use flowy_database::schema::doc_table; use flowy_database::schema::doc_table;
#[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)] #[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)]
@ -6,7 +6,7 @@ use flowy_database::schema::doc_table;
pub(crate) struct DocTable { pub(crate) struct DocTable {
pub id: String, pub id: String,
pub data: Vec<u8>, pub data: Vec<u8>,
pub version: i64, pub revision: i64,
} }
impl DocTable { impl DocTable {
@ -14,7 +14,7 @@ impl DocTable {
Self { Self {
id: doc.id, id: doc.id,
data: doc.data, data: doc.data,
version: 0, revision: 0,
} }
} }
} }
@ -27,10 +27,10 @@ pub(crate) struct DocTableChangeset {
} }
impl DocTableChangeset { impl DocTableChangeset {
pub(crate) fn new(params: SaveDocParams) -> Self { pub(crate) fn new(params: UpdateDocParams) -> Self {
Self { Self {
id: params.id, id: params.id,
data: params.data, data: params.doc_data,
} }
} }
} }
@ -40,6 +40,7 @@ impl std::convert::Into<Doc> for DocTable {
Doc { Doc {
id: self.id, id: self.id,
data: self.data, data: self.data,
revision: self.revision,
} }
} }
} }

View File

@ -1,10 +1,6 @@
pub mod helper; use crate::editor::{TestBuilder, TestOp::*};
use flowy_document::services::doc::{FlowyDoc, PlainDoc};
use crate::helper::{TestOp::*, *}; use flowy_ot::core::{Interval, NEW_LINE, WHITESPACE};
use flowy_ot::{
client::{FlowyDoc, PlainDoc},
core::{Interval, NEW_LINE, WHITESPACE},
};
#[test] #[test]
fn attributes_bold_added() { fn attributes_bold_added() {

View File

@ -1,8 +1,11 @@
mod attribute_test;
mod op_test;
mod serde_test;
mod undo_redo_test;
use derive_more::Display; use derive_more::Display;
use flowy_ot::{ use flowy_document::services::doc::{CustomDocument, Document};
client::{CustomDocument, Document}, use flowy_ot::core::*;
core::*,
};
use rand::{prelude::*, Rng as WrappedRng}; use rand::{prelude::*, Rng as WrappedRng};
use std::{sync::Once, time::Duration}; use std::{sync::Once, time::Duration};

View File

@ -1,9 +1,7 @@
pub mod helper; use crate::editor::{Rng, TestBuilder, TestOp::*};
use crate::helper::TestOp::*;
use bytecount::num_chars; use bytecount::num_chars;
use flowy_ot::{client::PlainDoc, core::*}; use flowy_document::services::doc::PlainDoc;
use helper::*; use flowy_ot::core::*;
#[test] #[test]
fn attributes_insert_text() { fn attributes_insert_text() {

View File

@ -1,7 +1,5 @@
use flowy_ot::{ use flowy_document::services::doc::{Document, PlainDoc};
client::{Document, PlainDoc}, use flowy_ot::core::*;
core::*,
};
#[test] #[test]
fn operation_insert_serialize_test() { fn operation_insert_serialize_test() {

View File

@ -1,10 +1,6 @@
pub mod helper; use crate::editor::{TestBuilder, TestOp::*};
use flowy_document::services::doc::{FlowyDoc, PlainDoc, RECORD_THRESHOLD};
use crate::helper::{TestOp::*, *}; use flowy_ot::core::{Interval, NEW_LINE, WHITESPACE};
use flowy_ot::{
client::{FlowyDoc, PlainDoc, RECORD_THRESHOLD},
core::{Interval, NEW_LINE, WHITESPACE},
};
#[test] #[test]
fn history_insert_undo() { fn history_insert_undo() {

View File

@ -0,0 +1 @@
mod editor;

View File

@ -11,17 +11,10 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = {version = "1.0"} serde_json = {version = "1.0"}
derive_more = {version = "0.99", features = ["display"]} derive_more = {version = "0.99", features = ["display"]}
log = "0.4" log = "0.4"
color-eyre = { version = "0.5", default-features = false }
chrono = "0.4.19"
lazy_static = "1.4.0" lazy_static = "1.4.0"
url = "2.2"
strum = "0.21" strum = "0.21"
strum_macros = "0.21" strum_macros = "0.21"
bytes = "1.0" bytes = "1.0"
[dev-dependencies]
criterion = "0.3"
rand = "0.7.3"
env_logger = "0.8.2"

View File

@ -1,4 +1,2 @@
pub mod client;
pub mod core; pub mod core;
pub mod errors; pub mod errors;
pub mod server;

View File

@ -1 +0,0 @@

View File

@ -3,7 +3,7 @@ use crate::{
errors::WorkspaceError, errors::WorkspaceError,
}; };
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
use flowy_document::entities::doc::{ApplyChangesetParams, SaveDocParams}; use flowy_document::entities::doc::{DocChangeset, UpdateDocParams};
use std::convert::TryInto; use std::convert::TryInto;
#[derive(Default, ProtoBuf)] #[derive(Default, ProtoBuf)]
@ -109,16 +109,19 @@ pub struct SaveViewDataRequest {
pub data: Vec<u8>, pub data: Vec<u8>,
} }
impl TryInto<SaveDocParams> for SaveViewDataRequest { impl TryInto<UpdateDocParams> for SaveViewDataRequest {
type Error = WorkspaceError; type Error = WorkspaceError;
fn try_into(self) -> Result<SaveDocParams, Self::Error> { fn try_into(self) -> Result<UpdateDocParams, Self::Error> {
let view_id = ViewId::parse(self.view_id).map_err(|e| WorkspaceError::view_id().context(e))?.0; let view_id = ViewId::parse(self.view_id).map_err(|e| WorkspaceError::view_id().context(e))?.0;
// Opti: Vec<u8> -> Delta -> Vec<u8> // Opti: Vec<u8> -> Delta -> Vec<u8>
let data = DeltaData::parse(self.data).map_err(|e| WorkspaceError::view_data().context(e))?.0; 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<u8>, pub data: Vec<u8>,
} }
impl TryInto<ApplyChangesetParams> for ApplyChangesetRequest { impl TryInto<DocChangeset> for ApplyChangesetRequest {
type Error = WorkspaceError; type Error = WorkspaceError;
fn try_into(self) -> Result<ApplyChangesetParams, Self::Error> { fn try_into(self) -> Result<DocChangeset, Self::Error> {
let view_id = ViewId::parse(self.view_id).map_err(|e| WorkspaceError::view_id().context(e))?.0; let view_id = ViewId::parse(self.view_id).map_err(|e| WorkspaceError::view_id().context(e))?.0;
// Opti: Vec<u8> -> Delta -> Vec<u8> // Opti: Vec<u8> -> Delta -> Vec<u8>
let data = DeltaData::parse(self.data).map_err(|e| WorkspaceError::view_data().context(e))?.0; 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 })
} }
} }

View File

@ -49,6 +49,6 @@ pub enum WorkspaceEvent {
#[event(input = "OpenViewRequest", output = "Doc")] #[event(input = "OpenViewRequest", output = "Doc")]
OpenView = 205, OpenView = 205,
#[event(input = "ApplyChangesetRequest", output = "Doc")] #[event(input = "DocChangeset", output = "Doc")]
ApplyChangeset = 206, ApplyChangeset = 206,
} }

View File

@ -16,7 +16,7 @@ use crate::{
services::ViewController, services::ViewController,
}; };
use flowy_dispatch::prelude::{data_result, Data, DataResult, Unit}; 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}; use std::{convert::TryInto, sync::Arc};
#[tracing::instrument(skip(data, controller), err)] #[tracing::instrument(skip(data, controller), err)]
@ -60,7 +60,7 @@ pub(crate) async fn apply_changeset_handler(
data: Data<ApplyChangesetRequest>, data: Data<ApplyChangesetRequest>,
controller: Unit<Arc<ViewController>>, controller: Unit<Arc<ViewController>>,
) -> DataResult<Doc, WorkspaceError> { ) -> DataResult<Doc, WorkspaceError> {
let params: ApplyChangesetParams = data.into_inner().try_into()?; let params: DocChangeset = data.into_inner().try_into()?;
let doc = controller.apply_changeset(params).await?; let doc = controller.apply_changeset(params).await?;
data_result(doc) data_result(doc)
} }

View File

@ -14,7 +14,7 @@ use crate::{
}; };
use flowy_database::SqliteConnection; use flowy_database::SqliteConnection;
use flowy_document::{ use flowy_document::{
entities::doc::{ApplyChangesetParams, CreateDocParams, Doc, QueryDocParams}, entities::doc::{CreateDocParams, Doc, DocChangeset, QueryDocParams},
module::FlowyDocument, module::FlowyDocument,
}; };
use std::sync::Arc; use std::sync::Arc;
@ -125,7 +125,7 @@ impl ViewController {
Ok(()) Ok(())
} }
pub(crate) async fn apply_changeset(&self, params: ApplyChangesetParams) -> Result<Doc, WorkspaceError> { pub(crate) async fn apply_changeset(&self, params: DocChangeset) -> Result<Doc, WorkspaceError> {
let pool = self.database.db_pool()?; let pool = self.database.db_pool()?;
let doc = self.document.apply_changeset(params, pool).await?; let doc = self.document.apply_changeset(params, pool).await?;
Ok(doc) Ok(doc)