feat: config grid db

This commit is contained in:
appflowy 2022-03-04 18:11:12 +08:00
parent 67e6b091a5
commit 49807a0b57
34 changed files with 1070 additions and 785 deletions

View File

@ -16,16 +16,14 @@ export 'grid.pbenum.dart';
class Grid extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Grid', createEmptyInstance: create)
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
..aOM<RepeatedFilter>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'filters', subBuilder: RepeatedFilter.create)
..aOM<RepeatedFieldOrder>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldOrders', subBuilder: RepeatedFieldOrder.create)
..aOM<RepeatedRowOrder>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rowOrders', subBuilder: RepeatedRowOrder.create)
..aOM<RepeatedFieldOrder>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldOrders', subBuilder: RepeatedFieldOrder.create)
..aOM<RepeatedRowOrder>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rowOrders', subBuilder: RepeatedRowOrder.create)
..hasRequiredFields = false
;
Grid._() : super();
factory Grid({
$core.String? id,
RepeatedFilter? filters,
RepeatedFieldOrder? fieldOrders,
RepeatedRowOrder? rowOrders,
}) {
@ -33,9 +31,6 @@ class Grid extends $pb.GeneratedMessage {
if (id != null) {
_result.id = id;
}
if (filters != null) {
_result.filters = filters;
}
if (fieldOrders != null) {
_result.fieldOrders = fieldOrders;
}
@ -75,37 +70,26 @@ class Grid extends $pb.GeneratedMessage {
void clearId() => clearField(1);
@$pb.TagNumber(2)
RepeatedFilter get filters => $_getN(1);
RepeatedFieldOrder get fieldOrders => $_getN(1);
@$pb.TagNumber(2)
set filters(RepeatedFilter v) { setField(2, v); }
set fieldOrders(RepeatedFieldOrder v) { setField(2, v); }
@$pb.TagNumber(2)
$core.bool hasFilters() => $_has(1);
$core.bool hasFieldOrders() => $_has(1);
@$pb.TagNumber(2)
void clearFilters() => clearField(2);
void clearFieldOrders() => clearField(2);
@$pb.TagNumber(2)
RepeatedFilter ensureFilters() => $_ensure(1);
RepeatedFieldOrder ensureFieldOrders() => $_ensure(1);
@$pb.TagNumber(3)
RepeatedFieldOrder get fieldOrders => $_getN(2);
RepeatedRowOrder get rowOrders => $_getN(2);
@$pb.TagNumber(3)
set fieldOrders(RepeatedFieldOrder v) { setField(3, v); }
set rowOrders(RepeatedRowOrder v) { setField(3, v); }
@$pb.TagNumber(3)
$core.bool hasFieldOrders() => $_has(2);
$core.bool hasRowOrders() => $_has(2);
@$pb.TagNumber(3)
void clearFieldOrders() => clearField(3);
void clearRowOrders() => clearField(3);
@$pb.TagNumber(3)
RepeatedFieldOrder ensureFieldOrders() => $_ensure(2);
@$pb.TagNumber(4)
RepeatedRowOrder get rowOrders => $_getN(3);
@$pb.TagNumber(4)
set rowOrders(RepeatedRowOrder v) { setField(4, v); }
@$pb.TagNumber(4)
$core.bool hasRowOrders() => $_has(3);
@$pb.TagNumber(4)
void clearRowOrders() => clearField(4);
@$pb.TagNumber(4)
RepeatedRowOrder ensureRowOrders() => $_ensure(3);
RepeatedRowOrder ensureRowOrders() => $_ensure(2);
}
class FieldOrder extends $pb.GeneratedMessage {
@ -890,6 +874,95 @@ class Cell extends $pb.GeneratedMessage {
void clearContent() => clearField(3);
}
class CellChangeset extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CellChangeset', createEmptyInstance: create)
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rowId')
..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data')
..hasRequiredFields = false
;
CellChangeset._() : super();
factory CellChangeset({
$core.String? id,
$core.String? rowId,
$core.String? fieldId,
$core.String? data,
}) {
final _result = create();
if (id != null) {
_result.id = id;
}
if (rowId != null) {
_result.rowId = rowId;
}
if (fieldId != null) {
_result.fieldId = fieldId;
}
if (data != null) {
_result.data = data;
}
return _result;
}
factory CellChangeset.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory CellChangeset.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
CellChangeset clone() => CellChangeset()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
CellChangeset copyWith(void Function(CellChangeset) updates) => super.copyWith((message) => updates(message as CellChangeset)) as CellChangeset; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static CellChangeset create() => CellChangeset._();
CellChangeset createEmptyInstance() => create();
static $pb.PbList<CellChangeset> createRepeated() => $pb.PbList<CellChangeset>();
@$core.pragma('dart2js:noInline')
static CellChangeset getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<CellChangeset>(create);
static CellChangeset? _defaultInstance;
@$pb.TagNumber(1)
$core.String get id => $_getSZ(0);
@$pb.TagNumber(1)
set id($core.String v) { $_setString(0, v); }
@$pb.TagNumber(1)
$core.bool hasId() => $_has(0);
@$pb.TagNumber(1)
void clearId() => clearField(1);
@$pb.TagNumber(2)
$core.String get rowId => $_getSZ(1);
@$pb.TagNumber(2)
set rowId($core.String v) { $_setString(1, v); }
@$pb.TagNumber(2)
$core.bool hasRowId() => $_has(1);
@$pb.TagNumber(2)
void clearRowId() => clearField(2);
@$pb.TagNumber(3)
$core.String get fieldId => $_getSZ(2);
@$pb.TagNumber(3)
set fieldId($core.String v) { $_setString(2, v); }
@$pb.TagNumber(3)
$core.bool hasFieldId() => $_has(2);
@$pb.TagNumber(3)
void clearFieldId() => clearField(3);
@$pb.TagNumber(4)
$core.String get data => $_getSZ(3);
@$pb.TagNumber(4)
set data($core.String v) { $_setString(3, v); }
@$pb.TagNumber(4)
$core.bool hasData() => $_has(3);
@$pb.TagNumber(4)
void clearData() => clearField(4);
}
class CreateGridPayload extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CreateGridPayload', createEmptyInstance: create)
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
@ -984,119 +1057,3 @@ class GridId extends $pb.GeneratedMessage {
void clearValue() => clearField(1);
}
class Filter extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Filter', createEmptyInstance: create)
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
..hasRequiredFields = false
;
Filter._() : super();
factory Filter({
$core.String? id,
$core.String? name,
$core.String? desc,
}) {
final _result = create();
if (id != null) {
_result.id = id;
}
if (name != null) {
_result.name = name;
}
if (desc != null) {
_result.desc = desc;
}
return _result;
}
factory Filter.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory Filter.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
Filter clone() => Filter()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
Filter copyWith(void Function(Filter) updates) => super.copyWith((message) => updates(message as Filter)) as Filter; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static Filter create() => Filter._();
Filter createEmptyInstance() => create();
static $pb.PbList<Filter> createRepeated() => $pb.PbList<Filter>();
@$core.pragma('dart2js:noInline')
static Filter getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Filter>(create);
static Filter? _defaultInstance;
@$pb.TagNumber(1)
$core.String get id => $_getSZ(0);
@$pb.TagNumber(1)
set id($core.String v) { $_setString(0, v); }
@$pb.TagNumber(1)
$core.bool hasId() => $_has(0);
@$pb.TagNumber(1)
void clearId() => clearField(1);
@$pb.TagNumber(2)
$core.String get name => $_getSZ(1);
@$pb.TagNumber(2)
set name($core.String v) { $_setString(1, v); }
@$pb.TagNumber(2)
$core.bool hasName() => $_has(1);
@$pb.TagNumber(2)
void clearName() => clearField(2);
@$pb.TagNumber(3)
$core.String get desc => $_getSZ(2);
@$pb.TagNumber(3)
set desc($core.String v) { $_setString(2, v); }
@$pb.TagNumber(3)
$core.bool hasDesc() => $_has(2);
@$pb.TagNumber(3)
void clearDesc() => clearField(3);
}
class RepeatedFilter extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RepeatedFilter', createEmptyInstance: create)
..pc<Filter>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'items', $pb.PbFieldType.PM, subBuilder: Filter.create)
..hasRequiredFields = false
;
RepeatedFilter._() : super();
factory RepeatedFilter({
$core.Iterable<Filter>? items,
}) {
final _result = create();
if (items != null) {
_result.items.addAll(items);
}
return _result;
}
factory RepeatedFilter.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory RepeatedFilter.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
RepeatedFilter clone() => RepeatedFilter()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
RepeatedFilter copyWith(void Function(RepeatedFilter) updates) => super.copyWith((message) => updates(message as RepeatedFilter)) as RepeatedFilter; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static RepeatedFilter create() => RepeatedFilter._();
RepeatedFilter createEmptyInstance() => create();
static $pb.PbList<RepeatedFilter> createRepeated() => $pb.PbList<RepeatedFilter>();
@$core.pragma('dart2js:noInline')
static RepeatedFilter getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RepeatedFilter>(create);
static RepeatedFilter? _defaultInstance;
@$pb.TagNumber(1)
$core.List<Filter> get items => $_getList(0);
}

View File

@ -28,14 +28,13 @@ const Grid$json = const {
'1': 'Grid',
'2': const [
const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
const {'1': 'filters', '3': 2, '4': 1, '5': 11, '6': '.RepeatedFilter', '10': 'filters'},
const {'1': 'field_orders', '3': 3, '4': 1, '5': 11, '6': '.RepeatedFieldOrder', '10': 'fieldOrders'},
const {'1': 'row_orders', '3': 4, '4': 1, '5': 11, '6': '.RepeatedRowOrder', '10': 'rowOrders'},
const {'1': 'field_orders', '3': 2, '4': 1, '5': 11, '6': '.RepeatedFieldOrder', '10': 'fieldOrders'},
const {'1': 'row_orders', '3': 3, '4': 1, '5': 11, '6': '.RepeatedRowOrder', '10': 'rowOrders'},
],
};
/// Descriptor for `Grid`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List gridDescriptor = $convert.base64Decode('CgRHcmlkEg4KAmlkGAEgASgJUgJpZBIpCgdmaWx0ZXJzGAIgASgLMg8uUmVwZWF0ZWRGaWx0ZXJSB2ZpbHRlcnMSNgoMZmllbGRfb3JkZXJzGAMgASgLMhMuUmVwZWF0ZWRGaWVsZE9yZGVyUgtmaWVsZE9yZGVycxIwCgpyb3dfb3JkZXJzGAQgASgLMhEuUmVwZWF0ZWRSb3dPcmRlclIJcm93T3JkZXJz');
final $typed_data.Uint8List gridDescriptor = $convert.base64Decode('CgRHcmlkEg4KAmlkGAEgASgJUgJpZBI2CgxmaWVsZF9vcmRlcnMYAiABKAsyEy5SZXBlYXRlZEZpZWxkT3JkZXJSC2ZpZWxkT3JkZXJzEjAKCnJvd19vcmRlcnMYAyABKAsyES5SZXBlYXRlZFJvd09yZGVyUglyb3dPcmRlcnM=');
@$core.Deprecated('Use fieldOrderDescriptor instead')
const FieldOrder$json = const {
'1': 'FieldOrder',
@ -196,6 +195,19 @@ const Cell$json = const {
/// Descriptor for `Cell`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List cellDescriptor = $convert.base64Decode('CgRDZWxsEg4KAmlkGAEgASgJUgJpZBIZCghmaWVsZF9pZBgCIAEoCVIHZmllbGRJZBIYCgdjb250ZW50GAMgASgJUgdjb250ZW50');
@$core.Deprecated('Use cellChangesetDescriptor instead')
const CellChangeset$json = const {
'1': 'CellChangeset',
'2': const [
const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
const {'1': 'row_id', '3': 2, '4': 1, '5': 9, '10': 'rowId'},
const {'1': 'field_id', '3': 3, '4': 1, '5': 9, '10': 'fieldId'},
const {'1': 'data', '3': 4, '4': 1, '5': 9, '10': 'data'},
],
};
/// Descriptor for `CellChangeset`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List cellChangesetDescriptor = $convert.base64Decode('Cg1DZWxsQ2hhbmdlc2V0Eg4KAmlkGAEgASgJUgJpZBIVCgZyb3dfaWQYAiABKAlSBXJvd0lkEhkKCGZpZWxkX2lkGAMgASgJUgdmaWVsZElkEhIKBGRhdGEYBCABKAlSBGRhdGE=');
@$core.Deprecated('Use createGridPayloadDescriptor instead')
const CreateGridPayload$json = const {
'1': 'CreateGridPayload',
@ -216,25 +228,3 @@ const GridId$json = const {
/// Descriptor for `GridId`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List gridIdDescriptor = $convert.base64Decode('CgZHcmlkSWQSFAoFdmFsdWUYASABKAlSBXZhbHVl');
@$core.Deprecated('Use filterDescriptor instead')
const Filter$json = const {
'1': 'Filter',
'2': const [
const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
const {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
const {'1': 'desc', '3': 3, '4': 1, '5': 9, '10': 'desc'},
],
};
/// Descriptor for `Filter`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List filterDescriptor = $convert.base64Decode('CgZGaWx0ZXISDgoCaWQYASABKAlSAmlkEhIKBG5hbWUYAiABKAlSBG5hbWUSEgoEZGVzYxgDIAEoCVIEZGVzYw==');
@$core.Deprecated('Use repeatedFilterDescriptor instead')
const RepeatedFilter$json = const {
'1': 'RepeatedFilter',
'2': const [
const {'1': 'items', '3': 1, '4': 3, '5': 11, '6': '.Filter', '10': 'items'},
],
};
/// Descriptor for `RepeatedFilter`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List repeatedFilterDescriptor = $convert.base64Decode('Cg5SZXBlYXRlZEZpbHRlchIdCgVpdGVtcxgBIAMoCzIHLkZpbHRlclIFaXRlbXM=');

View File

@ -909,6 +909,7 @@ dependencies = [
"dissimilar",
"flowy-derive",
"flowy-folder-data-model",
"flowy-grid-data-model",
"futures",
"lib-infra",
"lib-ot",
@ -1048,14 +1049,22 @@ dependencies = [
name = "flowy-grid"
version = "0.1.0"
dependencies = [
"async-trait",
"bytes",
"chrono",
"dart-notify",
"diesel",
"flowy-collaboration",
"flowy-derive",
"flowy-error",
"flowy-grid-data-model",
"flowy-sync",
"lazy_static",
"lib-dispatch",
"lib-infra",
"lib-ot",
"lib-sqlite",
"parking_lot",
"protobuf",
"rust_decimal",
"rusty-money",
@ -1073,8 +1082,11 @@ dependencies = [
"flowy-derive",
"lib-infra",
"protobuf",
"serde",
"serde_json",
"strum",
"strum_macros",
"uuid",
]
[[package]]

View File

@ -97,7 +97,7 @@ impl RevisionWSDataStream for BlockRevisionWSDataStream {
}
pub(crate) struct BlockWSDataSink(pub(crate) Arc<WSDataProvider>);
impl RevisionWSDataIterator for BlockWSDataSink {
impl RevisionWebSocketSink for BlockWSDataSink {
fn next(&self) -> FutureResult<Option<ClientRevisionWSData>, FlowyError> {
let sink_provider = self.0.clone();
FutureResult::new(async move { sink_provider.next().await })

View File

@ -25,7 +25,7 @@ use flowy_block::BlockManager;
use flowy_database::kv::KV;
use flowy_folder_data_model::entities::share::{ExportData, ExportParams};
use lib_infra::uuid_string;
use lib_infra::uuid;
const LATEST_VIEW_ID: &str = "latest_view_id";
@ -176,7 +176,7 @@ impl ViewController {
thumbnail: view.thumbnail,
data_type: view.data_type,
data: document_json,
view_id: uuid_string(),
view_id: uuid(),
ext_data: view.ext_data,
plugin_type: view.plugin_type,
};

View File

@ -43,7 +43,7 @@ pub(crate) async fn make_folder_ws_manager(
}
pub(crate) struct FolderWSDataSink(Arc<WSDataProvider>);
impl RevisionWSDataIterator for FolderWSDataSink {
impl RevisionWebSocketSink for FolderWSDataSink {
fn next(&self) -> FutureResult<Option<ClientRevisionWSData>, FlowyError> {
let sink_provider = self.0.clone();
FutureResult::new(async move { sink_provider.next().await })

View File

@ -6,13 +6,19 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
flowy-grid-data-model = { path = "../../../shared-lib/flowy-grid-data-model" }
lib-dispatch = { path = "../lib-dispatch" }
lib-infra = { path = "../../../shared-lib/lib-infra" }
dart-notify = { path = "../dart-notify" }
lib-sqlite = { path = "../lib-sqlite" }
flowy-sync = { path = "../flowy-sync" }
flowy-error = { path = "../flowy-error"}
flowy-derive = { path = "../../../shared-lib/flowy-derive" }
lib-ot = { path = "../../../shared-lib/lib-ot" }
lib-infra = { path = "../../../shared-lib/lib-infra" }
flowy-grid-data-model = { path = "../../../shared-lib/flowy-grid-data-model" }
flowy-collaboration = { path = "../../../shared-lib/flowy-collaboration" }
strum = "0.21"
strum_macros = "0.21"
flowy-derive = { path = "../../../shared-lib/flowy-derive" }
tracing = { version = "0.1", features = ["log"] }
protobuf = {version = "2.18.0"}
rust_decimal = "1.8.1"
@ -21,6 +27,11 @@ lazy_static = "1.4.0"
chrono = "0.4.19"
uuid = { version = "0.8", features = ["serde", "v4"] }
bytes = { version = "1.0" }
async-trait = "0.1.52"
diesel = {version = "1.4.8", features = ["sqlite"]}
parking_lot = "0.11"
[build-dependencies]
lib-infra = { path = "../../../shared-lib/lib-infra", features = ["protobuf_file_gen", "proto_gen"] }

View File

@ -1,3 +1,3 @@
proto_crates = ["src/event_map.rs", "src/cell_service/cell_data.rs"]
proto_crates = ["src/event_map.rs", "src/services/cell_data.rs"]
event_files = ["src/event_map.rs"]

View File

@ -5,5 +5,5 @@ mod controller;
mod event_handler;
mod event_map;
mod cell_service;
mod protobuf;
mod services;

View File

@ -1,5 +1,5 @@
use crate::cell_service::util::*;
use crate::impl_any_data;
use crate::services::util::*;
use bytes::Bytes;
use chrono::format::strftime::StrftimeItems;
use chrono::NaiveDateTime;

View File

@ -0,0 +1,143 @@
use crate::services::row_kv::{RowKVPersistence, RowKVTransaction};
use flowy_collaboration::client_grid::{GridChange, GridPad};
use flowy_collaboration::entities::revision::Revision;
use flowy_collaboration::util::make_delta_from_revisions;
use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::entities::{GridId, RawRow};
use flowy_sync::{
RevisionCloudService, RevisionCompact, RevisionManager, RevisionObjectBuilder, RevisionPersistence,
RevisionWebSocket, RevisionWebSocketManager,
};
use lib_infra::future::FutureResult;
use lib_ot::core::PlainTextAttributes;
use lib_sqlite::ConnectionPool;
use parking_lot::RwLock;
use std::sync::Arc;
pub struct ClientGridEditor {
user_id: String,
grid_id: GridId,
grid: Arc<RwLock<GridPad>>,
rev_manager: Arc<RevisionManager>,
kv: Arc<RowKVPersistence>,
}
impl ClientGridEditor {
pub async fn new(
user_id: &str,
grid_id: &GridId,
token: &str,
pool: Arc<ConnectionPool>,
_web_socket: Arc<dyn RevisionWebSocket>,
) -> FlowyResult<Self> {
let rev_persistence = Arc::new(RevisionPersistence::new(user_id, grid_id.as_ref(), pool.clone()));
let mut rev_manager = RevisionManager::new(user_id, grid_id.as_ref(), rev_persistence);
let cloud = Arc::new(GridRevisionCloudService {
token: token.to_string(),
});
let grid = Arc::new(RwLock::new(
rev_manager.load::<GridPadBuilder, GridRevisionCompact>(cloud).await?,
));
let rev_manager = Arc::new(rev_manager);
let kv = Arc::new(RowKVPersistence::new(pool));
let user_id = user_id.to_owned();
let grid_id = grid_id.to_owned();
Ok(Self {
user_id,
grid_id,
grid,
rev_manager,
kv,
})
}
pub async fn create_row(&self, row: RawRow) -> FlowyResult<()> {
let _ = self
.modify(|grid| {
let change = grid.create_row(&row)?;
Ok(change)
})
.await?;
let _ = self.kv.set(row)?;
Ok(())
}
pub async fn modify<F>(&self, f: F) -> FlowyResult<()>
where
F: FnOnce(&mut GridPad) -> FlowyResult<Option<GridChange>>,
{
let mut write_guard = self.grid.write();
match f(&mut *write_guard)? {
None => {}
Some(change) => {
let _ = self.apply_change(change).await?;
}
}
Ok(())
}
async fn apply_change(&self, change: GridChange) -> FlowyResult<()> {
let GridChange { delta, md5 } = change;
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
let delta_data = delta.to_bytes();
let revision = Revision::new(
&self.rev_manager.object_id,
base_rev_id,
rev_id,
delta_data,
&self.user_id,
md5,
);
let _ = self
.rev_manager
.add_local_revision::<GridRevisionCompact>(&revision)
.await?;
Ok(())
}
}
struct GridPadBuilder();
impl RevisionObjectBuilder for GridPadBuilder {
type Output = GridPad;
fn build_object(_object_id: &str, revisions: Vec<Revision>) -> FlowyResult<Self::Output> {
let pad = GridPad::from_revisions(revisions)?;
Ok(pad)
}
}
struct GridRevisionCloudService {
#[allow(dead_code)]
token: String,
}
impl RevisionCloudService for GridRevisionCloudService {
#[tracing::instrument(level = "trace", skip(self))]
fn fetch_object(&self, _user_id: &str, _object_id: &str) -> FutureResult<Vec<Revision>, FlowyError> {
FutureResult::new(async move { Ok(vec![]) })
}
}
struct GridRevisionCompact();
impl RevisionCompact for GridRevisionCompact {
fn compact_revisions(user_id: &str, object_id: &str, mut revisions: Vec<Revision>) -> FlowyResult<Revision> {
if revisions.is_empty() {
return Err(FlowyError::internal().context("Can't compact the empty folder's revisions"));
}
if revisions.len() == 1 {
return Ok(revisions.pop().unwrap());
}
let first_revision = revisions.first().unwrap();
let last_revision = revisions.last().unwrap();
let (base_rev_id, rev_id) = first_revision.pair_rev_id();
let md5 = last_revision.md5.clone();
let delta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?;
let delta_data = delta.to_bytes();
Ok(Revision::new(object_id, base_rev_id, rev_id, delta_data, user_id, md5))
}
}

View File

@ -2,4 +2,7 @@ mod stringify;
mod util;
pub mod cell_data;
pub mod grid_editor;
mod row_kv;
pub use stringify::*;

View File

@ -0,0 +1,95 @@
use async_trait::async_trait;
use diesel::SqliteConnection;
use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::entities::RawRow;
use lib_infra::future::{BoxResultFuture, FutureResult};
use lib_sqlite::{ConnectionManager, ConnectionPool};
use std::sync::Arc;
pub trait RowKVTransaction {
fn get(&self, row_id: &str) -> FlowyResult<Option<RawRow>>;
fn set(&self, row: RawRow) -> FlowyResult<()>;
fn remove(&self, row_id: &str) -> FlowyResult<()>;
fn batch_get(&self, ids: Vec<String>) -> FlowyResult<()>;
fn batch_set(&self, rows: Vec<RawRow>) -> FlowyResult<()>;
fn batch_delete(&self, ids: Vec<String>) -> FlowyResult<()>;
}
pub struct RowKVPersistence {
pool: Arc<ConnectionPool>,
}
impl RowKVPersistence {
pub fn new(pool: Arc<ConnectionPool>) -> Self {
Self { pool }
}
pub fn begin_transaction<F, O>(&self, f: F) -> FlowyResult<O>
where
F: for<'a> FnOnce(Box<dyn RowKVTransaction + 'a>) -> FlowyResult<O>,
{
let conn = self.pool.get()?;
conn.immediate_transaction::<_, FlowyError, _>(|| {
let sql_transaction = SqliteTransaction { conn: &conn };
f(Box::new(sql_transaction))
})
}
}
impl RowKVTransaction for RowKVPersistence {
fn get(&self, row_id: &str) -> FlowyResult<Option<RawRow>> {
self.begin_transaction(|transaction| transaction.get(row_id))
}
fn set(&self, row: RawRow) -> FlowyResult<()> {
self.begin_transaction(|transaction| transaction.set(row))
}
fn remove(&self, row_id: &str) -> FlowyResult<()> {
self.begin_transaction(|transaction| transaction.remove(row_id))
}
fn batch_get(&self, ids: Vec<String>) -> FlowyResult<()> {
self.begin_transaction(|transaction| transaction.batch_get(ids))
}
fn batch_set(&self, rows: Vec<RawRow>) -> FlowyResult<()> {
self.begin_transaction(|transaction| transaction.batch_set(rows))
}
fn batch_delete(&self, ids: Vec<String>) -> FlowyResult<()> {
self.begin_transaction(|transaction| transaction.batch_delete(ids))
}
}
pub struct SqliteTransaction<'a> {
conn: &'a SqliteConnection,
}
#[async_trait]
impl<'a> RowKVTransaction for SqliteTransaction<'a> {
fn get(&self, row_id: &str) -> FlowyResult<Option<RawRow>> {
todo!()
}
fn set(&self, row: RawRow) -> FlowyResult<()> {
todo!()
}
fn remove(&self, row_id: &str) -> FlowyResult<()> {
todo!()
}
fn batch_get(&self, ids: Vec<String>) -> FlowyResult<()> {
todo!()
}
fn batch_set(&self, rows: Vec<RawRow>) -> FlowyResult<()> {
todo!()
}
fn batch_delete(&self, ids: Vec<String>) -> FlowyResult<()> {
todo!()
}
}

View File

@ -1,5 +1,5 @@
use crate::cell_service::cell_data::*;
use crate::cell_service::util::*;
use crate::services::cell_data::*;
use crate::services::util::*;
use flowy_error::FlowyError;
use flowy_grid_data_model::entities::{AnyData, Field, FieldType};

View File

@ -1,4 +1,4 @@
use crate::cell_service::cell_data::FlowyMoney;
use crate::services::cell_data::FlowyMoney;
use flowy_error::FlowyError;
use flowy_grid_data_model::entities::{AnyData, Field, FieldType};
use lazy_static::lazy_static;

View File

@ -259,7 +259,7 @@ use flowy_user::event_map::UserCloudService;
use flowy_user_data_model::entities::{
SignInParams, SignInResponse, SignUpParams, SignUpResponse, UpdateUserParams, UserProfile,
};
use lib_infra::{future::FutureResult, timestamp, uuid_string};
use lib_infra::{future::FutureResult, timestamp, uuid};
impl FolderCouldServiceV1 for LocalServer {
fn init(&self) {}
@ -267,7 +267,7 @@ impl FolderCouldServiceV1 for LocalServer {
fn create_workspace(&self, _token: &str, params: CreateWorkspaceParams) -> FutureResult<Workspace, FlowyError> {
let time = timestamp();
let workspace = Workspace {
id: uuid_string(),
id: uuid(),
name: params.name,
desc: params.desc,
apps: RepeatedApp::default(),
@ -327,7 +327,7 @@ impl FolderCouldServiceV1 for LocalServer {
fn create_app(&self, _token: &str, params: CreateAppParams) -> FutureResult<App, FlowyError> {
let time = timestamp();
let app = App {
id: uuid_string(),
id: uuid(),
workspace_id: params.workspace_id,
name: params.name,
desc: params.desc,
@ -369,7 +369,7 @@ impl FolderCouldServiceV1 for LocalServer {
impl UserCloudService for LocalServer {
fn sign_up(&self, params: SignUpParams) -> FutureResult<SignUpResponse, FlowyError> {
let uid = uuid_string();
let uid = uuid();
FutureResult::new(async move {
Ok(SignUpResponse {
user_id: uid.clone(),
@ -381,7 +381,7 @@ impl UserCloudService for LocalServer {
}
fn sign_in(&self, params: SignInParams) -> FutureResult<SignInResponse, FlowyError> {
let user_id = uuid_string();
let user_id = uuid();
FutureResult::new(async {
Ok(SignInResponse {
user_id: user_id.clone(),

View File

@ -30,7 +30,7 @@ pub trait RevisionWSDataStream: Send + Sync {
// The sink provides the data that will be sent through the web socket to the
// backend.
pub trait RevisionWSDataIterator: Send + Sync {
pub trait RevisionWebSocketSink: Send + Sync {
fn next(&self) -> FutureResult<Option<ClientRevisionWSData>, FlowyError>;
}
@ -43,7 +43,7 @@ pub trait RevisionWebSocket: Send + Sync + 'static {
pub struct RevisionWebSocketManager {
pub object_name: String,
pub object_id: String,
ws_data_sink: Arc<dyn RevisionWSDataIterator>,
ws_data_sink: Arc<dyn RevisionWebSocketSink>,
ws_data_stream: Arc<dyn RevisionWSDataStream>,
rev_web_socket: Arc<dyn RevisionWebSocket>,
pub ws_passthrough_tx: Sender<ServerRevisionWSData>,
@ -62,7 +62,7 @@ impl RevisionWebSocketManager {
object_name: &str,
object_id: &str,
rev_web_socket: Arc<dyn RevisionWebSocket>,
ws_data_sink: Arc<dyn RevisionWSDataIterator>,
ws_data_sink: Arc<dyn RevisionWebSocketSink>,
ws_data_stream: Arc<dyn RevisionWSDataStream>,
ping_duration: Duration,
) -> Self {
@ -246,7 +246,7 @@ type SinkStopTx = broadcast::Sender<()>;
pub struct RevisionWSSink {
object_id: String,
object_name: String,
provider: Arc<dyn RevisionWSDataIterator>,
provider: Arc<dyn RevisionWebSocketSink>,
rev_web_socket: Arc<dyn RevisionWebSocket>,
stop_rx: Option<SinkStopRx>,
ping_duration: Duration,
@ -256,7 +256,7 @@ impl RevisionWSSink {
pub fn new(
object_id: &str,
object_name: &str,
provider: Arc<dyn RevisionWSDataIterator>,
provider: Arc<dyn RevisionWebSocketSink>,
rev_web_socket: Arc<dyn RevisionWebSocket>,
stop_rx: SinkStopRx,
ping_duration: Duration,

View File

@ -14,7 +14,7 @@ use flowy_user::{
event_map::UserEvent::{InitUser, SignIn, SignOut, SignUp},
};
use lib_dispatch::prelude::{EventDispatcher, ModuleRequest, ToBytes};
use lib_infra::uuid_string;
use lib_infra::uuid;
use std::{fs, path::PathBuf, sync::Arc};
pub struct ViewTest {
@ -118,7 +118,7 @@ pub fn root_dir() -> String {
}
pub fn random_email() -> String {
format!("{}@appflowy.io", uuid_string())
format!("{}@appflowy.io", uuid())
}
pub fn login_email() -> String {

View File

@ -5,7 +5,7 @@ use crate::helper::*;
use flowy_net::{get_client_server_configuration, ClientServerConfiguration};
use flowy_sdk::{FlowySDK, FlowySDKConfig};
use flowy_user::entities::UserProfile;
use lib_infra::uuid_string;
use lib_infra::uuid;
pub mod prelude {
pub use crate::{event_builder::*, helper::*, *};
@ -36,7 +36,7 @@ impl std::default::Default for FlowySDKTest {
impl FlowySDKTest {
pub fn new(server_config: ClientServerConfiguration) -> Self {
let config = FlowySDKConfig::new(&root_dir(), server_config, &uuid_string()).log_filter("trace");
let config = FlowySDKConfig::new(&root_dir(), server_config, &uuid()).log_filter("trace");
let sdk = std::thread::spawn(|| FlowySDK::new(config)).join().unwrap();
std::mem::forget(sdk.dispatcher());
Self { inner: sdk }

View File

@ -2,7 +2,7 @@ use crate::helper::*;
use flowy_test::{event_builder::UserModuleEventBuilder, FlowySDKTest};
use flowy_user::{errors::ErrorCode, event_map::UserEvent::*};
use flowy_user_data_model::entities::{UpdateUserPayload, UserProfile};
use lib_infra::uuid_string;
use lib_infra::uuid;
use serial_test::*;
#[tokio::test]
@ -54,7 +54,7 @@ async fn user_update_with_name() {
async fn user_update_with_email() {
let sdk = FlowySDKTest::default();
let user = sdk.init_user().await;
let new_email = format!("{}@gmail.com", uuid_string());
let new_email = format!("{}@gmail.com", uuid());
let request = UpdateUserPayload::new(&user.id).email(&new_email);
let _ = UserModuleEventBuilder::new(sdk.clone())
.event(UpdateUser)

4
shared-lib/Cargo.lock generated
View File

@ -413,6 +413,7 @@ dependencies = [
"dissimilar",
"flowy-derive",
"flowy-folder-data-model",
"flowy-grid-data-model",
"futures",
"lib-infra",
"lib-ot",
@ -486,8 +487,11 @@ dependencies = [
"flowy-derive",
"lib-infra",
"protobuf",
"serde",
"serde_json",
"strum",
"strum_macros",
"uuid",
]
[[package]]

View File

@ -10,6 +10,7 @@ lib-ot = { path = "../lib-ot" }
lib-infra = { path = "../lib-infra" }
flowy-derive = { path = "../flowy-derive" }
flowy-folder-data-model = { path = "../flowy-folder-data-model" }
flowy-grid-data-model = { path = "../flowy-grid-data-model" }
protobuf = {version = "2.18.0"}
bytes = "1.0"
log = "0.4.14"

View File

@ -41,10 +41,9 @@ impl FolderPadBuilder {
// TODO: Reconvert from history if delta.to_str() failed.
let folder_json = delta.to_str()?;
let mut folder: FolderPad = serde_json::from_str(&folder_json).map_err(|e| {
CollaborateError::internal().context(format!("Deserialize json to root folder failed: {}", e))
})?;
folder.root = delta;
let mut folder: FolderPad = serde_json::from_str(&folder_json)
.map_err(|e| CollaborateError::internal().context(format!("Deserialize delta to folder failed: {}", e)))?;
folder.delta = delta;
Ok(folder)
}
@ -55,11 +54,11 @@ impl FolderPadBuilder {
pub(crate) fn build(self) -> CollaborateResult<FolderPad> {
let json = serde_json::to_string(&self)
.map_err(|e| CollaborateError::internal().context(format!("serial trash to json failed: {}", e)))?;
.map_err(|e| CollaborateError::internal().context(format!("Serialize to folder json str failed: {}", e)))?;
Ok(FolderPad {
workspaces: self.workspaces,
trash: self.trash,
root: PlainTextDeltaBuilder::new().insert(&json).build(),
delta: PlainTextDeltaBuilder::new().insert(&json).build(),
})
}
}

View File

@ -1,3 +1,4 @@
use crate::util::cal_diff;
use crate::{
client_folder::builder::FolderPadBuilder,
entities::{
@ -6,9 +7,9 @@ use crate::{
},
errors::{CollaborateError, CollaborateResult},
};
use dissimilar::*;
use flowy_folder_data_model::entities::{app::App, trash::Trash, view::View, workspace::Workspace};
use lib_ot::core::{FlowyStr, OperationTransformable, PlainTextDelta, PlainTextDeltaBuilder};
use lib_ot::core::*;
use lib_ot::rich_text::RichTextAttributes;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
@ -17,35 +18,7 @@ pub struct FolderPad {
pub(crate) workspaces: Vec<Arc<Workspace>>,
pub(crate) trash: Vec<Arc<Trash>>,
#[serde(skip)]
pub(crate) root: FolderDelta,
}
pub fn default_folder_delta() -> FolderDelta {
PlainTextDeltaBuilder::new()
.insert(r#"{"workspaces":[],"trash":[]}"#)
.build()
}
pub fn initial_folder_delta(folder_pad: &FolderPad) -> CollaborateResult<FolderDelta> {
let json = folder_pad.to_json()?;
let delta = PlainTextDeltaBuilder::new().insert(&json).build();
Ok(delta)
}
impl std::default::Default for FolderPad {
fn default() -> Self {
FolderPad {
workspaces: vec![],
trash: vec![],
root: default_folder_delta(),
}
}
}
pub struct FolderChange {
pub delta: FolderDelta,
/// md5: the md5 of the FolderPad's delta after applying the change.
pub md5: String,
pub(crate) delta: FolderDelta,
}
impl FolderPad {
@ -65,20 +38,20 @@ impl FolderPad {
}
pub fn delta(&self) -> &FolderDelta {
&self.root
&self.delta
}
pub fn reset_folder(&mut self, delta: FolderDelta) -> CollaborateResult<String> {
let folder = FolderPad::from_delta(delta)?;
self.workspaces = folder.workspaces;
self.trash = folder.trash;
self.root = folder.root;
self.delta = folder.delta;
Ok(self.md5())
}
pub fn compose_remote_delta(&mut self, delta: FolderDelta) -> CollaborateResult<String> {
let composed_delta = self.root.compose(&delta)?;
let composed_delta = self.delta.compose(&delta)?;
self.reset_folder(composed_delta)
}
@ -295,7 +268,7 @@ impl FolderPad {
}
pub fn md5(&self) -> String {
md5(&self.root.to_bytes())
md5(&self.delta.to_bytes())
}
pub fn to_json(&self) -> CollaborateResult<String> {
@ -315,9 +288,13 @@ impl FolderPad {
Some(_) => {
let old = cloned_self.to_json()?;
let new = self.to_json()?;
let delta = cal_diff(old, new);
self.root = self.root.compose(&delta)?;
Ok(Some(FolderChange { delta, md5: self.md5() }))
match cal_diff::<PlainTextAttributes>(old, new) {
None => Ok(None),
Some(delta) => {
self.delta = self.delta.compose(&delta)?;
Ok(Some(FolderChange { delta, md5: self.md5() }))
}
}
}
}
}
@ -346,9 +323,13 @@ impl FolderPad {
Some(_) => {
let old = cloned_self.to_json()?;
let new = self.to_json()?;
let delta = cal_diff(old, new);
self.root = self.root.compose(&delta)?;
Ok(Some(FolderChange { delta, md5: self.md5() }))
match cal_diff::<PlainTextAttributes>(old, new) {
None => Ok(None),
Some(delta) => {
self.delta = self.delta.compose(&delta)?;
Ok(Some(FolderChange { delta, md5: self.md5() }))
}
}
}
}
}
@ -391,23 +372,32 @@ impl FolderPad {
}
}
fn cal_diff(old: String, new: String) -> PlainTextDelta {
let chunks = dissimilar::diff(&old, &new);
let mut delta_builder = PlainTextDeltaBuilder::new();
for chunk in &chunks {
match chunk {
Chunk::Equal(s) => {
delta_builder = delta_builder.retain(FlowyStr::from(*s).utf16_size());
}
Chunk::Delete(s) => {
delta_builder = delta_builder.delete(FlowyStr::from(*s).utf16_size());
}
Chunk::Insert(s) => {
delta_builder = delta_builder.insert(*s);
}
pub fn default_folder_delta() -> FolderDelta {
PlainTextDeltaBuilder::new()
.insert(r#"{"workspaces":[],"trash":[]}"#)
.build()
}
pub fn initial_folder_delta(folder_pad: &FolderPad) -> CollaborateResult<FolderDelta> {
let json = folder_pad.to_json()?;
let delta = PlainTextDeltaBuilder::new().insert(&json).build();
Ok(delta)
}
impl std::default::Default for FolderPad {
fn default() -> Self {
FolderPad {
workspaces: vec![],
trash: vec![],
delta: default_folder_delta(),
}
}
delta_builder.build()
}
pub struct FolderChange {
pub delta: FolderDelta,
/// md5: the md5 of the FolderPad's delta after applying the change.
pub md5: String,
}
#[cfg(test)]

View File

@ -0,0 +1,141 @@
use crate::entities::revision::{md5, Revision};
use crate::errors::{internal_error, CollaborateError, CollaborateResult};
use crate::util::{cal_diff, make_delta_from_revisions};
use flowy_grid_data_model::entities::{CellChangeset, Field, FieldOrder, Grid, RawRow, RowOrder};
use lib_infra::uuid;
use lib_ot::core::{FlowyStr, OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder};
use std::sync::Arc;
pub type GridDelta = PlainTextDelta;
pub type GridDeltaBuilder = PlainTextDeltaBuilder;
pub struct GridPad {
pub(crate) grid: Arc<Grid>,
pub(crate) delta: GridDelta,
}
impl GridPad {
pub fn from_delta(delta: GridDelta) -> CollaborateResult<Self> {
let s = delta.to_str()?;
let grid: Grid = serde_json::from_str(&s)
.map_err(|e| CollaborateError::internal().context(format!("Deserialize delta to grid failed: {}", e)))?;
Ok(Self {
grid: Arc::new(grid),
delta,
})
}
pub fn from_revisions(revisions: Vec<Revision>) -> CollaborateResult<Self> {
let folder_delta: GridDelta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?;
Self::from_delta(folder_delta)
}
pub fn create_row(&mut self, row: &RawRow) -> CollaborateResult<Option<GridChange>> {
self.modify_grid(|grid| {
let row_order = RowOrder {
grid_id: grid.id.clone(),
row_id: row.id.clone(),
visibility: true,
};
grid.row_orders.push(row_order);
Ok(Some(()))
})
}
pub fn create_field(&mut self, field: &Field) -> CollaborateResult<Option<GridChange>> {
self.modify_grid(|grid| {
let field_order = FieldOrder {
field_id: field.id.clone(),
visibility: true,
};
grid.field_orders.push(field_order);
Ok(Some(()))
})
}
pub fn delete_row(&mut self, row_id: &str) -> CollaborateResult<Option<GridChange>> {
self.modify_grid(
|grid| match grid.row_orders.iter().position(|row_order| row_order.row_id == row_id) {
None => Ok(None),
Some(index) => {
grid.row_orders.remove(index);
Ok(Some(()))
}
},
)
}
pub fn delete_field(&mut self, field_id: &str) -> CollaborateResult<Option<GridChange>> {
self.modify_grid(|grid| {
match grid
.field_orders
.iter()
.position(|field_order| field_order.field_id == field_id)
{
None => Ok(None),
Some(index) => {
grid.field_orders.remove(index);
Ok(Some(()))
}
}
})
}
pub fn md5(&self) -> String {
md5(&self.delta.to_bytes())
}
pub fn modify_grid<F>(&mut self, f: F) -> CollaborateResult<Option<GridChange>>
where
F: FnOnce(&mut Grid) -> CollaborateResult<Option<()>>,
{
let cloned_grid = self.grid.clone();
match f(&mut Arc::make_mut(&mut self.grid))? {
None => Ok(None),
Some(_) => {
let old = json_from_grid(&cloned_grid)?;
let new = json_from_grid(&self.grid)?;
match cal_diff::<PlainTextAttributes>(old, new) {
None => Ok(None),
Some(delta) => {
self.delta = self.delta.compose(&delta)?;
Ok(Some(GridChange { delta, md5: self.md5() }))
}
}
}
}
}
}
fn json_from_grid(grid: &Arc<Grid>) -> CollaborateResult<String> {
let json = serde_json::to_string(grid)
.map_err(|err| internal_error(format!("Serialize grid to json str failed. {:?}", err)))?;
Ok(json)
}
pub struct GridChange {
pub delta: GridDelta,
/// md5: the md5 of the grid after applying the change.
pub md5: String,
}
pub fn default_grid_delta(grid: &Grid) -> GridDelta {
let json = serde_json::to_string(&grid).unwrap();
PlainTextDeltaBuilder::new().insert(&json).build()
}
impl std::default::Default for GridPad {
fn default() -> Self {
let grid = Grid {
id: uuid(),
field_orders: Default::default(),
row_orders: Default::default(),
};
let delta = default_grid_delta(&grid);
GridPad {
grid: Arc::new(grid),
delta,
}
}
}

View File

@ -0,0 +1,3 @@
mod grid_pad;
pub use grid_pad::*;

View File

@ -1,5 +1,6 @@
pub mod client_document;
pub mod client_folder;
pub mod client_grid;
pub mod entities;
pub mod errors;
pub mod protobuf;

View File

@ -10,6 +10,8 @@ use crate::{
Revision as RevisionPB,
},
};
use dissimilar::Chunk;
use lib_ot::core::{DeltaBuilder, FlowyStr};
use lib_ot::{
core::{Attributes, Delta, OperationTransformable, NEW_LINE, WHITESPACE},
rich_text::RichTextDelta,
@ -254,3 +256,28 @@ pub fn rev_id_from_str(s: &str) -> Result<i64, CollaborateError> {
.map_err(|e| CollaborateError::internal().context(format!("Parse rev_id from {} failed. {}", s, e)))?;
Ok(rev_id)
}
pub fn cal_diff<T: Attributes>(old: String, new: String) -> Option<Delta<T>> {
let chunks = dissimilar::diff(&old, &new);
let mut delta_builder = DeltaBuilder::<T>::new();
for chunk in &chunks {
match chunk {
Chunk::Equal(s) => {
delta_builder = delta_builder.retain(FlowyStr::from(*s).utf16_size());
}
Chunk::Delete(s) => {
delta_builder = delta_builder.delete(FlowyStr::from(*s).utf16_size());
}
Chunk::Insert(s) => {
delta_builder = delta_builder.insert(*s);
}
}
}
let delta = delta_builder.build();
if delta.is_empty() {
None
} else {
Some(delta)
}
}

View File

@ -11,6 +11,9 @@ protobuf = {version = "2.18.0"}
bytes = "1.0"
strum = "0.21"
strum_macros = "0.21"
serde = { version = "1.0", features = ["derive"] }
serde_json = {version = "1.0"}
uuid = { version = "0.8", features = ["serde", "v4"] }
[build-dependencies]
lib-infra = { path = "../lib-infra", features = ["protobuf_file_gen"] }

View File

@ -1,24 +1,23 @@
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use strum_macros::{Display, EnumIter, EnumString};
#[derive(Debug, Default, ProtoBuf)]
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, ProtoBuf)]
pub struct Grid {
#[pb(index = 1)]
pub id: String,
#[pb(index = 2)]
pub filters: RepeatedFilter,
#[pb(index = 3)]
#[serde(flatten)]
pub field_orders: RepeatedFieldOrder,
#[pb(index = 4)]
#[pb(index = 3)]
#[serde(flatten)]
pub row_orders: RepeatedRowOrder,
}
#[derive(Debug, Default, ProtoBuf)]
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, ProtoBuf)]
pub struct FieldOrder {
#[pb(index = 1)]
pub field_id: String,
@ -27,12 +26,27 @@ pub struct FieldOrder {
pub visibility: bool,
}
#[derive(Debug, Default, ProtoBuf)]
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, ProtoBuf)]
pub struct RepeatedFieldOrder {
#[pb(index = 1)]
#[serde(rename(serialize = "field_orders", deserialize = "field_orders"))]
pub items: Vec<FieldOrder>,
}
impl std::ops::Deref for RepeatedFieldOrder {
type Target = Vec<FieldOrder>;
fn deref(&self) -> &Self::Target {
&self.items
}
}
impl std::ops::DerefMut for RepeatedFieldOrder {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.items
}
}
#[derive(Debug, Default, ProtoBuf)]
pub struct Field {
#[pb(index = 1)]
@ -63,7 +77,7 @@ pub struct RepeatedField {
pub items: Vec<Field>,
}
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum, EnumString, EnumIter, Display)]
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum, EnumString, EnumIter, Display, Serialize, Deserialize)]
pub enum FieldType {
RichText = 0,
Number = 1,
@ -130,7 +144,7 @@ impl ToString for AnyData {
}
}
#[derive(Debug, Default, ProtoBuf)]
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, ProtoBuf)]
pub struct RowOrder {
#[pb(index = 1)]
pub grid_id: String,
@ -142,12 +156,27 @@ pub struct RowOrder {
pub visibility: bool,
}
#[derive(Debug, Default, ProtoBuf)]
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, ProtoBuf)]
pub struct RepeatedRowOrder {
#[pb(index = 1)]
#[serde(rename(serialize = "row_orders", deserialize = "row_orders"))]
pub items: Vec<RowOrder>,
}
impl std::ops::Deref for RepeatedRowOrder {
type Target = Vec<RowOrder>;
fn deref(&self) -> &Self::Target {
&self.items
}
}
impl std::ops::DerefMut for RepeatedRowOrder {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.items
}
}
#[derive(Debug, Default, ProtoBuf)]
pub struct RawRow {
#[pb(index = 1)]
@ -202,6 +231,21 @@ pub struct Cell {
pub content: String,
}
#[derive(Debug, Default, ProtoBuf)]
pub struct CellChangeset {
#[pb(index = 1)]
pub id: String,
#[pb(index = 2)]
pub row_id: String,
#[pb(index = 3)]
pub field_id: String,
#[pb(index = 4)]
pub data: String,
}
#[derive(ProtoBuf, Default)]
pub struct CreateGridPayload {
#[pb(index = 1)]
@ -214,20 +258,8 @@ pub struct GridId {
pub value: String,
}
#[derive(Debug, Default, ProtoBuf)]
pub struct Filter {
#[pb(index = 1)]
pub id: String,
#[pb(index = 2)]
pub name: String,
#[pb(index = 3)]
pub desc: String,
}
#[derive(Debug, Default, ProtoBuf)]
pub struct RepeatedFilter {
#[pb(index = 1)]
pub items: Vec<Filter>,
impl AsRef<str> for GridId {
fn as_ref(&self) -> &str {
&self.value
}
}

View File

@ -27,7 +27,6 @@
pub struct Grid {
// message fields
pub id: ::std::string::String,
pub filters: ::protobuf::SingularPtrField<RepeatedFilter>,
pub field_orders: ::protobuf::SingularPtrField<RepeatedFieldOrder>,
pub row_orders: ::protobuf::SingularPtrField<RepeatedRowOrder>,
// special fields
@ -72,40 +71,7 @@ impl Grid {
::std::mem::replace(&mut self.id, ::std::string::String::new())
}
// .RepeatedFilter filters = 2;
pub fn get_filters(&self) -> &RepeatedFilter {
self.filters.as_ref().unwrap_or_else(|| <RepeatedFilter as ::protobuf::Message>::default_instance())
}
pub fn clear_filters(&mut self) {
self.filters.clear();
}
pub fn has_filters(&self) -> bool {
self.filters.is_some()
}
// Param is passed by value, moved
pub fn set_filters(&mut self, v: RepeatedFilter) {
self.filters = ::protobuf::SingularPtrField::some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_filters(&mut self) -> &mut RepeatedFilter {
if self.filters.is_none() {
self.filters.set_default();
}
self.filters.as_mut().unwrap()
}
// Take field
pub fn take_filters(&mut self) -> RepeatedFilter {
self.filters.take().unwrap_or_else(|| RepeatedFilter::new())
}
// .RepeatedFieldOrder field_orders = 3;
// .RepeatedFieldOrder field_orders = 2;
pub fn get_field_orders(&self) -> &RepeatedFieldOrder {
@ -138,7 +104,7 @@ impl Grid {
self.field_orders.take().unwrap_or_else(|| RepeatedFieldOrder::new())
}
// .RepeatedRowOrder row_orders = 4;
// .RepeatedRowOrder row_orders = 3;
pub fn get_row_orders(&self) -> &RepeatedRowOrder {
@ -174,11 +140,6 @@ impl Grid {
impl ::protobuf::Message for Grid {
fn is_initialized(&self) -> bool {
for v in &self.filters {
if !v.is_initialized() {
return false;
}
};
for v in &self.field_orders {
if !v.is_initialized() {
return false;
@ -200,12 +161,9 @@ impl ::protobuf::Message for Grid {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.id)?;
},
2 => {
::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.filters)?;
},
3 => {
::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.field_orders)?;
},
4 => {
3 => {
::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.row_orders)?;
},
_ => {
@ -223,10 +181,6 @@ impl ::protobuf::Message for Grid {
if !self.id.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.id);
}
if let Some(ref v) = self.filters.as_ref() {
let len = v.compute_size();
my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
}
if let Some(ref v) = self.field_orders.as_ref() {
let len = v.compute_size();
my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
@ -244,18 +198,13 @@ impl ::protobuf::Message for Grid {
if !self.id.is_empty() {
os.write_string(1, &self.id)?;
}
if let Some(ref v) = self.filters.as_ref() {
if let Some(ref v) = self.field_orders.as_ref() {
os.write_tag(2, ::protobuf::wire_format::WireTypeLengthDelimited)?;
os.write_raw_varint32(v.get_cached_size())?;
v.write_to_with_cached_sizes(os)?;
}
if let Some(ref v) = self.field_orders.as_ref() {
os.write_tag(3, ::protobuf::wire_format::WireTypeLengthDelimited)?;
os.write_raw_varint32(v.get_cached_size())?;
v.write_to_with_cached_sizes(os)?;
}
if let Some(ref v) = self.row_orders.as_ref() {
os.write_tag(4, ::protobuf::wire_format::WireTypeLengthDelimited)?;
os.write_tag(3, ::protobuf::wire_format::WireTypeLengthDelimited)?;
os.write_raw_varint32(v.get_cached_size())?;
v.write_to_with_cached_sizes(os)?;
}
@ -302,11 +251,6 @@ impl ::protobuf::Message for Grid {
|m: &Grid| { &m.id },
|m: &mut Grid| { &mut m.id },
));
fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<RepeatedFilter>>(
"filters",
|m: &Grid| { &m.filters },
|m: &mut Grid| { &mut m.filters },
));
fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<RepeatedFieldOrder>>(
"field_orders",
|m: &Grid| { &m.field_orders },
@ -334,7 +278,6 @@ impl ::protobuf::Message for Grid {
impl ::protobuf::Clear for Grid {
fn clear(&mut self) {
self.id.clear();
self.filters.clear();
self.field_orders.clear();
self.row_orders.clear();
self.unknown_fields.clear();
@ -3011,6 +2954,291 @@ impl ::protobuf::reflect::ProtobufValue for Cell {
}
}
#[derive(PartialEq,Clone,Default)]
pub struct CellChangeset {
// message fields
pub id: ::std::string::String,
pub row_id: ::std::string::String,
pub field_id: ::std::string::String,
pub data: ::std::string::String,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a CellChangeset {
fn default() -> &'a CellChangeset {
<CellChangeset as ::protobuf::Message>::default_instance()
}
}
impl CellChangeset {
pub fn new() -> CellChangeset {
::std::default::Default::default()
}
// string id = 1;
pub fn get_id(&self) -> &str {
&self.id
}
pub fn clear_id(&mut self) {
self.id.clear();
}
// Param is passed by value, moved
pub fn set_id(&mut self, v: ::std::string::String) {
self.id = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_id(&mut self) -> &mut ::std::string::String {
&mut self.id
}
// Take field
pub fn take_id(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.id, ::std::string::String::new())
}
// string row_id = 2;
pub fn get_row_id(&self) -> &str {
&self.row_id
}
pub fn clear_row_id(&mut self) {
self.row_id.clear();
}
// Param is passed by value, moved
pub fn set_row_id(&mut self, v: ::std::string::String) {
self.row_id = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_row_id(&mut self) -> &mut ::std::string::String {
&mut self.row_id
}
// Take field
pub fn take_row_id(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.row_id, ::std::string::String::new())
}
// string field_id = 3;
pub fn get_field_id(&self) -> &str {
&self.field_id
}
pub fn clear_field_id(&mut self) {
self.field_id.clear();
}
// Param is passed by value, moved
pub fn set_field_id(&mut self, v: ::std::string::String) {
self.field_id = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_field_id(&mut self) -> &mut ::std::string::String {
&mut self.field_id
}
// Take field
pub fn take_field_id(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.field_id, ::std::string::String::new())
}
// string data = 4;
pub fn get_data(&self) -> &str {
&self.data
}
pub fn clear_data(&mut self) {
self.data.clear();
}
// Param is passed by value, moved
pub fn set_data(&mut self, v: ::std::string::String) {
self.data = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_data(&mut self) -> &mut ::std::string::String {
&mut self.data
}
// Take field
pub fn take_data(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.data, ::std::string::String::new())
}
}
impl ::protobuf::Message for CellChangeset {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.id)?;
},
2 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.row_id)?;
},
3 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.field_id)?;
},
4 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.data)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if !self.id.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.id);
}
if !self.row_id.is_empty() {
my_size += ::protobuf::rt::string_size(2, &self.row_id);
}
if !self.field_id.is_empty() {
my_size += ::protobuf::rt::string_size(3, &self.field_id);
}
if !self.data.is_empty() {
my_size += ::protobuf::rt::string_size(4, &self.data);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
if !self.id.is_empty() {
os.write_string(1, &self.id)?;
}
if !self.row_id.is_empty() {
os.write_string(2, &self.row_id)?;
}
if !self.field_id.is_empty() {
os.write_string(3, &self.field_id)?;
}
if !self.data.is_empty() {
os.write_string(4, &self.data)?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &dyn (::std::any::Any) {
self as &dyn (::std::any::Any)
}
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
self as &mut dyn (::std::any::Any)
}
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> CellChangeset {
CellChangeset::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"id",
|m: &CellChangeset| { &m.id },
|m: &mut CellChangeset| { &mut m.id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"row_id",
|m: &CellChangeset| { &m.row_id },
|m: &mut CellChangeset| { &mut m.row_id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"field_id",
|m: &CellChangeset| { &m.field_id },
|m: &mut CellChangeset| { &mut m.field_id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"data",
|m: &CellChangeset| { &m.data },
|m: &mut CellChangeset| { &mut m.data },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<CellChangeset>(
"CellChangeset",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static CellChangeset {
static instance: ::protobuf::rt::LazyV2<CellChangeset> = ::protobuf::rt::LazyV2::INIT;
instance.get(CellChangeset::new)
}
}
impl ::protobuf::Clear for CellChangeset {
fn clear(&mut self) {
self.id.clear();
self.row_id.clear();
self.field_id.clear();
self.data.clear();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for CellChangeset {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for CellChangeset {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(PartialEq,Clone,Default)]
pub struct CreateGridPayload {
// message fields
@ -3329,415 +3557,6 @@ impl ::protobuf::reflect::ProtobufValue for GridId {
}
}
#[derive(PartialEq,Clone,Default)]
pub struct Filter {
// message fields
pub id: ::std::string::String,
pub name: ::std::string::String,
pub desc: ::std::string::String,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a Filter {
fn default() -> &'a Filter {
<Filter as ::protobuf::Message>::default_instance()
}
}
impl Filter {
pub fn new() -> Filter {
::std::default::Default::default()
}
// string id = 1;
pub fn get_id(&self) -> &str {
&self.id
}
pub fn clear_id(&mut self) {
self.id.clear();
}
// Param is passed by value, moved
pub fn set_id(&mut self, v: ::std::string::String) {
self.id = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_id(&mut self) -> &mut ::std::string::String {
&mut self.id
}
// Take field
pub fn take_id(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.id, ::std::string::String::new())
}
// string name = 2;
pub fn get_name(&self) -> &str {
&self.name
}
pub fn clear_name(&mut self) {
self.name.clear();
}
// Param is passed by value, moved
pub fn set_name(&mut self, v: ::std::string::String) {
self.name = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_name(&mut self) -> &mut ::std::string::String {
&mut self.name
}
// Take field
pub fn take_name(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.name, ::std::string::String::new())
}
// string desc = 3;
pub fn get_desc(&self) -> &str {
&self.desc
}
pub fn clear_desc(&mut self) {
self.desc.clear();
}
// Param is passed by value, moved
pub fn set_desc(&mut self, v: ::std::string::String) {
self.desc = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_desc(&mut self) -> &mut ::std::string::String {
&mut self.desc
}
// Take field
pub fn take_desc(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.desc, ::std::string::String::new())
}
}
impl ::protobuf::Message for Filter {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.id)?;
},
2 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.name)?;
},
3 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.desc)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if !self.id.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.id);
}
if !self.name.is_empty() {
my_size += ::protobuf::rt::string_size(2, &self.name);
}
if !self.desc.is_empty() {
my_size += ::protobuf::rt::string_size(3, &self.desc);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
if !self.id.is_empty() {
os.write_string(1, &self.id)?;
}
if !self.name.is_empty() {
os.write_string(2, &self.name)?;
}
if !self.desc.is_empty() {
os.write_string(3, &self.desc)?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &dyn (::std::any::Any) {
self as &dyn (::std::any::Any)
}
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
self as &mut dyn (::std::any::Any)
}
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> Filter {
Filter::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"id",
|m: &Filter| { &m.id },
|m: &mut Filter| { &mut m.id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"name",
|m: &Filter| { &m.name },
|m: &mut Filter| { &mut m.name },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"desc",
|m: &Filter| { &m.desc },
|m: &mut Filter| { &mut m.desc },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<Filter>(
"Filter",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static Filter {
static instance: ::protobuf::rt::LazyV2<Filter> = ::protobuf::rt::LazyV2::INIT;
instance.get(Filter::new)
}
}
impl ::protobuf::Clear for Filter {
fn clear(&mut self) {
self.id.clear();
self.name.clear();
self.desc.clear();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for Filter {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for Filter {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(PartialEq,Clone,Default)]
pub struct RepeatedFilter {
// message fields
pub items: ::protobuf::RepeatedField<Filter>,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a RepeatedFilter {
fn default() -> &'a RepeatedFilter {
<RepeatedFilter as ::protobuf::Message>::default_instance()
}
}
impl RepeatedFilter {
pub fn new() -> RepeatedFilter {
::std::default::Default::default()
}
// repeated .Filter items = 1;
pub fn get_items(&self) -> &[Filter] {
&self.items
}
pub fn clear_items(&mut self) {
self.items.clear();
}
// Param is passed by value, moved
pub fn set_items(&mut self, v: ::protobuf::RepeatedField<Filter>) {
self.items = v;
}
// Mutable pointer to the field.
pub fn mut_items(&mut self) -> &mut ::protobuf::RepeatedField<Filter> {
&mut self.items
}
// Take field
pub fn take_items(&mut self) -> ::protobuf::RepeatedField<Filter> {
::std::mem::replace(&mut self.items, ::protobuf::RepeatedField::new())
}
}
impl ::protobuf::Message for RepeatedFilter {
fn is_initialized(&self) -> bool {
for v in &self.items {
if !v.is_initialized() {
return false;
}
};
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.items)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
for value in &self.items {
let len = value.compute_size();
my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
};
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
for v in &self.items {
os.write_tag(1, ::protobuf::wire_format::WireTypeLengthDelimited)?;
os.write_raw_varint32(v.get_cached_size())?;
v.write_to_with_cached_sizes(os)?;
};
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &dyn (::std::any::Any) {
self as &dyn (::std::any::Any)
}
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
self as &mut dyn (::std::any::Any)
}
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> RepeatedFilter {
RepeatedFilter::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<Filter>>(
"items",
|m: &RepeatedFilter| { &m.items },
|m: &mut RepeatedFilter| { &mut m.items },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<RepeatedFilter>(
"RepeatedFilter",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static RepeatedFilter {
static instance: ::protobuf::rt::LazyV2<RepeatedFilter> = ::protobuf::rt::LazyV2::INIT;
instance.get(RepeatedFilter::new)
}
}
impl ::protobuf::Clear for RepeatedFilter {
fn clear(&mut self) {
self.items.clear();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for RepeatedFilter {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for RepeatedFilter {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum FieldType {
RichText = 0,
@ -3801,11 +3620,10 @@ impl ::protobuf::reflect::ProtobufValue for FieldType {
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\ngrid.proto\"\xab\x01\n\x04Grid\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\
\x02id\x12)\n\x07filters\x18\x02\x20\x01(\x0b2\x0f.RepeatedFilterR\x07fi\
lters\x126\n\x0cfield_orders\x18\x03\x20\x01(\x0b2\x13.RepeatedFieldOrde\
rR\x0bfieldOrders\x120\n\nrow_orders\x18\x04\x20\x01(\x0b2\x11.RepeatedR\
owOrderR\trowOrders\"G\n\nFieldOrder\x12\x19\n\x08field_id\x18\x01\x20\
\n\ngrid.proto\"\x80\x01\n\x04Grid\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\
\x02id\x126\n\x0cfield_orders\x18\x02\x20\x01(\x0b2\x13.RepeatedFieldOrd\
erR\x0bfieldOrders\x120\n\nrow_orders\x18\x03\x20\x01(\x0b2\x11.Repeated\
RowOrderR\trowOrders\"G\n\nFieldOrder\x12\x19\n\x08field_id\x18\x01\x20\
\x01(\tR\x07fieldId\x12\x1e\n\nvisibility\x18\x02\x20\x01(\x08R\nvisibil\
ity\"7\n\x12RepeatedFieldOrder\x12!\n\x05items\x18\x01\x20\x03(\x0b2\x0b\
.FieldOrderR\x05items\"\xc5\x01\n\x05Field\x12\x0e\n\x02id\x18\x01\x20\
@ -3835,15 +3653,15 @@ static file_descriptor_proto_data: &'static [u8] = b"\
y\x18\x01\x20\x01(\tR\x03key\x12\x1b\n\x05value\x18\x02\x20\x01(\x0b2\
\x05.CellR\x05value:\x028\x01\"K\n\x04Cell\x12\x0e\n\x02id\x18\x01\x20\
\x01(\tR\x02id\x12\x19\n\x08field_id\x18\x02\x20\x01(\tR\x07fieldId\x12\
\x18\n\x07content\x18\x03\x20\x01(\tR\x07content\"'\n\x11CreateGridPaylo\
ad\x12\x12\n\x04name\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06GridId\x12\
\x14\n\x05value\x18\x01\x20\x01(\tR\x05value\"@\n\x06Filter\x12\x0e\n\
\x02id\x18\x01\x20\x01(\tR\x02id\x12\x12\n\x04name\x18\x02\x20\x01(\tR\
\x04name\x12\x12\n\x04desc\x18\x03\x20\x01(\tR\x04desc\"/\n\x0eRepeatedF\
ilter\x12\x1d\n\x05items\x18\x01\x20\x03(\x0b2\x07.FilterR\x05items*d\n\
\tFieldType\x12\x0c\n\x08RichText\x10\0\x12\n\n\x06Number\x10\x01\x12\
\x0c\n\x08DateTime\x10\x02\x12\x10\n\x0cSingleSelect\x10\x03\x12\x0f\n\
\x0bMultiSelect\x10\x04\x12\x0c\n\x08Checkbox\x10\x05b\x06proto3\
\x18\n\x07content\x18\x03\x20\x01(\tR\x07content\"e\n\rCellChangeset\x12\
\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x15\n\x06row_id\x18\x02\x20\
\x01(\tR\x05rowId\x12\x19\n\x08field_id\x18\x03\x20\x01(\tR\x07fieldId\
\x12\x12\n\x04data\x18\x04\x20\x01(\tR\x04data\"'\n\x11CreateGridPayload\
\x12\x12\n\x04name\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06GridId\x12\x14\
\n\x05value\x18\x01\x20\x01(\tR\x05value*d\n\tFieldType\x12\x0c\n\x08Ric\
hText\x10\0\x12\n\n\x06Number\x10\x01\x12\x0c\n\x08DateTime\x10\x02\x12\
\x10\n\x0cSingleSelect\x10\x03\x12\x0f\n\x0bMultiSelect\x10\x04\x12\x0c\
\n\x08Checkbox\x10\x05b\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -2,9 +2,8 @@ syntax = "proto3";
message Grid {
string id = 1;
RepeatedFilter filters = 2;
RepeatedFieldOrder field_orders = 3;
RepeatedRowOrder row_orders = 4;
RepeatedFieldOrder field_orders = 2;
RepeatedRowOrder row_orders = 3;
}
message FieldOrder {
string field_id = 1;
@ -60,20 +59,18 @@ message Cell {
string field_id = 2;
string content = 3;
}
message CellChangeset {
string id = 1;
string row_id = 2;
string field_id = 3;
string data = 4;
}
message CreateGridPayload {
string name = 1;
}
message GridId {
string value = 1;
}
message Filter {
string id = 1;
string name = 2;
string desc = 3;
}
message RepeatedFilter {
repeated Filter items = 1;
}
enum FieldType {
RichText = 0;
Number = 1;

View File

@ -0,0 +1,58 @@
use flowy_grid_data_model::entities::*;
#[test]
fn grid_serde_test() {
let grid_id = "1".to_owned();
let field_orders = RepeatedFieldOrder {
items: vec![create_field_order("1")],
};
let row_orders = RepeatedRowOrder {
items: vec![create_row_order(&grid_id, "1")],
};
let grid = Grid {
id: grid_id,
field_orders,
row_orders,
};
let json = serde_json::to_string(&grid).unwrap();
let grid2: Grid = serde_json::from_str(&json).unwrap();
assert_eq!(grid, grid2);
assert_eq!(
json,
r#"{"id":"1","field_orders":[{"field_id":"1","visibility":false}],"row_orders":[{"grid_id":"1","row_id":"1","visibility":false}]}"#
)
}
#[test]
fn grid_default_serde_test() {
let grid_id = "1".to_owned();
let grid = Grid {
id: grid_id,
field_orders: RepeatedFieldOrder::default(),
row_orders: RepeatedRowOrder::default(),
};
let json = serde_json::to_string(&grid).unwrap();
assert_eq!(json, r#"{"id":"1","field_orders":[],"row_orders":[]}"#)
}
fn create_field_order(field_id: &str) -> FieldOrder {
FieldOrder {
field_id: field_id.to_owned(),
visibility: false,
}
}
fn create_row_order(grid_id: &str, row_id: &str) -> RowOrder {
RowOrder {
grid_id: grid_id.to_string(),
row_id: row_id.to_string(),
visibility: false,
}
}
fn uuid() -> String {
uuid::Uuid::new_v4().to_string()
}

View File

@ -3,7 +3,7 @@ pub mod future;
pub mod retry;
#[allow(dead_code)]
pub fn uuid_string() -> String {
pub fn uuid() -> String {
uuid::Uuid::new_v4().to_string()
}