diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/meta.pb.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/meta.pb.dart index 924cb4addb..3b5d6f5dad 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/meta.pb.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/meta.pb.dart @@ -17,7 +17,7 @@ class GridMeta extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridMeta', createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId') ..pc(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fields', $pb.PbFieldType.PM, subBuilder: Field.create) - ..pc(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blocks', $pb.PbFieldType.PM, subBuilder: Block.create) + ..pc(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blocks', $pb.PbFieldType.PM, subBuilder: GridBlock.create) ..hasRequiredFields = false ; @@ -25,7 +25,7 @@ class GridMeta extends $pb.GeneratedMessage { factory GridMeta({ $core.String? gridId, $core.Iterable? fields, - $core.Iterable? blocks, + $core.Iterable? blocks, }) { final _result = create(); if (gridId != null) { @@ -73,19 +73,19 @@ class GridMeta extends $pb.GeneratedMessage { $core.List get fields => $_getList(1); @$pb.TagNumber(3) - $core.List get blocks => $_getList(2); + $core.List get blocks => $_getList(2); } -class Block extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Block', createEmptyInstance: create) +class GridBlock extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridBlock', createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id') ..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'startRowIndex', $pb.PbFieldType.O3) ..a<$core.int>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rowCount', $pb.PbFieldType.O3) ..hasRequiredFields = false ; - Block._() : super(); - factory Block({ + GridBlock._() : super(); + factory GridBlock({ $core.String? id, $core.int? startRowIndex, $core.int? rowCount, @@ -102,26 +102,26 @@ class Block extends $pb.GeneratedMessage { } return _result; } - factory Block.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory Block.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + factory GridBlock.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory GridBlock.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') - Block clone() => Block()..mergeFromMessage(this); + GridBlock clone() => GridBlock()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - Block copyWith(void Function(Block) updates) => super.copyWith((message) => updates(message as Block)) as Block; // ignore: deprecated_member_use + GridBlock copyWith(void Function(GridBlock) updates) => super.copyWith((message) => updates(message as GridBlock)) as GridBlock; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') - static Block create() => Block._(); - Block createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); + static GridBlock create() => GridBlock._(); + GridBlock createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') - static Block getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Block? _defaultInstance; + static GridBlock getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static GridBlock? _defaultInstance; @$pb.TagNumber(1) $core.String get id => $_getSZ(0); @@ -151,15 +151,15 @@ class Block extends $pb.GeneratedMessage { void clearRowCount() => clearField(3); } -class BlockMeta extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'BlockMeta', createEmptyInstance: create) +class GridBlockMeta extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridBlockMeta', createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockId') ..pc(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rows', $pb.PbFieldType.PM, subBuilder: RowMeta.create) ..hasRequiredFields = false ; - BlockMeta._() : super(); - factory BlockMeta({ + GridBlockMeta._() : super(); + factory GridBlockMeta({ $core.String? blockId, $core.Iterable? rows, }) { @@ -172,26 +172,26 @@ class BlockMeta extends $pb.GeneratedMessage { } return _result; } - factory BlockMeta.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory BlockMeta.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + factory GridBlockMeta.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory GridBlockMeta.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') - BlockMeta clone() => BlockMeta()..mergeFromMessage(this); + GridBlockMeta clone() => GridBlockMeta()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - BlockMeta copyWith(void Function(BlockMeta) updates) => super.copyWith((message) => updates(message as BlockMeta)) as BlockMeta; // ignore: deprecated_member_use + GridBlockMeta copyWith(void Function(GridBlockMeta) updates) => super.copyWith((message) => updates(message as GridBlockMeta)) as GridBlockMeta; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') - static BlockMeta create() => BlockMeta._(); - BlockMeta createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); + static GridBlockMeta create() => GridBlockMeta._(); + GridBlockMeta createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') - static BlockMeta getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BlockMeta? _defaultInstance; + static GridBlockMeta getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static GridBlockMeta? _defaultInstance; @$pb.TagNumber(1) $core.String get blockId => $_getSZ(0); @@ -394,6 +394,244 @@ class RepeatedField extends $pb.GeneratedMessage { $core.List get items => $_getList(0); } +enum FieldChangeset_OneOfName { + name, + notSet +} + +enum FieldChangeset_OneOfDesc { + desc, + notSet +} + +enum FieldChangeset_OneOfFieldType { + fieldType, + notSet +} + +enum FieldChangeset_OneOfFrozen { + frozen, + notSet +} + +enum FieldChangeset_OneOfVisibility { + visibility, + notSet +} + +enum FieldChangeset_OneOfWidth { + width, + notSet +} + +enum FieldChangeset_OneOfTypeOptions { + typeOptions, + notSet +} + +class FieldChangeset extends $pb.GeneratedMessage { + static const $core.Map<$core.int, FieldChangeset_OneOfName> _FieldChangeset_OneOfNameByTag = { + 2 : FieldChangeset_OneOfName.name, + 0 : FieldChangeset_OneOfName.notSet + }; + static const $core.Map<$core.int, FieldChangeset_OneOfDesc> _FieldChangeset_OneOfDescByTag = { + 3 : FieldChangeset_OneOfDesc.desc, + 0 : FieldChangeset_OneOfDesc.notSet + }; + static const $core.Map<$core.int, FieldChangeset_OneOfFieldType> _FieldChangeset_OneOfFieldTypeByTag = { + 4 : FieldChangeset_OneOfFieldType.fieldType, + 0 : FieldChangeset_OneOfFieldType.notSet + }; + static const $core.Map<$core.int, FieldChangeset_OneOfFrozen> _FieldChangeset_OneOfFrozenByTag = { + 5 : FieldChangeset_OneOfFrozen.frozen, + 0 : FieldChangeset_OneOfFrozen.notSet + }; + static const $core.Map<$core.int, FieldChangeset_OneOfVisibility> _FieldChangeset_OneOfVisibilityByTag = { + 6 : FieldChangeset_OneOfVisibility.visibility, + 0 : FieldChangeset_OneOfVisibility.notSet + }; + static const $core.Map<$core.int, FieldChangeset_OneOfWidth> _FieldChangeset_OneOfWidthByTag = { + 7 : FieldChangeset_OneOfWidth.width, + 0 : FieldChangeset_OneOfWidth.notSet + }; + static const $core.Map<$core.int, FieldChangeset_OneOfTypeOptions> _FieldChangeset_OneOfTypeOptionsByTag = { + 8 : FieldChangeset_OneOfTypeOptions.typeOptions, + 0 : FieldChangeset_OneOfTypeOptions.notSet + }; + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'FieldChangeset', createEmptyInstance: create) + ..oo(0, [2]) + ..oo(1, [3]) + ..oo(2, [4]) + ..oo(3, [5]) + ..oo(4, [6]) + ..oo(5, [7]) + ..oo(6, [8]) + ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId') + ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name') + ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc') + ..e(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldType', $pb.PbFieldType.OE, defaultOrMaker: FieldType.RichText, valueOf: FieldType.valueOf, enumValues: FieldType.values) + ..aOB(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'frozen') + ..aOB(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'visibility') + ..a<$core.int>(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'width', $pb.PbFieldType.O3) + ..aOM(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'typeOptions', subBuilder: AnyData.create) + ..hasRequiredFields = false + ; + + FieldChangeset._() : super(); + factory FieldChangeset({ + $core.String? fieldId, + $core.String? name, + $core.String? desc, + FieldType? fieldType, + $core.bool? frozen, + $core.bool? visibility, + $core.int? width, + AnyData? typeOptions, + }) { + final _result = create(); + if (fieldId != null) { + _result.fieldId = fieldId; + } + if (name != null) { + _result.name = name; + } + if (desc != null) { + _result.desc = desc; + } + if (fieldType != null) { + _result.fieldType = fieldType; + } + if (frozen != null) { + _result.frozen = frozen; + } + if (visibility != null) { + _result.visibility = visibility; + } + if (width != null) { + _result.width = width; + } + if (typeOptions != null) { + _result.typeOptions = typeOptions; + } + return _result; + } + factory FieldChangeset.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory FieldChangeset.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') + FieldChangeset clone() => FieldChangeset()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + FieldChangeset copyWith(void Function(FieldChangeset) updates) => super.copyWith((message) => updates(message as FieldChangeset)) as FieldChangeset; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static FieldChangeset create() => FieldChangeset._(); + FieldChangeset createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static FieldChangeset getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static FieldChangeset? _defaultInstance; + + FieldChangeset_OneOfName whichOneOfName() => _FieldChangeset_OneOfNameByTag[$_whichOneof(0)]!; + void clearOneOfName() => clearField($_whichOneof(0)); + + FieldChangeset_OneOfDesc whichOneOfDesc() => _FieldChangeset_OneOfDescByTag[$_whichOneof(1)]!; + void clearOneOfDesc() => clearField($_whichOneof(1)); + + FieldChangeset_OneOfFieldType whichOneOfFieldType() => _FieldChangeset_OneOfFieldTypeByTag[$_whichOneof(2)]!; + void clearOneOfFieldType() => clearField($_whichOneof(2)); + + FieldChangeset_OneOfFrozen whichOneOfFrozen() => _FieldChangeset_OneOfFrozenByTag[$_whichOneof(3)]!; + void clearOneOfFrozen() => clearField($_whichOneof(3)); + + FieldChangeset_OneOfVisibility whichOneOfVisibility() => _FieldChangeset_OneOfVisibilityByTag[$_whichOneof(4)]!; + void clearOneOfVisibility() => clearField($_whichOneof(4)); + + FieldChangeset_OneOfWidth whichOneOfWidth() => _FieldChangeset_OneOfWidthByTag[$_whichOneof(5)]!; + void clearOneOfWidth() => clearField($_whichOneof(5)); + + FieldChangeset_OneOfTypeOptions whichOneOfTypeOptions() => _FieldChangeset_OneOfTypeOptionsByTag[$_whichOneof(6)]!; + void clearOneOfTypeOptions() => clearField($_whichOneof(6)); + + @$pb.TagNumber(1) + $core.String get fieldId => $_getSZ(0); + @$pb.TagNumber(1) + set fieldId($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasFieldId() => $_has(0); + @$pb.TagNumber(1) + void clearFieldId() => 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); + + @$pb.TagNumber(4) + FieldType get fieldType => $_getN(3); + @$pb.TagNumber(4) + set fieldType(FieldType v) { setField(4, v); } + @$pb.TagNumber(4) + $core.bool hasFieldType() => $_has(3); + @$pb.TagNumber(4) + void clearFieldType() => clearField(4); + + @$pb.TagNumber(5) + $core.bool get frozen => $_getBF(4); + @$pb.TagNumber(5) + set frozen($core.bool v) { $_setBool(4, v); } + @$pb.TagNumber(5) + $core.bool hasFrozen() => $_has(4); + @$pb.TagNumber(5) + void clearFrozen() => clearField(5); + + @$pb.TagNumber(6) + $core.bool get visibility => $_getBF(5); + @$pb.TagNumber(6) + set visibility($core.bool v) { $_setBool(5, v); } + @$pb.TagNumber(6) + $core.bool hasVisibility() => $_has(5); + @$pb.TagNumber(6) + void clearVisibility() => clearField(6); + + @$pb.TagNumber(7) + $core.int get width => $_getIZ(6); + @$pb.TagNumber(7) + set width($core.int v) { $_setSignedInt32(6, v); } + @$pb.TagNumber(7) + $core.bool hasWidth() => $_has(6); + @$pb.TagNumber(7) + void clearWidth() => clearField(7); + + @$pb.TagNumber(8) + AnyData get typeOptions => $_getN(7); + @$pb.TagNumber(8) + set typeOptions(AnyData v) { setField(8, v); } + @$pb.TagNumber(8) + $core.bool hasTypeOptions() => $_has(7); + @$pb.TagNumber(8) + void clearTypeOptions() => clearField(8); + @$pb.TagNumber(8) + AnyData ensureTypeOptions() => $_ensure(7); +} + class AnyData extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'AnyData', createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'typeId') diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/meta.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/meta.pbjson.dart index 5685a4c17b..c955a005fb 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/meta.pbjson.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/meta.pbjson.dart @@ -29,15 +29,15 @@ const GridMeta$json = const { '2': const [ const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'}, const {'1': 'fields', '3': 2, '4': 3, '5': 11, '6': '.Field', '10': 'fields'}, - const {'1': 'blocks', '3': 3, '4': 3, '5': 11, '6': '.Block', '10': 'blocks'}, + const {'1': 'blocks', '3': 3, '4': 3, '5': 11, '6': '.GridBlock', '10': 'blocks'}, ], }; /// Descriptor for `GridMeta`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List gridMetaDescriptor = $convert.base64Decode('CghHcmlkTWV0YRIXCgdncmlkX2lkGAEgASgJUgZncmlkSWQSHgoGZmllbGRzGAIgAygLMgYuRmllbGRSBmZpZWxkcxIeCgZibG9ja3MYAyADKAsyBi5CbG9ja1IGYmxvY2tz'); -@$core.Deprecated('Use blockDescriptor instead') -const Block$json = const { - '1': 'Block', +final $typed_data.Uint8List gridMetaDescriptor = $convert.base64Decode('CghHcmlkTWV0YRIXCgdncmlkX2lkGAEgASgJUgZncmlkSWQSHgoGZmllbGRzGAIgAygLMgYuRmllbGRSBmZpZWxkcxIiCgZibG9ja3MYAyADKAsyCi5HcmlkQmxvY2tSBmJsb2Nrcw=='); +@$core.Deprecated('Use gridBlockDescriptor instead') +const GridBlock$json = const { + '1': 'GridBlock', '2': const [ const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, const {'1': 'start_row_index', '3': 2, '4': 1, '5': 5, '10': 'startRowIndex'}, @@ -45,19 +45,19 @@ const Block$json = const { ], }; -/// Descriptor for `Block`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List blockDescriptor = $convert.base64Decode('CgVCbG9jaxIOCgJpZBgBIAEoCVICaWQSJgoPc3RhcnRfcm93X2luZGV4GAIgASgFUg1zdGFydFJvd0luZGV4EhsKCXJvd19jb3VudBgDIAEoBVIIcm93Q291bnQ='); -@$core.Deprecated('Use blockMetaDescriptor instead') -const BlockMeta$json = const { - '1': 'BlockMeta', +/// Descriptor for `GridBlock`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List gridBlockDescriptor = $convert.base64Decode('CglHcmlkQmxvY2sSDgoCaWQYASABKAlSAmlkEiYKD3N0YXJ0X3Jvd19pbmRleBgCIAEoBVINc3RhcnRSb3dJbmRleBIbCglyb3dfY291bnQYAyABKAVSCHJvd0NvdW50'); +@$core.Deprecated('Use gridBlockMetaDescriptor instead') +const GridBlockMeta$json = const { + '1': 'GridBlockMeta', '2': const [ const {'1': 'block_id', '3': 1, '4': 1, '5': 9, '10': 'blockId'}, const {'1': 'rows', '3': 2, '4': 3, '5': 11, '6': '.RowMeta', '10': 'rows'}, ], }; -/// Descriptor for `BlockMeta`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List blockMetaDescriptor = $convert.base64Decode('CglCbG9ja01ldGESGQoIYmxvY2tfaWQYASABKAlSB2Jsb2NrSWQSHAoEcm93cxgCIAMoCzIILlJvd01ldGFSBHJvd3M='); +/// Descriptor for `GridBlockMeta`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List gridBlockMetaDescriptor = $convert.base64Decode('Cg1HcmlkQmxvY2tNZXRhEhkKCGJsb2NrX2lkGAEgASgJUgdibG9ja0lkEhwKBHJvd3MYAiADKAsyCC5Sb3dNZXRhUgRyb3dz'); @$core.Deprecated('Use fieldDescriptor instead') const Field$json = const { '1': 'Field', @@ -85,6 +85,32 @@ const RepeatedField$json = const { /// Descriptor for `RepeatedField`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List repeatedFieldDescriptor = $convert.base64Decode('Cg1SZXBlYXRlZEZpZWxkEhwKBWl0ZW1zGAEgAygLMgYuRmllbGRSBWl0ZW1z'); +@$core.Deprecated('Use fieldChangesetDescriptor instead') +const FieldChangeset$json = const { + '1': 'FieldChangeset', + '2': const [ + const {'1': 'field_id', '3': 1, '4': 1, '5': 9, '10': 'fieldId'}, + const {'1': 'name', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'name'}, + const {'1': 'desc', '3': 3, '4': 1, '5': 9, '9': 1, '10': 'desc'}, + const {'1': 'field_type', '3': 4, '4': 1, '5': 14, '6': '.FieldType', '9': 2, '10': 'fieldType'}, + const {'1': 'frozen', '3': 5, '4': 1, '5': 8, '9': 3, '10': 'frozen'}, + const {'1': 'visibility', '3': 6, '4': 1, '5': 8, '9': 4, '10': 'visibility'}, + const {'1': 'width', '3': 7, '4': 1, '5': 5, '9': 5, '10': 'width'}, + const {'1': 'type_options', '3': 8, '4': 1, '5': 11, '6': '.AnyData', '9': 6, '10': 'typeOptions'}, + ], + '8': const [ + const {'1': 'one_of_name'}, + const {'1': 'one_of_desc'}, + const {'1': 'one_of_field_type'}, + const {'1': 'one_of_frozen'}, + const {'1': 'one_of_visibility'}, + const {'1': 'one_of_width'}, + const {'1': 'one_of_type_options'}, + ], +}; + +/// Descriptor for `FieldChangeset`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List fieldChangesetDescriptor = $convert.base64Decode('Cg5GaWVsZENoYW5nZXNldBIZCghmaWVsZF9pZBgBIAEoCVIHZmllbGRJZBIUCgRuYW1lGAIgASgJSABSBG5hbWUSFAoEZGVzYxgDIAEoCUgBUgRkZXNjEisKCmZpZWxkX3R5cGUYBCABKA4yCi5GaWVsZFR5cGVIAlIJZmllbGRUeXBlEhgKBmZyb3plbhgFIAEoCEgDUgZmcm96ZW4SIAoKdmlzaWJpbGl0eRgGIAEoCEgEUgp2aXNpYmlsaXR5EhYKBXdpZHRoGAcgASgFSAVSBXdpZHRoEi0KDHR5cGVfb3B0aW9ucxgIIAEoCzIILkFueURhdGFIBlILdHlwZU9wdGlvbnNCDQoLb25lX29mX25hbWVCDQoLb25lX29mX2Rlc2NCEwoRb25lX29mX2ZpZWxkX3R5cGVCDwoNb25lX29mX2Zyb3plbkITChFvbmVfb2ZfdmlzaWJpbGl0eUIOCgxvbmVfb2Zfd2lkdGhCFQoTb25lX29mX3R5cGVfb3B0aW9ucw=='); @$core.Deprecated('Use anyDataDescriptor instead') const AnyData$json = const { '1': 'AnyData', diff --git a/frontend/rust-lib/dart-ffi/Cargo.toml b/frontend/rust-lib/dart-ffi/Cargo.toml index 96755db623..046fd85668 100644 --- a/frontend/rust-lib/dart-ffi/Cargo.toml +++ b/frontend/rust-lib/dart-ffi/Cargo.toml @@ -7,8 +7,8 @@ edition = "2018" [lib] name = "dart_ffi" # this value will change depending on the target os -# default staticlib -crate-type = ["staticlib"] +# default cdylib +crate-type = ["cdylib"] [dependencies] diff --git a/frontend/rust-lib/flowy-block/src/editor.rs b/frontend/rust-lib/flowy-block/src/editor.rs index d7977c19db..fa3c1c757c 100644 --- a/frontend/rust-lib/flowy-block/src/editor.rs +++ b/frontend/rust-lib/flowy-block/src/editor.rs @@ -1,4 +1,4 @@ -use crate::queue::BlockRevisionCompact; +use crate::queue::TextBlockRevisionCompactor; use crate::web_socket::{make_block_ws_manager, EditorCommandSender}; use crate::{ errors::FlowyError, @@ -40,9 +40,7 @@ impl ClientTextBlockEditor { rev_web_socket: Arc, cloud_service: Arc, ) -> FlowyResult> { - let document_info = rev_manager - .load::(cloud_service) - .await?; + let document_info = rev_manager.load::(cloud_service).await?; let delta = document_info.delta()?; let rev_manager = Arc::new(rev_manager); let doc_id = doc_id.to_string(); diff --git a/frontend/rust-lib/flowy-block/src/manager.rs b/frontend/rust-lib/flowy-block/src/manager.rs index 091ca31bec..a08dc9b7b1 100644 --- a/frontend/rust-lib/flowy-block/src/manager.rs +++ b/frontend/rust-lib/flowy-block/src/manager.rs @@ -8,6 +8,7 @@ use flowy_collaboration::entities::{ }; use flowy_database::ConnectionPool; use flowy_error::FlowyResult; +use flowy_sync::disk::SQLiteTextBlockRevisionPersistence; use flowy_sync::{RevisionCloudService, RevisionManager, RevisionPersistence, RevisionWebSocket}; use lib_infra::future::FutureResult; use std::{convert::TryInto, sync::Arc}; @@ -84,7 +85,7 @@ impl TextBlockManager { let doc_id = doc_id.as_ref().to_owned(); let db_pool = self.user.db_pool()?; // Maybe we could save the block to disk without creating the RevisionManager - let rev_manager = self.make_block_rev_manager(&doc_id, db_pool)?; + let rev_manager = self.make_rev_manager(&doc_id, db_pool)?; let _ = rev_manager.reset_object(revisions).await?; Ok(()) } @@ -111,20 +112,20 @@ impl TextBlockManager { match self.editor_map.get(block_id) { None => { let db_pool = self.user.db_pool()?; - self.make_block_editor(block_id, db_pool).await + self.make_text_block_editor(block_id, db_pool).await } Some(editor) => Ok(editor), } } - async fn make_block_editor( + async fn make_text_block_editor( &self, block_id: &str, pool: Arc, ) -> Result, FlowyError> { let user = self.user.clone(); let token = self.user.token()?; - let rev_manager = self.make_block_rev_manager(block_id, pool.clone())?; + let rev_manager = self.make_rev_manager(block_id, pool.clone())?; let cloud_service = Arc::new(TextBlockRevisionCloudService { token, server: self.cloud_service.clone(), @@ -135,9 +136,10 @@ impl TextBlockManager { Ok(doc_editor) } - fn make_block_rev_manager(&self, doc_id: &str, pool: Arc) -> Result { + fn make_rev_manager(&self, doc_id: &str, pool: Arc) -> Result { let user_id = self.user.user_id()?; - let rev_persistence = Arc::new(RevisionPersistence::new(&user_id, doc_id, pool)); + let disk_cache = Arc::new(SQLiteTextBlockRevisionPersistence::new(&user_id, pool)); + let rev_persistence = Arc::new(RevisionPersistence::new(&user_id, doc_id, disk_cache)); Ok(RevisionManager::new(&user_id, doc_id, rev_persistence)) } } diff --git a/frontend/rust-lib/flowy-block/src/queue.rs b/frontend/rust-lib/flowy-block/src/queue.rs index aaaca7dbb4..c7683e9f0f 100644 --- a/frontend/rust-lib/flowy-block/src/queue.rs +++ b/frontend/rust-lib/flowy-block/src/queue.rs @@ -1,6 +1,7 @@ use crate::web_socket::EditorCommandReceiver; use crate::TextBlockUser; use async_stream::stream; +use bytes::Bytes; use flowy_collaboration::util::make_delta_from_revisions; use flowy_collaboration::{ client_document::{history::UndoResult, ClientDocument}, @@ -8,8 +9,9 @@ use flowy_collaboration::{ errors::CollaborateError, }; use flowy_error::{FlowyError, FlowyResult}; -use flowy_sync::{DeltaMD5, RevisionCompact, RevisionManager, RichTextTransformDeltas, TransformDeltas}; +use flowy_sync::{DeltaMD5, RevisionCompactor, RevisionManager, RichTextTransformDeltas, TransformDeltas}; use futures::stream::StreamExt; +use lib_ot::core::{Attributes, Delta}; use lib_ot::{ core::{Interval, OperationTransformable}, rich_text::{RichTextAttribute, RichTextAttributes, RichTextDelta}, @@ -187,31 +189,17 @@ impl EditBlockQueue { ); let _ = self .rev_manager - .add_local_revision::(&revision) + .add_local_revision(&revision, Box::new(TextBlockRevisionCompactor())) .await?; Ok(rev_id.into()) } } -pub(crate) struct BlockRevisionCompact(); -impl RevisionCompact for BlockRevisionCompact { - fn compact_revisions(user_id: &str, object_id: &str, mut revisions: Vec) -> FlowyResult { - if revisions.is_empty() { - return Err(FlowyError::internal().context("Can't compact the empty block'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(); +pub(crate) struct TextBlockRevisionCompactor(); +impl RevisionCompactor for TextBlockRevisionCompactor { + fn bytes_from_revisions(&self, revisions: Vec) -> FlowyResult { let delta = make_delta_from_revisions::(revisions)?; - let delta_data = delta.to_bytes(); - Ok(Revision::new(object_id, base_rev_id, rev_id, delta_data, user_id, md5)) + Ok(delta.to_bytes()) } } diff --git a/frontend/rust-lib/flowy-database/migrations/2022-03-04-101530_flowy-grid/down.sql b/frontend/rust-lib/flowy-database/migrations/2022-03-04-101530_flowy-grid/down.sql index 1447085d7f..96473ec177 100644 --- a/frontend/rust-lib/flowy-database/migrations/2022-03-04-101530_flowy-grid/down.sql +++ b/frontend/rust-lib/flowy-database/migrations/2022-03-04-101530_flowy-grid/down.sql @@ -1,3 +1,2 @@ -- This file should undo anything in `up.sql` -DROP TABLE kv_table; -DROP TABLE grid_rev_table; \ No newline at end of file +DROP TABLE kv_table; \ No newline at end of file diff --git a/frontend/rust-lib/flowy-database/migrations/2022-03-04-101530_flowy-grid/up.sql b/frontend/rust-lib/flowy-database/migrations/2022-03-04-101530_flowy-grid/up.sql index c8b592109b..edde5d7dfa 100644 --- a/frontend/rust-lib/flowy-database/migrations/2022-03-04-101530_flowy-grid/up.sql +++ b/frontend/rust-lib/flowy-database/migrations/2022-03-04-101530_flowy-grid/up.sql @@ -2,13 +2,4 @@ CREATE TABLE kv_table ( key TEXT NOT NULL PRIMARY KEY, value BLOB NOT NULL DEFAULT (x'') -); - -CREATE TABLE grid_rev_table ( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - object_id TEXT NOT NULL DEFAULT '', - base_rev_id BIGINT NOT NULL DEFAULT 0, - rev_id BIGINT NOT NULL DEFAULT 0, - data BLOB NOT NULL DEFAULT (x''), - state INTEGER NOT NULL DEFAULT 0 ); \ No newline at end of file diff --git a/frontend/rust-lib/flowy-database/migrations/2022-03-11-025536_flowy-grid/down.sql b/frontend/rust-lib/flowy-database/migrations/2022-03-11-025536_flowy-grid/down.sql new file mode 100644 index 0000000000..9d48f0fb8a --- /dev/null +++ b/frontend/rust-lib/flowy-database/migrations/2022-03-11-025536_flowy-grid/down.sql @@ -0,0 +1,3 @@ +-- This file should undo anything in `up.sql` +DROP TABLE grid_rev_table; +DROP TABLE grid_meta_rev_table; \ No newline at end of file diff --git a/frontend/rust-lib/flowy-database/migrations/2022-03-11-025536_flowy-grid/up.sql b/frontend/rust-lib/flowy-database/migrations/2022-03-11-025536_flowy-grid/up.sql new file mode 100644 index 0000000000..4ce278ec8f --- /dev/null +++ b/frontend/rust-lib/flowy-database/migrations/2022-03-11-025536_flowy-grid/up.sql @@ -0,0 +1,18 @@ +-- Your SQL goes here +CREATE TABLE grid_rev_table ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + object_id TEXT NOT NULL DEFAULT '', + base_rev_id BIGINT NOT NULL DEFAULT 0, + rev_id BIGINT NOT NULL DEFAULT 0, + data BLOB NOT NULL DEFAULT (x''), + state INTEGER NOT NULL DEFAULT 0 +); + +CREATE TABLE grid_meta_rev_table ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + object_id TEXT NOT NULL DEFAULT '', + base_rev_id BIGINT NOT NULL DEFAULT 0, + rev_id BIGINT NOT NULL DEFAULT 0, + data BLOB NOT NULL DEFAULT (x''), + state INTEGER NOT NULL DEFAULT 0 +); \ No newline at end of file diff --git a/frontend/rust-lib/flowy-database/src/macros.rs b/frontend/rust-lib/flowy-database/src/macros.rs index 9ef9740fee..e1534bf25f 100644 --- a/frontend/rust-lib/flowy-database/src/macros.rs +++ b/frontend/rust-lib/flowy-database/src/macros.rs @@ -160,3 +160,39 @@ macro_rules! impl_sql_integer_expression { } }; } + +#[macro_export] +macro_rules! impl_rev_state_map { + ($target:ident) => { + impl std::convert::From for $target { + fn from(value: i32) -> Self { + match value { + 0 => $target::Sync, + 1 => $target::Ack, + o => { + tracing::error!("Unsupported rev state {}, fallback to RevState::Local", o); + $target::Sync + } + } + } + } + + impl std::convert::From<$target> for RevisionState { + fn from(s: $target) -> Self { + match s { + $target::Sync => RevisionState::Sync, + $target::Ack => RevisionState::Ack, + } + } + } + + impl std::convert::From for $target { + fn from(s: RevisionState) -> Self { + match s { + RevisionState::Sync => $target::Sync, + RevisionState::Ack => $target::Ack, + } + } + } + }; +} diff --git a/frontend/rust-lib/flowy-database/src/schema.rs b/frontend/rust-lib/flowy-database/src/schema.rs index 7397f5b704..6be116e806 100644 --- a/frontend/rust-lib/flowy-database/src/schema.rs +++ b/frontend/rust-lib/flowy-database/src/schema.rs @@ -21,6 +21,17 @@ table! { } } +table! { + grid_meta_rev_table (id) { + id -> Integer, + object_id -> Text, + base_rev_id -> BigInt, + rev_id -> BigInt, + data -> Binary, + state -> Integer, + } +} + table! { grid_rev_table (id) { id -> Integer, @@ -102,6 +113,7 @@ table! { allow_tables_to_appear_in_same_query!( app_table, doc_table, + grid_meta_rev_table, grid_rev_table, kv_table, rev_table, diff --git a/frontend/rust-lib/flowy-folder/src/manager.rs b/frontend/rust-lib/flowy-folder/src/manager.rs index b41539d628..3ccb00466e 100644 --- a/frontend/rust-lib/flowy-folder/src/manager.rs +++ b/frontend/rust-lib/flowy-folder/src/manager.rs @@ -17,7 +17,8 @@ use flowy_collaboration::{client_folder::FolderPad, entities::ws_data::ServerRev use flowy_error::FlowyError; use flowy_folder_data_model::entities::view::ViewDataType; use flowy_folder_data_model::user_default; -use flowy_sync::RevisionWebSocket; +use flowy_sync::disk::SQLiteTextBlockRevisionPersistence; +use flowy_sync::{RevisionManager, RevisionPersistence, RevisionWebSocket}; use lazy_static::lazy_static; use lib_infra::future::FutureResult; use std::{collections::HashMap, convert::TryInto, fmt::Formatter, sync::Arc}; @@ -163,7 +164,12 @@ impl FolderManager { let _ = self.persistence.initialize(user_id, &folder_id).await?; let pool = self.persistence.db_pool()?; - let folder_editor = ClientFolderEditor::new(user_id, &folder_id, token, pool, self.web_socket.clone()).await?; + let disk_cache = Arc::new(SQLiteTextBlockRevisionPersistence::new(&user_id, pool)); + let rev_persistence = Arc::new(RevisionPersistence::new(user_id, folder_id.as_ref(), disk_cache)); + let rev_manager = RevisionManager::new(user_id, folder_id.as_ref(), rev_persistence); + + let folder_editor = + ClientFolderEditor::new(user_id, &folder_id, token, rev_manager, self.web_socket.clone()).await?; *self.folder_editor.write().await = Some(Arc::new(folder_editor)); let _ = self.app_controller.initialize()?; diff --git a/frontend/rust-lib/flowy-folder/src/services/folder_editor.rs b/frontend/rust-lib/flowy-folder/src/services/folder_editor.rs index 536eef4c9f..203c7c4cec 100644 --- a/frontend/rust-lib/flowy-folder/src/services/folder_editor.rs +++ b/frontend/rust-lib/flowy-folder/src/services/folder_editor.rs @@ -5,14 +5,16 @@ use flowy_collaboration::{ }; use crate::manager::FolderId; +use bytes::Bytes; use flowy_collaboration::util::make_delta_from_revisions; use flowy_error::{FlowyError, FlowyResult}; +use flowy_sync::disk::RevisionDiskCache; use flowy_sync::{ - RevisionCloudService, RevisionCompact, RevisionManager, RevisionObjectBuilder, RevisionPersistence, + RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder, RevisionPersistence, RevisionWebSocket, RevisionWebSocketManager, }; use lib_infra::future::FutureResult; -use lib_ot::core::PlainTextAttributes; +use lib_ot::core::{Delta, PlainTextAttributes}; use lib_sqlite::ConnectionPool; use parking_lot::RwLock; use std::sync::Arc; @@ -30,19 +32,13 @@ impl ClientFolderEditor { user_id: &str, folder_id: &FolderId, token: &str, - pool: Arc, + mut rev_manager: RevisionManager, web_socket: Arc, ) -> FlowyResult { - let rev_persistence = Arc::new(RevisionPersistence::new(user_id, folder_id.as_ref(), pool)); - let mut rev_manager = RevisionManager::new(user_id, folder_id.as_ref(), rev_persistence); let cloud = Arc::new(FolderRevisionCloudService { token: token.to_string(), }); - let folder = Arc::new(RwLock::new( - rev_manager - .load::(cloud) - .await?, - )); + let folder = Arc::new(RwLock::new(rev_manager.load::(cloud).await?)); let rev_manager = Arc::new(rev_manager); let ws_manager = make_folder_ws_manager( user_id, @@ -86,7 +82,7 @@ impl ClientFolderEditor { ); let _ = futures::executor::block_on(async { self.rev_manager - .add_local_revision::(&revision) + .add_local_revision(&revision, Box::new(FolderRevisionCompactor())) .await })?; Ok(()) @@ -128,24 +124,10 @@ impl ClientFolderEditor { } } -struct FolderRevisionCompact(); -impl RevisionCompact for FolderRevisionCompact { - fn compact_revisions(user_id: &str, object_id: &str, mut revisions: Vec) -> FlowyResult { - 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(); +struct FolderRevisionCompactor(); +impl RevisionCompactor for FolderRevisionCompactor { + fn bytes_from_revisions(&self, revisions: Vec) -> FlowyResult { let delta = make_delta_from_revisions::(revisions)?; - let delta_data = delta.to_bytes(); - Ok(Revision::new(object_id, base_rev_id, rev_id, delta_data, user_id, md5)) + Ok(delta.to_bytes()) } } diff --git a/frontend/rust-lib/flowy-folder/src/services/persistence/migration.rs b/frontend/rust-lib/flowy-folder/src/services/persistence/migration.rs index 3f499fc45f..20e6d71c66 100644 --- a/frontend/rust-lib/flowy-folder/src/services/persistence/migration.rs +++ b/frontend/rust-lib/flowy-folder/src/services/persistence/migration.rs @@ -11,6 +11,7 @@ use flowy_folder_data_model::entities::{ view::{RepeatedView, View}, workspace::Workspace, }; +use flowy_sync::disk::SQLiteTextBlockRevisionPersistence; use flowy_sync::{RevisionLoader, RevisionPersistence}; use std::sync::Arc; @@ -87,7 +88,8 @@ impl FolderMigration { return Ok(None); } let pool = self.database.db_pool()?; - let rev_persistence = Arc::new(RevisionPersistence::new(user_id, folder_id.as_ref(), pool.clone())); + let disk_cache = Arc::new(SQLiteTextBlockRevisionPersistence::new(&user_id, pool)); + let rev_persistence = Arc::new(RevisionPersistence::new(user_id, folder_id.as_ref(), disk_cache)); let (revisions, _) = RevisionLoader { object_id: folder_id.as_ref().to_owned(), user_id: self.user_id.clone(), diff --git a/frontend/rust-lib/flowy-grid/src/manager.rs b/frontend/rust-lib/flowy-grid/src/manager.rs index 83c12b2b78..6c5aa8236a 100644 --- a/frontend/rust-lib/flowy-grid/src/manager.rs +++ b/frontend/rust-lib/flowy-grid/src/manager.rs @@ -7,6 +7,7 @@ use flowy_error::{FlowyError, FlowyResult}; use flowy_grid_data_model::entities::{Field, RowMeta}; use flowy_sync::{RevisionManager, RevisionPersistence, RevisionWebSocket}; +use flowy_sync::disk::SQLiteGridRevisionPersistence; use lib_sqlite::ConnectionPool; use parking_lot::RwLock; use std::sync::Arc; @@ -104,7 +105,9 @@ impl GridManager { fn make_grid_rev_manager(&self, grid_id: &str, pool: Arc) -> FlowyResult { let user_id = self.grid_user.user_id()?; - let rev_persistence = Arc::new(RevisionPersistence::new(&user_id, grid_id, pool)); + + let disk_cache = Arc::new(SQLiteGridRevisionPersistence::new(&user_id, pool)); + let rev_persistence = Arc::new(RevisionPersistence::new(&user_id, grid_id, disk_cache)); let rev_manager = RevisionManager::new(&user_id, grid_id, rev_persistence); Ok(rev_manager) } diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs index 9bc7b6da05..4fb93c16b2 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -2,6 +2,8 @@ use crate::manager::GridUser; use crate::services::kv_persistence::{GridKVPersistence, KVTransaction}; use crate::services::stringify::stringify_deserialize; +use crate::services::grid_meta_editor::ClientGridBlockMetaEditor; +use bytes::Bytes; use dashmap::DashMap; use flowy_collaboration::client_grid::{GridChange, GridMetaPad}; use flowy_collaboration::entities::revision::Revision; @@ -10,10 +12,10 @@ use flowy_error::{FlowyError, FlowyResult}; use flowy_grid_data_model::entities::{ Cell, CellMeta, Field, Grid, RepeatedField, RepeatedFieldOrder, RepeatedRow, RepeatedRowOrder, Row, RowMeta, }; -use flowy_sync::{RevisionCloudService, RevisionCompact, RevisionManager, RevisionObjectBuilder}; +use flowy_sync::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder}; use lib_infra::future::FutureResult; use lib_infra::uuid; -use lib_ot::core::PlainTextAttributes; +use lib_ot::core::{Delta, PlainTextAttributes}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use std::collections::HashMap; use std::sync::Arc; @@ -24,10 +26,8 @@ pub struct ClientGridEditor { user: Arc, grid_meta_pad: Arc>, rev_manager: Arc, + block_meta_manager: Arc, kv_persistence: Arc, - - field_map: DashMap, - cell_map: DashMap, } impl ClientGridEditor { @@ -39,50 +39,22 @@ impl ClientGridEditor { ) -> FlowyResult> { let token = user.token()?; let cloud = Arc::new(GridRevisionCloudService { token }); - let grid_pad = rev_manager.load::(cloud).await?; + let grid_pad = rev_manager.load::(cloud).await?; let rev_manager = Arc::new(rev_manager); - let field_map = load_all_fields(&grid_pad, &kv_persistence).await?; let grid_meta_pad = Arc::new(RwLock::new(grid_pad)); - let cell_map = DashMap::new(); + let block_meta_manager = Arc::new(GridBlockMetaEditorManager::new()); Ok(Arc::new(Self { grid_id: grid_id.to_owned(), user, grid_meta_pad, rev_manager, + block_meta_manager, kv_persistence, - field_map, - cell_map, })) } - pub async fn create_empty_row(&self) -> FlowyResult<()> { - let row = RowMeta::new(&uuid(), &self.grid_id, vec![]); - self.create_row(row).await?; - Ok(()) - } - - async fn create_row(&self, row: RowMeta) -> FlowyResult<()> { - let _ = self.modify(|grid| Ok(grid.create_row(row)?)).await?; - // self.cell_map.insert(row.id.clone(), row.clone()); - // let _ = self.kv_persistence.set(row)?; - Ok(()) - } - - pub async fn delete_rows(&self, ids: Vec) -> FlowyResult<()> { - let _ = self.modify(|grid| Ok(grid.delete_rows(&ids)?)).await?; - // let _ = self.kv.batch_delete(ids)?; - Ok(()) - } - - // pub async fn update_row(&self, cell: Cell) -> FlowyResult<()> { - // match self.cell_map.get(&cell.id) { - // None => Err(FlowyError::internal().context(format!("Can't find cell with id: {}", cell.id))), - // Some(raw_cell) => {} - // } - // } - pub async fn create_field(&mut self, field: Field) -> FlowyResult<()> { let _ = self.modify(|grid| Ok(grid.create_field(field)?)).await?; Ok(()) @@ -90,76 +62,38 @@ impl ClientGridEditor { pub async fn delete_field(&mut self, field_id: &str) -> FlowyResult<()> { let _ = self.modify(|grid| Ok(grid.delete_field(field_id)?)).await?; - // let _ = self.kv.remove(field_id)?; Ok(()) } - pub async fn get_rows(&self, row_orders: RepeatedRowOrder) -> FlowyResult { - let ids = row_orders - .items - .into_iter() - .map(|row_order| row_order.row_id) - .collect::>(); - let row_metas: Vec = self.kv_persistence.batch_get(ids)?; - - let make_cell = |field_id: String, raw_cell: CellMeta| { - let some_field = self.field_map.get(&field_id); - if some_field.is_none() { - tracing::error!("Can't find the field with {}", field_id); - return None; - } - self.cell_map.insert(raw_cell.id.clone(), raw_cell.clone()); - - let field = some_field.unwrap(); - match stringify_deserialize(raw_cell.data, field.value()) { - Ok(content) => { - let cell = Cell { - id: raw_cell.id, - field_id: field_id.clone(), - content, - }; - Some((field_id, cell)) - } - Err(_) => None, - } - }; - - let rows = row_metas - .into_par_iter() - .map(|row_meta| { - let mut row = Row { - id: row_meta.id.clone(), - cell_by_field_id: Default::default(), - height: row_meta.height, - }; - row.cell_by_field_id = row_meta - .cell_by_field_id - .into_par_iter() - .flat_map(|(field_id, raw_cell)| make_cell(field_id, raw_cell)) - .collect::>(); - row - }) - .collect::>(); - - Ok(rows.into()) + pub async fn create_empty_row(&self) -> FlowyResult<()> { + // let _ = self.modify(|grid| { + // + // + // grid.blocks + // + // }).await?; + todo!() } - pub async fn get_fields(&self, field_orders: RepeatedFieldOrder) -> FlowyResult { - let fields = field_orders - .iter() - .flat_map(|field_order| match self.field_map.get(&field_order.field_id) { - None => { - tracing::error!("Can't find the field with {}", field_order.field_id); - None - } - Some(field) => Some(field.value().clone()), - }) - .collect::>(); - Ok(fields.into()) + async fn create_row(&self, row: RowMeta) -> FlowyResult<()> { + todo!() + } + + pub async fn get_rows(&self, row_orders: RepeatedRowOrder) -> FlowyResult { + todo!() + } + + pub async fn delete_rows(&self, ids: Vec) -> FlowyResult<()> { + todo!() } pub async fn grid_data(&self) -> Grid { - self.grid_meta_pad.read().await.grid_data() + todo!() + } + + pub async fn get_fields(&self, field_orders: RepeatedFieldOrder) -> FlowyResult { + let fields = self.grid_meta_pad.read().await.get_fields(field_orders)?; + Ok(fields) } pub async fn delta_str(&self) -> String { @@ -195,7 +129,7 @@ impl ClientGridEditor { ); let _ = self .rev_manager - .add_local_revision::(&revision) + .add_local_revision(&revision, Box::new(GridRevisionCompactor())) .await?; Ok(()) } @@ -241,24 +175,73 @@ impl RevisionCloudService for GridRevisionCloudService { } } -struct GridRevisionCompact(); -impl RevisionCompact for GridRevisionCompact { - fn compact_revisions(user_id: &str, object_id: &str, mut revisions: Vec) -> FlowyResult { - 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(); +struct GridRevisionCompactor(); +impl RevisionCompactor for GridRevisionCompactor { + fn bytes_from_revisions(&self, revisions: Vec) -> FlowyResult { let delta = make_delta_from_revisions::(revisions)?; - let delta_data = delta.to_bytes(); - Ok(Revision::new(object_id, base_rev_id, rev_id, delta_data, user_id, md5)) + Ok(delta.to_bytes()) + } +} + +struct GridBlockMetaEditorManager { + editor_map: DashMap>, +} + +impl GridBlockMetaEditorManager { + fn new() -> Self { + Self { + editor_map: DashMap::new(), + } + } + + pub async fn get_rows(&self, row_orders: RepeatedRowOrder) -> FlowyResult { + // let ids = row_orders + // .items + // .into_iter() + // .map(|row_order| row_order.row_id) + // .collect::>(); + // let row_metas: Vec = self.kv_persistence.batch_get(ids)?; + // + // let make_cell = |field_id: String, raw_cell: CellMeta| { + // let some_field = self.field_map.get(&field_id); + // if some_field.is_none() { + // tracing::error!("Can't find the field with {}", field_id); + // return None; + // } + // self.cell_map.insert(raw_cell.id.clone(), raw_cell.clone()); + // + // let field = some_field.unwrap(); + // match stringify_deserialize(raw_cell.data, field.value()) { + // Ok(content) => { + // let cell = Cell { + // id: raw_cell.id, + // field_id: field_id.clone(), + // content, + // }; + // Some((field_id, cell)) + // } + // Err(_) => None, + // } + // }; + // + // let rows = row_metas + // .into_par_iter() + // .map(|row_meta| { + // let mut row = Row { + // id: row_meta.id.clone(), + // cell_by_field_id: Default::default(), + // height: row_meta.height, + // }; + // row.cell_by_field_id = row_meta + // .cell_by_field_id + // .into_par_iter() + // .flat_map(|(field_id, raw_cell)| make_cell(field_id, raw_cell)) + // .collect::>(); + // row + // }) + // .collect::>(); + // + // Ok(rows.into()) + todo!() } } diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_meta_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_meta_editor.rs new file mode 100644 index 0000000000..3e3e5e82d3 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/grid_meta_editor.rs @@ -0,0 +1,125 @@ +use bytes::Bytes; +use flowy_collaboration::client_grid::{GridBlockMetaChange, GridBlockMetaPad}; +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::RowMeta; +use flowy_sync::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder}; +use lib_infra::future::FutureResult; +use lib_infra::uuid; +use lib_ot::core::PlainTextAttributes; +use std::sync::Arc; +use tokio::sync::RwLock; + +pub struct ClientGridBlockMetaEditor { + user_id: String, + block_id: String, + meta_pad: Arc>, + rev_manager: Arc, +} + +impl ClientGridBlockMetaEditor { + pub async fn new( + user_id: &str, + token: &str, + block_id: String, + mut rev_manager: RevisionManager, + ) -> FlowyResult { + let cloud = Arc::new(GridBlockMetaRevisionCloudService { + token: token.to_owned(), + }); + let block_meta_pad = rev_manager.load::(cloud).await?; + let meta_pad = Arc::new(RwLock::new(block_meta_pad)); + let rev_manager = Arc::new(rev_manager); + let user_id = user_id.to_owned(); + Ok(Self { + user_id, + block_id, + meta_pad, + rev_manager, + }) + } + + pub async fn create_empty_row(&self) -> FlowyResult<()> { + let row = RowMeta::new(&uuid(), &self.block_id, vec![]); + self.create_row(row).await?; + Ok(()) + } + + async fn create_row(&self, row: RowMeta) -> FlowyResult<()> { + // let _ = self.modify(|grid| Ok(grid.create_row(row)?)).await?; + // self.cell_map.insert(row.id.clone(), row.clone()); + // let _ = self.kv_persistence.set(row)?; + Ok(()) + } + + pub async fn delete_rows(&self, ids: Vec) -> FlowyResult<()> { + // let _ = self.modify(|grid| Ok(grid.delete_rows(&ids)?)).await?; + // let _ = self.kv.batch_delete(ids)?; + Ok(()) + } + + async fn modify(&self, f: F) -> FlowyResult<()> + where + F: for<'a> FnOnce(&'a mut GridBlockMetaPad) -> FlowyResult>, + { + let mut write_guard = self.meta_pad.write().await; + match f(&mut *write_guard)? { + None => {} + Some(change) => { + let _ = self.apply_change(change).await?; + } + } + Ok(()) + } + + async fn apply_change(&self, change: GridBlockMetaChange) -> FlowyResult<()> { + let GridBlockMetaChange { delta, md5 } = change; + let user_id = self.user_id.clone(); + 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, + &user_id, + md5, + ); + let _ = self + .rev_manager + .add_local_revision(&revision, Box::new(GridBlockMetaRevisionCompactor())) + .await?; + Ok(()) + } +} + +struct GridBlockMetaRevisionCloudService { + #[allow(dead_code)] + token: String, +} + +impl RevisionCloudService for GridBlockMetaRevisionCloudService { + #[tracing::instrument(level = "trace", skip(self))] + fn fetch_object(&self, _user_id: &str, _object_id: &str) -> FutureResult, FlowyError> { + FutureResult::new(async move { Ok(vec![]) }) + } +} + +struct GridBlockMetaPadBuilder(); +impl RevisionObjectBuilder for GridBlockMetaPadBuilder { + type Output = GridBlockMetaPad; + + fn build_object(object_id: &str, revisions: Vec) -> FlowyResult { + let pad = GridBlockMetaPad::from_revisions(object_id, revisions)?; + Ok(pad) + } +} + +struct GridBlockMetaRevisionCompactor(); +impl RevisionCompactor for GridBlockMetaRevisionCompactor { + fn bytes_from_revisions(&self, revisions: Vec) -> FlowyResult { + let delta = make_delta_from_revisions::(revisions)?; + Ok(delta.to_bytes()) + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/mod.rs b/frontend/rust-lib/flowy-grid/src/services/mod.rs index f1e1086589..226426aeb7 100644 --- a/frontend/rust-lib/flowy-grid/src/services/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/mod.rs @@ -3,5 +3,6 @@ mod util; pub mod cell_data; pub mod grid_builder; pub mod grid_editor; +pub mod grid_meta_editor; pub mod kv_persistence; pub mod stringify; diff --git a/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs b/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs index 5550395229..6fea7bbe73 100644 --- a/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs +++ b/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs @@ -66,10 +66,10 @@ fn make_view_data_processor( ) -> ViewDataProcessorMap { let mut map: HashMap> = HashMap::new(); - let block_data_impl = BlockManagerViewDataImpl(text_block_manager); + let block_data_impl = TextBlockViewDataProcessor(text_block_manager); map.insert(block_data_impl.data_type(), Arc::new(block_data_impl)); - let grid_data_impl = GridManagerViewDataImpl(grid_manager); + let grid_data_impl = GridViewDataProcessor(grid_manager); map.insert(grid_data_impl.data_type(), Arc::new(grid_data_impl)); Arc::new(map) @@ -133,8 +133,8 @@ impl WSMessageReceiver for FolderWSMessageReceiverImpl { } } -struct BlockManagerViewDataImpl(Arc); -impl ViewDataProcessor for BlockManagerViewDataImpl { +struct TextBlockViewDataProcessor(Arc); +impl ViewDataProcessor for TextBlockViewDataProcessor { fn initialize(&self) -> FutureResult<(), FlowyError> { let manager = self.0.clone(); FutureResult::new(async move { manager.init() }) @@ -186,8 +186,8 @@ impl ViewDataProcessor for BlockManagerViewDataImpl { } } -struct GridManagerViewDataImpl(Arc); -impl ViewDataProcessor for GridManagerViewDataImpl { +struct GridViewDataProcessor(Arc); +impl ViewDataProcessor for GridViewDataProcessor { fn initialize(&self) -> FutureResult<(), FlowyError> { FutureResult::new(async { Ok(()) }) } diff --git a/frontend/rust-lib/flowy-sync/src/cache/disk/grid_meta_rev_impl.rs b/frontend/rust-lib/flowy-sync/src/cache/disk/grid_meta_rev_impl.rs new file mode 100644 index 0000000000..ba4a0950af --- /dev/null +++ b/frontend/rust-lib/flowy-sync/src/cache/disk/grid_meta_rev_impl.rs @@ -0,0 +1,235 @@ +use crate::cache::disk::RevisionDiskCache; +use crate::disk::{RevisionChangeset, RevisionRecord, RevisionState}; +use crate::memory::RevisionMemoryCacheDelegate; +use bytes::Bytes; +use diesel::{sql_types::Integer, update, SqliteConnection}; +use flowy_collaboration::{ + entities::revision::{RevId, RevType, Revision, RevisionRange}, + util::md5, +}; +use flowy_database::{ + impl_sql_integer_expression, insert_or_ignore_into, + prelude::*, + schema::{grid_meta_rev_table, grid_meta_rev_table::dsl}, + ConnectionPool, +}; +use flowy_error::{internal_error, FlowyError, FlowyResult}; +use std::sync::Arc; + +pub struct SQLiteGridBlockMetaRevisionPersistence { + user_id: String, + pub(crate) pool: Arc, +} + +impl RevisionDiskCache for SQLiteGridBlockMetaRevisionPersistence { + type Error = FlowyError; + + fn create_revision_records(&self, revision_records: Vec) -> Result<(), Self::Error> { + let conn = self.pool.get().map_err(internal_error)?; + let _ = GridMetaRevisionSql::create(revision_records, &*conn)?; + Ok(()) + } + + fn read_revision_records( + &self, + object_id: &str, + rev_ids: Option>, + ) -> Result, Self::Error> { + let conn = self.pool.get().map_err(internal_error)?; + let records = GridMetaRevisionSql::read(&self.user_id, object_id, rev_ids, &*conn)?; + Ok(records) + } + + fn read_revision_records_with_range( + &self, + object_id: &str, + range: &RevisionRange, + ) -> Result, Self::Error> { + let conn = &*self.pool.get().map_err(internal_error)?; + let revisions = GridMetaRevisionSql::read_with_range(&self.user_id, object_id, range.clone(), conn)?; + Ok(revisions) + } + + fn update_revision_record(&self, changesets: Vec) -> FlowyResult<()> { + let conn = &*self.pool.get().map_err(internal_error)?; + let _ = conn.immediate_transaction::<_, FlowyError, _>(|| { + for changeset in changesets { + let _ = GridMetaRevisionSql::update(changeset, conn)?; + } + Ok(()) + })?; + Ok(()) + } + + fn delete_revision_records(&self, object_id: &str, rev_ids: Option>) -> Result<(), Self::Error> { + let conn = &*self.pool.get().map_err(internal_error)?; + let _ = GridMetaRevisionSql::delete(object_id, rev_ids, conn)?; + Ok(()) + } + + fn delete_and_insert_records( + &self, + object_id: &str, + deleted_rev_ids: Option>, + inserted_records: Vec, + ) -> Result<(), Self::Error> { + let conn = self.pool.get().map_err(internal_error)?; + conn.immediate_transaction::<_, FlowyError, _>(|| { + let _ = GridMetaRevisionSql::delete(object_id, deleted_rev_ids, &*conn)?; + let _ = GridMetaRevisionSql::create(inserted_records, &*conn)?; + Ok(()) + }) + } +} + +impl SQLiteGridBlockMetaRevisionPersistence { + pub fn new(user_id: &str, pool: Arc) -> Self { + Self { + user_id: user_id.to_owned(), + pool, + } + } +} + +struct GridMetaRevisionSql(); +impl GridMetaRevisionSql { + fn create(revision_records: Vec, conn: &SqliteConnection) -> Result<(), FlowyError> { + // Batch insert: https://diesel.rs/guides/all-about-inserts.html + + let records = revision_records + .into_iter() + .map(|record| { + tracing::trace!( + "[GridMetaRevisionSql] create revision: {}:{:?}", + record.revision.object_id, + record.revision.rev_id + ); + let rev_state: GridMetaRevisionState = record.state.into(); + ( + dsl::object_id.eq(record.revision.object_id), + dsl::base_rev_id.eq(record.revision.base_rev_id), + dsl::rev_id.eq(record.revision.rev_id), + dsl::data.eq(record.revision.delta_data), + dsl::state.eq(rev_state), + ) + }) + .collect::>(); + + let _ = insert_or_ignore_into(dsl::grid_meta_rev_table) + .values(&records) + .execute(conn)?; + Ok(()) + } + + fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> { + let state: GridMetaRevisionState = changeset.state.clone().into(); + let filter = dsl::grid_meta_rev_table + .filter(dsl::rev_id.eq(changeset.rev_id.as_ref())) + .filter(dsl::object_id.eq(changeset.object_id)); + let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?; + tracing::debug!( + "[GridMetaRevisionSql] update revision:{} state:to {:?}", + changeset.rev_id, + changeset.state + ); + Ok(()) + } + + fn read( + user_id: &str, + object_id: &str, + rev_ids: Option>, + conn: &SqliteConnection, + ) -> Result, FlowyError> { + let mut sql = dsl::grid_meta_rev_table + .filter(dsl::object_id.eq(object_id)) + .into_boxed(); + if let Some(rev_ids) = rev_ids { + sql = sql.filter(dsl::rev_id.eq_any(rev_ids)); + } + let rows = sql.order(dsl::rev_id.asc()).load::(conn)?; + let records = rows + .into_iter() + .map(|row| mk_revision_record_from_table(user_id, row)) + .collect::>(); + + Ok(records) + } + + fn read_with_range( + user_id: &str, + object_id: &str, + range: RevisionRange, + conn: &SqliteConnection, + ) -> Result, FlowyError> { + let rev_tables = dsl::grid_meta_rev_table + .filter(dsl::rev_id.ge(range.start)) + .filter(dsl::rev_id.le(range.end)) + .filter(dsl::object_id.eq(object_id)) + .order(dsl::rev_id.asc()) + .load::(conn)?; + + let revisions = rev_tables + .into_iter() + .map(|table| mk_revision_record_from_table(user_id, table)) + .collect::>(); + Ok(revisions) + } + + fn delete(object_id: &str, rev_ids: Option>, conn: &SqliteConnection) -> Result<(), FlowyError> { + let mut sql = diesel::delete(dsl::grid_meta_rev_table).into_boxed(); + sql = sql.filter(dsl::object_id.eq(object_id)); + + if let Some(rev_ids) = rev_ids { + tracing::trace!("[GridMetaRevisionSql] Delete revision: {}:{:?}", object_id, rev_ids); + sql = sql.filter(dsl::rev_id.eq_any(rev_ids)); + } + + let affected_row = sql.execute(conn)?; + tracing::trace!("[GridMetaRevisionSql] Delete {} rows", affected_row); + Ok(()) + } +} + +#[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)] +#[table_name = "grid_meta_rev_table"] +struct GridMetaRevisionTable { + id: i32, + object_id: String, + base_rev_id: i64, + rev_id: i64, + data: Vec, + state: GridMetaRevisionState, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, FromSqlRow, AsExpression)] +#[repr(i32)] +#[sql_type = "Integer"] +pub enum GridMetaRevisionState { + Sync = 0, + Ack = 1, +} +impl_sql_integer_expression!(GridMetaRevisionState); +impl_rev_state_map!(GridMetaRevisionState); +impl std::default::Default for GridMetaRevisionState { + fn default() -> Self { + GridMetaRevisionState::Sync + } +} + +fn mk_revision_record_from_table(user_id: &str, table: GridMetaRevisionTable) -> RevisionRecord { + let md5 = md5(&table.data); + let revision = Revision::new( + &table.object_id, + table.base_rev_id, + table.rev_id, + Bytes::from(table.data), + user_id, + md5, + ); + RevisionRecord { + revision, + state: table.state.into(), + write_to_disk: false, + } +} diff --git a/frontend/rust-lib/flowy-sync/src/cache/disk/grid_rev_impl.rs b/frontend/rust-lib/flowy-sync/src/cache/disk/grid_rev_impl.rs index 6053b17133..385bc812c7 100644 --- a/frontend/rust-lib/flowy-sync/src/cache/disk/grid_rev_impl.rs +++ b/frontend/rust-lib/flowy-sync/src/cache/disk/grid_rev_impl.rs @@ -1,5 +1,6 @@ use crate::cache::disk::RevisionDiskCache; use crate::disk::{RevisionChangeset, RevisionRecord, RevisionState}; +use crate::memory::RevisionMemoryCacheDelegate; use bytes::Bytes; use diesel::{sql_types::Integer, update, SqliteConnection}; use flowy_collaboration::{ @@ -23,12 +24,9 @@ pub struct SQLiteGridRevisionPersistence { impl RevisionDiskCache for SQLiteGridRevisionPersistence { type Error = FlowyError; - fn create_revision_records( - &self, - revision_records: Vec, - conn: &SqliteConnection, - ) -> Result<(), Self::Error> { - let _ = GridRevisionSql::create(revision_records, conn)?; + fn create_revision_records(&self, revision_records: Vec) -> Result<(), Self::Error> { + let conn = self.pool.get().map_err(internal_error)?; + let _ = GridRevisionSql::create(revision_records, &*conn)?; Ok(()) } @@ -78,14 +76,14 @@ impl RevisionDiskCache for SQLiteGridRevisionPersistence { let conn = self.pool.get().map_err(internal_error)?; conn.immediate_transaction::<_, FlowyError, _>(|| { let _ = GridRevisionSql::delete(object_id, deleted_rev_ids, &*conn)?; - let _ = self.create_revision_records(inserted_records, &*conn)?; + let _ = GridRevisionSql::create(inserted_records, &*conn)?; Ok(()) }) } } impl SQLiteGridRevisionPersistence { - pub(crate) fn new(user_id: &str, pool: Arc) -> Self { + pub fn new(user_id: &str, pool: Arc) -> Self { Self { user_id: user_id.to_owned(), pool, @@ -193,13 +191,13 @@ impl GridRevisionSql { #[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)] #[table_name = "grid_rev_table"] -pub(crate) struct GridRevisionTable { +struct GridRevisionTable { id: i32, - pub(crate) object_id: String, - pub(crate) base_rev_id: i64, - pub(crate) rev_id: i64, - pub(crate) data: Vec, - pub(crate) state: GridRevisionState, + object_id: String, + base_rev_id: i64, + rev_id: i64, + data: Vec, + state: GridRevisionState, } #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, FromSqlRow, AsExpression)] @@ -209,6 +207,8 @@ pub enum GridRevisionState { Sync = 0, Ack = 1, } +impl_sql_integer_expression!(GridRevisionState); +impl_rev_state_map!(GridRevisionState); impl std::default::Default for GridRevisionState { fn default() -> Self { @@ -216,44 +216,6 @@ impl std::default::Default for GridRevisionState { } } -impl std::convert::From for GridRevisionState { - fn from(value: i32) -> Self { - match value { - 0 => GridRevisionState::Sync, - 1 => GridRevisionState::Ack, - o => { - tracing::error!("Unsupported rev state {}, fallback to RevState::Local", o); - GridRevisionState::Sync - } - } - } -} - -impl GridRevisionState { - pub fn value(&self) -> i32 { - *self as i32 - } -} -impl_sql_integer_expression!(GridRevisionState); - -impl std::convert::From for RevisionState { - fn from(s: GridRevisionState) -> Self { - match s { - GridRevisionState::Sync => RevisionState::Sync, - GridRevisionState::Ack => RevisionState::Ack, - } - } -} - -impl std::convert::From for GridRevisionState { - fn from(s: RevisionState) -> Self { - match s { - RevisionState::Sync => GridRevisionState::Sync, - RevisionState::Ack => GridRevisionState::Ack, - } - } -} - fn mk_revision_record_from_table(user_id: &str, table: GridRevisionTable) -> RevisionRecord { let md5 = md5(&table.data); let revision = Revision::new( diff --git a/frontend/rust-lib/flowy-sync/src/cache/disk/mod.rs b/frontend/rust-lib/flowy-sync/src/cache/disk/mod.rs index 4fcad2d2ec..e261f8d7ec 100644 --- a/frontend/rust-lib/flowy-sync/src/cache/disk/mod.rs +++ b/frontend/rust-lib/flowy-sync/src/cache/disk/mod.rs @@ -1,11 +1,14 @@ mod folder_rev_impl; +mod grid_meta_rev_impl; mod grid_rev_impl; mod text_rev_impl; pub use folder_rev_impl::*; +pub use grid_meta_rev_impl::*; pub use grid_rev_impl::*; pub use text_rev_impl::*; +use crate::memory::RevisionMemoryCacheDelegate; use diesel::SqliteConnection; use flowy_collaboration::entities::revision::{RevId, Revision, RevisionRange}; use flowy_error::FlowyResult; @@ -13,11 +16,7 @@ use std::fmt::Debug; pub trait RevisionDiskCache: Sync + Send { type Error: Debug; - fn create_revision_records( - &self, - revision_records: Vec, - conn: &SqliteConnection, - ) -> Result<(), Self::Error>; + fn create_revision_records(&self, revision_records: Vec) -> Result<(), Self::Error>; // Read all the records if the rev_ids is None fn read_revision_records( diff --git a/frontend/rust-lib/flowy-sync/src/cache/disk/text_rev_impl.rs b/frontend/rust-lib/flowy-sync/src/cache/disk/text_rev_impl.rs index 7659ad33dc..0d780f824a 100644 --- a/frontend/rust-lib/flowy-sync/src/cache/disk/text_rev_impl.rs +++ b/frontend/rust-lib/flowy-sync/src/cache/disk/text_rev_impl.rs @@ -1,5 +1,6 @@ use crate::cache::disk::RevisionDiskCache; use crate::disk::{RevisionChangeset, RevisionRecord, RevisionState}; +use crate::memory::RevisionMemoryCacheDelegate; use bytes::Bytes; use diesel::{sql_types::Integer, update, SqliteConnection}; use flowy_collaboration::{ @@ -23,12 +24,9 @@ pub struct SQLiteTextBlockRevisionPersistence { impl RevisionDiskCache for SQLiteTextBlockRevisionPersistence { type Error = FlowyError; - fn create_revision_records( - &self, - revision_records: Vec, - conn: &SqliteConnection, - ) -> Result<(), Self::Error> { - let _ = TextRevisionSql::create(revision_records, conn)?; + fn create_revision_records(&self, revision_records: Vec) -> Result<(), Self::Error> { + let conn = self.pool.get().map_err(internal_error)?; + let _ = TextRevisionSql::create(revision_records, &*conn)?; Ok(()) } @@ -78,14 +76,14 @@ impl RevisionDiskCache for SQLiteTextBlockRevisionPersistence { let conn = self.pool.get().map_err(internal_error)?; conn.immediate_transaction::<_, FlowyError, _>(|| { let _ = TextRevisionSql::delete(object_id, deleted_rev_ids, &*conn)?; - let _ = self.create_revision_records(inserted_records, &*conn)?; + let _ = TextRevisionSql::create(inserted_records, &*conn)?; Ok(()) }) } } impl SQLiteTextBlockRevisionPersistence { - pub(crate) fn new(user_id: &str, pool: Arc) -> Self { + pub fn new(user_id: &str, pool: Arc) -> Self { Self { user_id: user_id.to_owned(), pool, @@ -210,6 +208,8 @@ enum TextRevisionState { Sync = 0, Ack = 1, } +impl_sql_integer_expression!(TextRevisionState); +impl_rev_state_map!(TextRevisionState); impl std::default::Default for TextRevisionState { fn default() -> Self { @@ -217,44 +217,6 @@ impl std::default::Default for TextRevisionState { } } -impl std::convert::From for TextRevisionState { - fn from(value: i32) -> Self { - match value { - 0 => TextRevisionState::Sync, - 1 => TextRevisionState::Ack, - o => { - tracing::error!("Unsupported rev state {}, fallback to RevState::Local", o); - TextRevisionState::Sync - } - } - } -} - -impl TextRevisionState { - pub fn value(&self) -> i32 { - *self as i32 - } -} -impl_sql_integer_expression!(TextRevisionState); - -impl std::convert::From for RevisionState { - fn from(s: TextRevisionState) -> Self { - match s { - TextRevisionState::Sync => RevisionState::Sync, - TextRevisionState::Ack => RevisionState::Ack, - } - } -} - -impl std::convert::From for TextRevisionState { - fn from(s: RevisionState) -> Self { - match s { - RevisionState::Sync => TextRevisionState::Sync, - RevisionState::Ack => TextRevisionState::Ack, - } - } -} - fn mk_revision_record_from_table(user_id: &str, table: RevisionTable) -> RevisionRecord { let md5 = md5(&table.data); let revision = Revision::new( @@ -279,6 +241,7 @@ pub enum RevTableType { Local = 0, Remote = 1, } +impl_sql_integer_expression!(RevTableType); impl std::default::Default for RevTableType { fn default() -> Self { @@ -298,12 +261,6 @@ impl std::convert::From for RevTableType { } } } -impl RevTableType { - pub fn value(&self) -> i32 { - *self as i32 - } -} -impl_sql_integer_expression!(RevTableType); impl std::convert::From for RevTableType { fn from(ty: RevType) -> Self { diff --git a/frontend/rust-lib/flowy-sync/src/rev_manager.rs b/frontend/rust-lib/flowy-sync/src/rev_manager.rs index ab5d7858b3..ae26bbf68c 100644 --- a/frontend/rust-lib/flowy-sync/src/rev_manager.rs +++ b/frontend/rust-lib/flowy-sync/src/rev_manager.rs @@ -1,11 +1,13 @@ use crate::disk::RevisionState; use crate::{RevisionPersistence, WSDataProviderDataSource}; +use bytes::Bytes; use flowy_collaboration::{ entities::revision::{RepeatedRevision, Revision, RevisionRange}, util::{pair_rev_id_from_revisions, RevIdCounter}, }; use flowy_error::{FlowyError, FlowyResult}; use lib_infra::future::FutureResult; +use lib_ot::core::{Attributes, Delta}; use std::sync::Arc; pub trait RevisionCloudService: Send + Sync { @@ -17,8 +19,26 @@ pub trait RevisionObjectBuilder: Send + Sync { fn build_object(object_id: &str, revisions: Vec) -> FlowyResult; } -pub trait RevisionCompact: Send + Sync { - fn compact_revisions(user_id: &str, object_id: &str, revisions: Vec) -> FlowyResult; +pub trait RevisionCompactor: Send + Sync { + fn compact(&self, user_id: &str, object_id: &str, mut revisions: Vec) -> FlowyResult { + 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_data = self.bytes_from_revisions(revisions)?; + Ok(Revision::new(object_id, base_rev_id, rev_id, delta_data, user_id, md5)) + } + + fn bytes_from_revisions(&self, revisions: Vec) -> FlowyResult; } pub struct RevisionManager { @@ -48,10 +68,9 @@ impl RevisionManager { } } - pub async fn load(&mut self, cloud: Arc) -> FlowyResult + pub async fn load(&mut self, cloud: Arc) -> FlowyResult where B: RevisionObjectBuilder, - C: RevisionCompact, { let (revisions, rev_id) = RevisionLoader { object_id: self.object_id.clone(), @@ -84,15 +103,16 @@ impl RevisionManager { Ok(()) } - #[tracing::instrument(level = "debug", skip(self, revision))] - pub async fn add_local_revision(&self, revision: &Revision) -> Result<(), FlowyError> - where - C: RevisionCompact, - { + #[tracing::instrument(level = "debug", skip_all, err)] + pub async fn add_local_revision<'a>( + &'a self, + revision: &Revision, + compactor: Box, + ) -> Result<(), FlowyError> { if revision.delta_data.is_empty() { return Err(FlowyError::internal().context("Delta data should be empty")); } - let rev_id = self.rev_persistence.add_sync_revision::(revision).await?; + let rev_id = self.rev_persistence.add_sync_revision(revision, compactor).await?; self.rev_id_counter.set(rev_id); Ok(()) } diff --git a/frontend/rust-lib/flowy-sync/src/rev_persistence.rs b/frontend/rust-lib/flowy-sync/src/rev_persistence.rs index d6b129170f..8a1d9647c4 100644 --- a/frontend/rust-lib/flowy-sync/src/rev_persistence.rs +++ b/frontend/rust-lib/flowy-sync/src/rev_persistence.rs @@ -1,9 +1,10 @@ use crate::cache::{ disk::{RevisionChangeset, RevisionDiskCache, SQLiteTextBlockRevisionPersistence}, - memory::{RevisionMemoryCache, RevisionMemoryCacheDelegate}, + memory::RevisionMemoryCacheDelegate, }; use crate::disk::{RevisionRecord, RevisionState}; -use crate::RevisionCompact; +use crate::memory::RevisionMemoryCache; +use crate::RevisionCompactor; use flowy_collaboration::entities::revision::{Revision, RevisionRange}; use flowy_database::ConnectionPool; use flowy_error::{internal_error, FlowyError, FlowyResult}; @@ -21,13 +22,17 @@ pub struct RevisionPersistence { memory_cache: Arc, sync_seq: RwLock, } + impl RevisionPersistence { - pub fn new(user_id: &str, object_id: &str, pool: Arc) -> RevisionPersistence { - let disk_cache = Arc::new(SQLiteTextBlockRevisionPersistence::new(user_id, pool)); - let memory_cache = Arc::new(RevisionMemoryCache::new(object_id, Arc::new(disk_cache.clone()))); + pub fn new( + user_id: &str, + object_id: &str, + disk_cache: Arc>, + ) -> RevisionPersistence { let object_id = object_id.to_owned(); let user_id = user_id.to_owned(); let sync_seq = RwLock::new(RevisionSyncSequence::new()); + let memory_cache = Arc::new(RevisionMemoryCache::new(&object_id, Arc::new(disk_cache.clone()))); Self { user_id, object_id, @@ -54,11 +59,12 @@ impl RevisionPersistence { } /// Save the revision to disk and append it to the end of the sync sequence. - #[tracing::instrument(level = "trace", skip(self, revision), fields(rev_id, compact_range, object_id=%self.object_id), err)] - pub(crate) async fn add_sync_revision(&self, revision: &Revision) -> FlowyResult - where - C: RevisionCompact, - { + #[tracing::instrument(level = "trace", skip_all, fields(rev_id, compact_range, object_id=%self.object_id), err)] + pub(crate) async fn add_sync_revision<'a>( + &'a self, + revision: &'a Revision, + compactor: Box, + ) -> FlowyResult { let result = self.sync_seq.read().await.compact(); match result { None => { @@ -78,7 +84,7 @@ impl RevisionPersistence { revisions.push(revision.clone()); // compact multiple revisions into one - let compact_revision = C::compact_revisions(&self.user_id, &self.object_id, revisions)?; + let compact_revision = compactor.compact(&self.user_id, &self.object_id, revisions)?; let rev_id = compact_revision.rev_id; tracing::Span::current().record("rev_id", &rev_id); @@ -215,17 +221,15 @@ pub fn mk_revision_disk_cache( Arc::new(SQLiteTextBlockRevisionPersistence::new(user_id, pool)) } -impl RevisionMemoryCacheDelegate for Arc { - #[tracing::instrument(level = "trace", skip(self, records), fields(checkpoint_result), err)] +impl RevisionMemoryCacheDelegate for Arc> { fn checkpoint_tick(&self, mut records: Vec) -> FlowyResult<()> { - let conn = &*self.pool.get().map_err(internal_error)?; records.retain(|record| record.write_to_disk); if !records.is_empty() { tracing::Span::current().record( "checkpoint_result", &format!("{} records were saved", records.len()).as_str(), ); - let _ = self.create_revision_records(records, conn)?; + let _ = self.create_revision_records(records)?; } Ok(()) } diff --git a/shared-lib/flowy-collaboration/src/client_grid/block_pad.rs b/shared-lib/flowy-collaboration/src/client_grid/block_pad.rs index 54f4b67967..0ca0d5ee03 100644 --- a/shared-lib/flowy-collaboration/src/client_grid/block_pad.rs +++ b/shared-lib/flowy-collaboration/src/client_grid/block_pad.rs @@ -1,28 +1,28 @@ use crate::entities::revision::{md5, RepeatedRevision, Revision}; use crate::errors::{internal_error, CollaborateError, CollaborateResult}; use crate::util::{cal_diff, make_delta_from_revisions}; -use flowy_grid_data_model::entities::{BlockMeta, RowMeta, RowMetaChangeset, RowOrder}; +use flowy_grid_data_model::entities::{GridBlockMeta, RowMeta, RowMetaChangeset, RowOrder}; use lib_infra::uuid; use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder}; use serde::{Deserialize, Serialize}; use std::sync::Arc; -pub type BlockMetaDelta = PlainTextDelta; -pub type BlockDeltaBuilder = PlainTextDeltaBuilder; +pub type GridBlockMetaDelta = PlainTextDelta; +pub type GridBlockMetaDeltaBuilder = PlainTextDeltaBuilder; #[derive(Debug, Deserialize, Serialize, Clone)] -pub struct BlockMetaPad { +pub struct GridBlockMetaPad { block_id: String, rows: Vec>, #[serde(skip)] - pub(crate) delta: BlockMetaDelta, + pub(crate) delta: GridBlockMetaDelta, } -impl BlockMetaPad { - pub fn from_delta(delta: BlockMetaDelta) -> CollaborateResult { +impl GridBlockMetaPad { + pub fn from_delta(delta: GridBlockMetaDelta) -> CollaborateResult { let s = delta.to_str()?; - let block_meta: BlockMeta = serde_json::from_str(&s).map_err(|e| { + let block_meta: GridBlockMeta = serde_json::from_str(&s).map_err(|e| { CollaborateError::internal().context(format!("Deserialize delta to block meta failed: {}", e)) })?; let block_id = block_meta.block_id; @@ -31,25 +31,25 @@ impl BlockMetaPad { } pub fn from_revisions(_grid_id: &str, revisions: Vec) -> CollaborateResult { - let block_delta: BlockMetaDelta = make_delta_from_revisions::(revisions)?; + let block_delta: GridBlockMetaDelta = make_delta_from_revisions::(revisions)?; Self::from_delta(block_delta) } - pub fn add_row(&mut self, row: RowMeta) -> CollaborateResult> { + pub fn add_row(&mut self, row: RowMeta) -> CollaborateResult> { self.modify(|rows| { rows.push(Arc::new(row)); Ok(Some(())) }) } - pub fn delete_rows(&mut self, row_ids: &[String]) -> CollaborateResult> { + pub fn delete_rows(&mut self, row_ids: &[String]) -> CollaborateResult> { self.modify(|rows| { rows.retain(|row| !row_ids.contains(&row.id)); Ok(Some(())) }) } - pub fn update_row(&mut self, changeset: RowMetaChangeset) -> CollaborateResult> { + pub fn update_row(&mut self, changeset: RowMetaChangeset) -> CollaborateResult> { let row_id = changeset.row_id.clone(); self.modify_row(&row_id, |row| { let mut is_changed = None; @@ -74,7 +74,7 @@ impl BlockMetaPad { }) } - pub fn modify(&mut self, f: F) -> CollaborateResult> + pub fn modify(&mut self, f: F) -> CollaborateResult> where F: for<'a> FnOnce(&'a mut Vec>) -> CollaborateResult>, { @@ -88,14 +88,14 @@ impl BlockMetaPad { None => Ok(None), Some(delta) => { self.delta = self.delta.compose(&delta)?; - Ok(Some(BlockMetaChange { delta, md5: self.md5() })) + Ok(Some(GridBlockMetaChange { delta, md5: self.md5() })) } } } } } - fn modify_row(&mut self, row_id: &str, f: F) -> CollaborateResult> + fn modify_row(&mut self, row_id: &str, f: F) -> CollaborateResult> where F: FnOnce(&mut RowMeta) -> CollaborateResult>, { @@ -123,39 +123,39 @@ impl BlockMetaPad { } } -fn json_from_grid(block_meta: &Arc) -> CollaborateResult { +fn json_from_grid(block_meta: &Arc) -> CollaborateResult { let json = serde_json::to_string(block_meta) .map_err(|err| internal_error(format!("Serialize grid to json str failed. {:?}", err)))?; Ok(json) } -pub struct BlockMetaChange { - pub delta: BlockMetaDelta, +pub struct GridBlockMetaChange { + pub delta: GridBlockMetaDelta, /// md5: the md5 of the grid after applying the change. pub md5: String, } -pub fn make_block_meta_delta(block_meta: &BlockMeta) -> BlockMetaDelta { +pub fn make_block_meta_delta(block_meta: &GridBlockMeta) -> GridBlockMetaDelta { let json = serde_json::to_string(&block_meta).unwrap(); PlainTextDeltaBuilder::new().insert(&json).build() } -pub fn make_block_meta_revisions(user_id: &str, block_meta: &BlockMeta) -> RepeatedRevision { +pub fn make_block_meta_revisions(user_id: &str, block_meta: &GridBlockMeta) -> RepeatedRevision { let delta = make_block_meta_delta(block_meta); let bytes = delta.to_bytes(); let revision = Revision::initial_revision(user_id, &block_meta.block_id, bytes); revision.into() } -impl std::default::Default for BlockMetaPad { +impl std::default::Default for GridBlockMetaPad { fn default() -> Self { - let block_meta = BlockMeta { + let block_meta = GridBlockMeta { block_id: uuid(), rows: vec![], }; let delta = make_block_meta_delta(&block_meta); - BlockMetaPad { + GridBlockMetaPad { block_id: block_meta.block_id, rows: block_meta.rows.into_iter().map(Arc::new).collect::>(), delta, @@ -165,7 +165,7 @@ impl std::default::Default for BlockMetaPad { #[cfg(test)] mod tests { - use crate::client_grid::{BlockMetaDelta, BlockMetaPad}; + use crate::client_grid::{GridBlockMetaDelta, GridMetaPad}; use flowy_grid_data_model::entities::{RowMeta, RowMetaChangeset}; use std::str::FromStr; @@ -241,8 +241,8 @@ mod tests { ); } - fn test_pad() -> BlockMetaPad { - let delta = BlockMetaDelta::from_delta_str(r#"[{"insert":"{\"block_id\":\"1\",\"rows\":[]}"}]"#).unwrap(); - BlockMetaPad::from_delta(delta).unwrap() + fn test_pad() -> GridMetaPad { + let delta = GridBlockMetaDelta::from_delta_str(r#"[{"insert":"{\"block_id\":\"1\",\"rows\":[]}"}]"#).unwrap(); + GridMetaPad::from_delta(delta).unwrap() } } diff --git a/shared-lib/flowy-collaboration/src/client_grid/grid_pad.rs b/shared-lib/flowy-collaboration/src/client_grid/grid_pad.rs index f61cd484fc..6e04107e44 100644 --- a/shared-lib/flowy-collaboration/src/client_grid/grid_pad.rs +++ b/shared-lib/flowy-collaboration/src/client_grid/grid_pad.rs @@ -1,9 +1,13 @@ use crate::entities::revision::{md5, RepeatedRevision, Revision}; use crate::errors::{internal_error, CollaborateError, CollaborateResult}; use crate::util::{cal_diff, make_delta_from_revisions}; -use flowy_grid_data_model::entities::{Field, FieldOrder, Grid, GridMeta, RowMeta, RowOrder}; +use flowy_grid_data_model::entities::{ + Field, FieldChangeset, FieldOrder, Grid, GridBlock, GridBlockChangeset, GridMeta, RepeatedField, + RepeatedFieldOrder, RowMeta, RowOrder, +}; use lib_infra::uuid; use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder}; +use std::collections::HashMap; use std::sync::Arc; pub type GridDelta = PlainTextDelta; @@ -31,13 +35,6 @@ impl GridMetaPad { Self::from_delta(grid_delta) } - pub fn create_row(&mut self, row: RowMeta) -> CollaborateResult> { - self.modify_grid(|grid| { - // grid.rows.push(row); - Ok(Some(())) - }) - } - pub fn create_field(&mut self, field: Field) -> CollaborateResult> { self.modify_grid(|grid| { grid.fields.push(field); @@ -45,13 +42,6 @@ impl GridMetaPad { }) } - pub fn delete_rows(&mut self, row_ids: &[String]) -> CollaborateResult> { - self.modify_grid(|grid| { - // grid.rows.retain(|row| !row_ids.contains(&row.id)); - Ok(Some(())) - }) - } - pub fn delete_field(&mut self, field_id: &str) -> CollaborateResult> { self.modify_grid(|grid| match grid.fields.iter().position(|field| field.id == field_id) { None => Ok(None), @@ -62,30 +52,93 @@ impl GridMetaPad { }) } - pub fn md5(&self) -> String { - md5(&self.delta.to_bytes()) - } - - pub fn grid_data(&self) -> Grid { - let field_orders = self + pub fn get_fields(&self, field_orders: RepeatedFieldOrder) -> CollaborateResult { + let field_by_field_id = self .grid_meta .fields .iter() - .map(FieldOrder::from) - .collect::>(); + .map(|field| (&field.id, field)) + .collect::>(); - // let row_orders = self - // .grid_meta - // .rows - // .iter() - // .map(RowOrder::from) - // .collect::>(); + let fields = field_orders + .iter() + .flat_map(|field_order| match field_by_field_id.get(&field_order.field_id) { + None => { + tracing::error!("Can't find the field with {}", field_order.field_id); + None + } + Some(field) => Some((*field).clone()), + }) + .collect::>(); + Ok(fields.into()) + } - Grid { - id: "".to_string(), - field_orders, - row_orders: vec![], - } + pub fn update_field(&mut self, change: FieldChangeset) -> CollaborateResult> { + let field_id = change.field_id.clone(); + self.modify_field(&field_id, |field| { + let mut is_changed = None; + if let Some(name) = change.name { + field.name = name; + is_changed = Some(()) + } + + if let Some(desc) = change.desc { + field.desc = desc; + is_changed = Some(()) + } + + if let Some(field_type) = change.field_type { + field.field_type = field_type; + is_changed = Some(()) + } + + if let Some(frozen) = change.frozen { + field.frozen = frozen; + is_changed = Some(()) + } + + if let Some(visibility) = change.visibility { + field.visibility = visibility; + is_changed = Some(()) + } + + if let Some(width) = change.width { + field.width = width; + is_changed = Some(()) + } + + if let Some(type_options) = change.type_options { + field.type_options = type_options; + is_changed = Some(()) + } + + Ok(is_changed) + }) + } + + pub fn create_block(&mut self, block: GridBlock) -> CollaborateResult> { + self.modify_grid(|grid| { + grid.blocks.push(block); + Ok(Some(())) + }) + } + + pub fn update_block(&mut self, change: GridBlockChangeset) -> CollaborateResult> { + let block_id = change.block_id.clone(); + self.modify_block(&block_id, |block| { + let mut is_changed = None; + + if let Some(row_count) = change.row_count { + block.row_count = row_count; + is_changed = Some(()); + } + + Ok(is_changed) + }) + } + + pub fn md5(&self) -> String { + md5(&self.delta.to_bytes()) } pub fn delta_str(&self) -> String { @@ -96,7 +149,7 @@ impl GridMetaPad { &self.grid_meta.fields } - pub fn modify_grid(&mut self, f: F) -> CollaborateResult> + fn modify_grid(&mut self, f: F) -> CollaborateResult> where F: FnOnce(&mut GridMeta) -> CollaborateResult>, { @@ -116,6 +169,32 @@ impl GridMetaPad { } } } + + pub fn modify_block(&mut self, block_id: &str, f: F) -> CollaborateResult> + where + F: FnOnce(&mut GridBlock) -> CollaborateResult>, + { + self.modify_grid(|grid| match grid.blocks.iter().position(|block| block.id == block_id) { + None => { + tracing::warn!("[GridMetaPad]: Can't find any block with id: {}", block_id); + Ok(None) + } + Some(index) => f(&mut grid.blocks[index]), + }) + } + + pub fn modify_field(&mut self, field_id: &str, f: F) -> CollaborateResult> + where + F: FnOnce(&mut Field) -> CollaborateResult>, + { + self.modify_grid(|grid| match grid.fields.iter().position(|field| field.id == field_id) { + None => { + tracing::warn!("[GridMetaPad]: Can't find any field with id: {}", field_id); + Ok(None) + } + Some(index) => f(&mut grid.fields[index]), + }) + } } fn json_from_grid(grid: &Arc) -> CollaborateResult { diff --git a/shared-lib/flowy-grid-data-model/src/entities/grid.rs b/shared-lib/flowy-grid-data-model/src/entities/grid.rs index e5f5c189cc..00c3981d79 100644 --- a/shared-lib/flowy-grid-data-model/src/entities/grid.rs +++ b/shared-lib/flowy-grid-data-model/src/entities/grid.rs @@ -2,9 +2,6 @@ use crate::entities::{Field, RowMeta}; use flowy_derive::ProtoBuf; use std::collections::HashMap; -pub const DEFAULT_ROW_HEIGHT: i32 = 36; -pub const DEFAULT_FIELD_WIDTH: i32 = 150; - #[derive(Debug, Clone, Default, ProtoBuf)] pub struct Grid { #[pb(index = 1)] diff --git a/shared-lib/flowy-grid-data-model/src/entities/meta.rs b/shared-lib/flowy-grid-data-model/src/entities/meta.rs index e8483dcb02..f6691456c8 100644 --- a/shared-lib/flowy-grid-data-model/src/entities/meta.rs +++ b/shared-lib/flowy-grid-data-model/src/entities/meta.rs @@ -15,11 +15,11 @@ pub struct GridMeta { pub fields: Vec, #[pb(index = 3)] - pub blocks: Vec, + pub blocks: Vec, } #[derive(Debug, Clone, Default, Serialize, Deserialize, ProtoBuf)] -pub struct Block { +pub struct GridBlock { #[pb(index = 1)] pub id: String, @@ -30,8 +30,14 @@ pub struct Block { pub row_count: i32, } +pub struct GridBlockChangeset { + pub block_id: String, + pub start_row_index: Option, + pub row_count: Option, +} + #[derive(Debug, Clone, Default, Serialize, Deserialize, ProtoBuf)] -pub struct BlockMeta { +pub struct GridBlockMeta { #[pb(index = 1)] pub block_id: String, @@ -81,6 +87,33 @@ impl Field { } } +#[derive(Debug, Clone, Default, ProtoBuf)] +pub struct FieldChangeset { + #[pb(index = 1)] + pub field_id: String, + + #[pb(index = 2, one_of)] + pub name: Option, + + #[pb(index = 3, one_of)] + pub desc: Option, + + #[pb(index = 4, one_of)] + pub field_type: Option, + + #[pb(index = 5, one_of)] + pub frozen: Option, + + #[pb(index = 6, one_of)] + pub visibility: Option, + + #[pb(index = 7, one_of)] + pub width: Option, + + #[pb(index = 8, one_of)] + pub type_options: Option, +} + #[derive(Debug, Default, ProtoBuf)] pub struct RepeatedField { #[pb(index = 1)] @@ -191,7 +224,7 @@ pub struct RowMeta { } impl RowMeta { - pub fn new(id: &str, grid_id: &str, cells: Vec) -> Self { + pub fn new(id: &str, block_id: &str, cells: Vec) -> Self { let cell_by_field_id = cells .into_iter() .map(|cell| (cell.id.clone(), cell)) @@ -199,7 +232,7 @@ impl RowMeta { Self { id: id.to_owned(), - block_id: grid_id.to_owned(), + block_id: block_id.to_owned(), cell_by_field_id, height: DEFAULT_ROW_HEIGHT, visibility: true, diff --git a/shared-lib/flowy-grid-data-model/src/protobuf/model/meta.rs b/shared-lib/flowy-grid-data-model/src/protobuf/model/meta.rs index 4107603b67..4ef31470a8 100644 --- a/shared-lib/flowy-grid-data-model/src/protobuf/model/meta.rs +++ b/shared-lib/flowy-grid-data-model/src/protobuf/model/meta.rs @@ -28,7 +28,7 @@ pub struct GridMeta { // message fields pub grid_id: ::std::string::String, pub fields: ::protobuf::RepeatedField, - pub blocks: ::protobuf::RepeatedField, + pub blocks: ::protobuf::RepeatedField, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, @@ -96,10 +96,10 @@ impl GridMeta { ::std::mem::replace(&mut self.fields, ::protobuf::RepeatedField::new()) } - // repeated .Block blocks = 3; + // repeated .GridBlock blocks = 3; - pub fn get_blocks(&self) -> &[Block] { + pub fn get_blocks(&self) -> &[GridBlock] { &self.blocks } pub fn clear_blocks(&mut self) { @@ -107,17 +107,17 @@ impl GridMeta { } // Param is passed by value, moved - pub fn set_blocks(&mut self, v: ::protobuf::RepeatedField) { + pub fn set_blocks(&mut self, v: ::protobuf::RepeatedField) { self.blocks = v; } // Mutable pointer to the field. - pub fn mut_blocks(&mut self) -> &mut ::protobuf::RepeatedField { + pub fn mut_blocks(&mut self) -> &mut ::protobuf::RepeatedField { &mut self.blocks } // Take field - pub fn take_blocks(&mut self) -> ::protobuf::RepeatedField { + pub fn take_blocks(&mut self) -> ::protobuf::RepeatedField { ::std::mem::replace(&mut self.blocks, ::protobuf::RepeatedField::new()) } } @@ -240,7 +240,7 @@ impl ::protobuf::Message for GridMeta { |m: &GridMeta| { &m.fields }, |m: &mut GridMeta| { &mut m.fields }, )); - fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( + fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( "blocks", |m: &GridMeta| { &m.blocks }, |m: &mut GridMeta| { &mut m.blocks }, @@ -281,7 +281,7 @@ impl ::protobuf::reflect::ProtobufValue for GridMeta { } #[derive(PartialEq,Clone,Default)] -pub struct Block { +pub struct GridBlock { // message fields pub id: ::std::string::String, pub start_row_index: i32, @@ -291,14 +291,14 @@ pub struct Block { pub cached_size: ::protobuf::CachedSize, } -impl<'a> ::std::default::Default for &'a Block { - fn default() -> &'a Block { - ::default_instance() +impl<'a> ::std::default::Default for &'a GridBlock { + fn default() -> &'a GridBlock { + ::default_instance() } } -impl Block { - pub fn new() -> Block { +impl GridBlock { + pub fn new() -> GridBlock { ::std::default::Default::default() } @@ -359,7 +359,7 @@ impl Block { } } -impl ::protobuf::Message for Block { +impl ::protobuf::Message for GridBlock { fn is_initialized(&self) -> bool { true } @@ -451,8 +451,8 @@ impl ::protobuf::Message for Block { Self::descriptor_static() } - fn new() -> Block { - Block::new() + fn new() -> GridBlock { + GridBlock::new() } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { @@ -461,34 +461,34 @@ impl ::protobuf::Message for Block { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( "id", - |m: &Block| { &m.id }, - |m: &mut Block| { &mut m.id }, + |m: &GridBlock| { &m.id }, + |m: &mut GridBlock| { &mut m.id }, )); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt32>( "start_row_index", - |m: &Block| { &m.start_row_index }, - |m: &mut Block| { &mut m.start_row_index }, + |m: &GridBlock| { &m.start_row_index }, + |m: &mut GridBlock| { &mut m.start_row_index }, )); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt32>( "row_count", - |m: &Block| { &m.row_count }, - |m: &mut Block| { &mut m.row_count }, + |m: &GridBlock| { &m.row_count }, + |m: &mut GridBlock| { &mut m.row_count }, )); - ::protobuf::reflect::MessageDescriptor::new_pb_name::( - "Block", + ::protobuf::reflect::MessageDescriptor::new_pb_name::( + "GridBlock", fields, file_descriptor_proto() ) }) } - fn default_instance() -> &'static Block { - static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; - instance.get(Block::new) + fn default_instance() -> &'static GridBlock { + static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; + instance.get(GridBlock::new) } } -impl ::protobuf::Clear for Block { +impl ::protobuf::Clear for GridBlock { fn clear(&mut self) { self.id.clear(); self.start_row_index = 0; @@ -497,20 +497,20 @@ impl ::protobuf::Clear for Block { } } -impl ::std::fmt::Debug for Block { +impl ::std::fmt::Debug for GridBlock { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } -impl ::protobuf::reflect::ProtobufValue for Block { +impl ::protobuf::reflect::ProtobufValue for GridBlock { fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { ::protobuf::reflect::ReflectValueRef::Message(self) } } #[derive(PartialEq,Clone,Default)] -pub struct BlockMeta { +pub struct GridBlockMeta { // message fields pub block_id: ::std::string::String, pub rows: ::protobuf::RepeatedField, @@ -519,14 +519,14 @@ pub struct BlockMeta { pub cached_size: ::protobuf::CachedSize, } -impl<'a> ::std::default::Default for &'a BlockMeta { - fn default() -> &'a BlockMeta { - ::default_instance() +impl<'a> ::std::default::Default for &'a GridBlockMeta { + fn default() -> &'a GridBlockMeta { + ::default_instance() } } -impl BlockMeta { - pub fn new() -> BlockMeta { +impl GridBlockMeta { + pub fn new() -> GridBlockMeta { ::std::default::Default::default() } @@ -582,7 +582,7 @@ impl BlockMeta { } } -impl ::protobuf::Message for BlockMeta { +impl ::protobuf::Message for GridBlockMeta { fn is_initialized(&self) -> bool { for v in &self.rows { if !v.is_initialized() { @@ -665,8 +665,8 @@ impl ::protobuf::Message for BlockMeta { Self::descriptor_static() } - fn new() -> BlockMeta { - BlockMeta::new() + fn new() -> GridBlockMeta { + GridBlockMeta::new() } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { @@ -675,29 +675,29 @@ impl ::protobuf::Message for BlockMeta { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( "block_id", - |m: &BlockMeta| { &m.block_id }, - |m: &mut BlockMeta| { &mut m.block_id }, + |m: &GridBlockMeta| { &m.block_id }, + |m: &mut GridBlockMeta| { &mut m.block_id }, )); fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( "rows", - |m: &BlockMeta| { &m.rows }, - |m: &mut BlockMeta| { &mut m.rows }, + |m: &GridBlockMeta| { &m.rows }, + |m: &mut GridBlockMeta| { &mut m.rows }, )); - ::protobuf::reflect::MessageDescriptor::new_pb_name::( - "BlockMeta", + ::protobuf::reflect::MessageDescriptor::new_pb_name::( + "GridBlockMeta", fields, file_descriptor_proto() ) }) } - fn default_instance() -> &'static BlockMeta { - static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; - instance.get(BlockMeta::new) + fn default_instance() -> &'static GridBlockMeta { + static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; + instance.get(GridBlockMeta::new) } } -impl ::protobuf::Clear for BlockMeta { +impl ::protobuf::Clear for GridBlockMeta { fn clear(&mut self) { self.block_id.clear(); self.rows.clear(); @@ -705,13 +705,13 @@ impl ::protobuf::Clear for BlockMeta { } } -impl ::std::fmt::Debug for BlockMeta { +impl ::std::fmt::Debug for GridBlockMeta { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } -impl ::protobuf::reflect::ProtobufValue for BlockMeta { +impl ::protobuf::reflect::ProtobufValue for GridBlockMeta { fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { ::protobuf::reflect::ReflectValueRef::Message(self) } @@ -1319,6 +1319,645 @@ impl ::protobuf::reflect::ProtobufValue for RepeatedField { } } +#[derive(PartialEq,Clone,Default)] +pub struct FieldChangeset { + // message fields + pub field_id: ::std::string::String, + // message oneof groups + pub one_of_name: ::std::option::Option, + pub one_of_desc: ::std::option::Option, + pub one_of_field_type: ::std::option::Option, + pub one_of_frozen: ::std::option::Option, + pub one_of_visibility: ::std::option::Option, + pub one_of_width: ::std::option::Option, + pub one_of_type_options: ::std::option::Option, + // special fields + pub unknown_fields: ::protobuf::UnknownFields, + pub cached_size: ::protobuf::CachedSize, +} + +impl<'a> ::std::default::Default for &'a FieldChangeset { + fn default() -> &'a FieldChangeset { + ::default_instance() + } +} + +#[derive(Clone,PartialEq,Debug)] +pub enum FieldChangeset_oneof_one_of_name { + name(::std::string::String), +} + +#[derive(Clone,PartialEq,Debug)] +pub enum FieldChangeset_oneof_one_of_desc { + desc(::std::string::String), +} + +#[derive(Clone,PartialEq,Debug)] +pub enum FieldChangeset_oneof_one_of_field_type { + field_type(FieldType), +} + +#[derive(Clone,PartialEq,Debug)] +pub enum FieldChangeset_oneof_one_of_frozen { + frozen(bool), +} + +#[derive(Clone,PartialEq,Debug)] +pub enum FieldChangeset_oneof_one_of_visibility { + visibility(bool), +} + +#[derive(Clone,PartialEq,Debug)] +pub enum FieldChangeset_oneof_one_of_width { + width(i32), +} + +#[derive(Clone,PartialEq,Debug)] +pub enum FieldChangeset_oneof_one_of_type_options { + type_options(AnyData), +} + +impl FieldChangeset { + pub fn new() -> FieldChangeset { + ::std::default::Default::default() + } + + // string field_id = 1; + + + 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 name = 2; + + + pub fn get_name(&self) -> &str { + match self.one_of_name { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_name::name(ref v)) => v, + _ => "", + } + } + pub fn clear_name(&mut self) { + self.one_of_name = ::std::option::Option::None; + } + + pub fn has_name(&self) -> bool { + match self.one_of_name { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_name::name(..)) => true, + _ => false, + } + } + + // Param is passed by value, moved + pub fn set_name(&mut self, v: ::std::string::String) { + self.one_of_name = ::std::option::Option::Some(FieldChangeset_oneof_one_of_name::name(v)) + } + + // Mutable pointer to the field. + pub fn mut_name(&mut self) -> &mut ::std::string::String { + if let ::std::option::Option::Some(FieldChangeset_oneof_one_of_name::name(_)) = self.one_of_name { + } else { + self.one_of_name = ::std::option::Option::Some(FieldChangeset_oneof_one_of_name::name(::std::string::String::new())); + } + match self.one_of_name { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_name::name(ref mut v)) => v, + _ => panic!(), + } + } + + // Take field + pub fn take_name(&mut self) -> ::std::string::String { + if self.has_name() { + match self.one_of_name.take() { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_name::name(v)) => v, + _ => panic!(), + } + } else { + ::std::string::String::new() + } + } + + // string desc = 3; + + + pub fn get_desc(&self) -> &str { + match self.one_of_desc { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_desc::desc(ref v)) => v, + _ => "", + } + } + pub fn clear_desc(&mut self) { + self.one_of_desc = ::std::option::Option::None; + } + + pub fn has_desc(&self) -> bool { + match self.one_of_desc { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_desc::desc(..)) => true, + _ => false, + } + } + + // Param is passed by value, moved + pub fn set_desc(&mut self, v: ::std::string::String) { + self.one_of_desc = ::std::option::Option::Some(FieldChangeset_oneof_one_of_desc::desc(v)) + } + + // Mutable pointer to the field. + pub fn mut_desc(&mut self) -> &mut ::std::string::String { + if let ::std::option::Option::Some(FieldChangeset_oneof_one_of_desc::desc(_)) = self.one_of_desc { + } else { + self.one_of_desc = ::std::option::Option::Some(FieldChangeset_oneof_one_of_desc::desc(::std::string::String::new())); + } + match self.one_of_desc { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_desc::desc(ref mut v)) => v, + _ => panic!(), + } + } + + // Take field + pub fn take_desc(&mut self) -> ::std::string::String { + if self.has_desc() { + match self.one_of_desc.take() { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_desc::desc(v)) => v, + _ => panic!(), + } + } else { + ::std::string::String::new() + } + } + + // .FieldType field_type = 4; + + + pub fn get_field_type(&self) -> FieldType { + match self.one_of_field_type { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_field_type::field_type(v)) => v, + _ => FieldType::RichText, + } + } + pub fn clear_field_type(&mut self) { + self.one_of_field_type = ::std::option::Option::None; + } + + pub fn has_field_type(&self) -> bool { + match self.one_of_field_type { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_field_type::field_type(..)) => true, + _ => false, + } + } + + // Param is passed by value, moved + pub fn set_field_type(&mut self, v: FieldType) { + self.one_of_field_type = ::std::option::Option::Some(FieldChangeset_oneof_one_of_field_type::field_type(v)) + } + + // bool frozen = 5; + + + pub fn get_frozen(&self) -> bool { + match self.one_of_frozen { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_frozen::frozen(v)) => v, + _ => false, + } + } + pub fn clear_frozen(&mut self) { + self.one_of_frozen = ::std::option::Option::None; + } + + pub fn has_frozen(&self) -> bool { + match self.one_of_frozen { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_frozen::frozen(..)) => true, + _ => false, + } + } + + // Param is passed by value, moved + pub fn set_frozen(&mut self, v: bool) { + self.one_of_frozen = ::std::option::Option::Some(FieldChangeset_oneof_one_of_frozen::frozen(v)) + } + + // bool visibility = 6; + + + pub fn get_visibility(&self) -> bool { + match self.one_of_visibility { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_visibility::visibility(v)) => v, + _ => false, + } + } + pub fn clear_visibility(&mut self) { + self.one_of_visibility = ::std::option::Option::None; + } + + pub fn has_visibility(&self) -> bool { + match self.one_of_visibility { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_visibility::visibility(..)) => true, + _ => false, + } + } + + // Param is passed by value, moved + pub fn set_visibility(&mut self, v: bool) { + self.one_of_visibility = ::std::option::Option::Some(FieldChangeset_oneof_one_of_visibility::visibility(v)) + } + + // int32 width = 7; + + + pub fn get_width(&self) -> i32 { + match self.one_of_width { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_width::width(v)) => v, + _ => 0, + } + } + pub fn clear_width(&mut self) { + self.one_of_width = ::std::option::Option::None; + } + + pub fn has_width(&self) -> bool { + match self.one_of_width { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_width::width(..)) => true, + _ => false, + } + } + + // Param is passed by value, moved + pub fn set_width(&mut self, v: i32) { + self.one_of_width = ::std::option::Option::Some(FieldChangeset_oneof_one_of_width::width(v)) + } + + // .AnyData type_options = 8; + + + pub fn get_type_options(&self) -> &AnyData { + match self.one_of_type_options { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_type_options::type_options(ref v)) => v, + _ => ::default_instance(), + } + } + pub fn clear_type_options(&mut self) { + self.one_of_type_options = ::std::option::Option::None; + } + + pub fn has_type_options(&self) -> bool { + match self.one_of_type_options { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_type_options::type_options(..)) => true, + _ => false, + } + } + + // Param is passed by value, moved + pub fn set_type_options(&mut self, v: AnyData) { + self.one_of_type_options = ::std::option::Option::Some(FieldChangeset_oneof_one_of_type_options::type_options(v)) + } + + // Mutable pointer to the field. + pub fn mut_type_options(&mut self) -> &mut AnyData { + if let ::std::option::Option::Some(FieldChangeset_oneof_one_of_type_options::type_options(_)) = self.one_of_type_options { + } else { + self.one_of_type_options = ::std::option::Option::Some(FieldChangeset_oneof_one_of_type_options::type_options(AnyData::new())); + } + match self.one_of_type_options { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_type_options::type_options(ref mut v)) => v, + _ => panic!(), + } + } + + // Take field + pub fn take_type_options(&mut self) -> AnyData { + if self.has_type_options() { + match self.one_of_type_options.take() { + ::std::option::Option::Some(FieldChangeset_oneof_one_of_type_options::type_options(v)) => v, + _ => panic!(), + } + } else { + AnyData::new() + } + } +} + +impl ::protobuf::Message for FieldChangeset { + fn is_initialized(&self) -> bool { + if let Some(FieldChangeset_oneof_one_of_type_options::type_options(ref v)) = self.one_of_type_options { + 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_singular_proto3_string_into(wire_type, is, &mut self.field_id)?; + }, + 2 => { + if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited { + return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); + } + self.one_of_name = ::std::option::Option::Some(FieldChangeset_oneof_one_of_name::name(is.read_string()?)); + }, + 3 => { + if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited { + return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); + } + self.one_of_desc = ::std::option::Option::Some(FieldChangeset_oneof_one_of_desc::desc(is.read_string()?)); + }, + 4 => { + if wire_type != ::protobuf::wire_format::WireTypeVarint { + return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); + } + self.one_of_field_type = ::std::option::Option::Some(FieldChangeset_oneof_one_of_field_type::field_type(is.read_enum()?)); + }, + 5 => { + if wire_type != ::protobuf::wire_format::WireTypeVarint { + return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); + } + self.one_of_frozen = ::std::option::Option::Some(FieldChangeset_oneof_one_of_frozen::frozen(is.read_bool()?)); + }, + 6 => { + if wire_type != ::protobuf::wire_format::WireTypeVarint { + return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); + } + self.one_of_visibility = ::std::option::Option::Some(FieldChangeset_oneof_one_of_visibility::visibility(is.read_bool()?)); + }, + 7 => { + if wire_type != ::protobuf::wire_format::WireTypeVarint { + return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); + } + self.one_of_width = ::std::option::Option::Some(FieldChangeset_oneof_one_of_width::width(is.read_int32()?)); + }, + 8 => { + if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited { + return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); + } + self.one_of_type_options = ::std::option::Option::Some(FieldChangeset_oneof_one_of_type_options::type_options(is.read_message()?)); + }, + _ => { + ::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.field_id.is_empty() { + my_size += ::protobuf::rt::string_size(1, &self.field_id); + } + if let ::std::option::Option::Some(ref v) = self.one_of_name { + match v { + &FieldChangeset_oneof_one_of_name::name(ref v) => { + my_size += ::protobuf::rt::string_size(2, &v); + }, + }; + } + if let ::std::option::Option::Some(ref v) = self.one_of_desc { + match v { + &FieldChangeset_oneof_one_of_desc::desc(ref v) => { + my_size += ::protobuf::rt::string_size(3, &v); + }, + }; + } + if let ::std::option::Option::Some(ref v) = self.one_of_field_type { + match v { + &FieldChangeset_oneof_one_of_field_type::field_type(v) => { + my_size += ::protobuf::rt::enum_size(4, v); + }, + }; + } + if let ::std::option::Option::Some(ref v) = self.one_of_frozen { + match v { + &FieldChangeset_oneof_one_of_frozen::frozen(v) => { + my_size += 2; + }, + }; + } + if let ::std::option::Option::Some(ref v) = self.one_of_visibility { + match v { + &FieldChangeset_oneof_one_of_visibility::visibility(v) => { + my_size += 2; + }, + }; + } + if let ::std::option::Option::Some(ref v) = self.one_of_width { + match v { + &FieldChangeset_oneof_one_of_width::width(v) => { + my_size += ::protobuf::rt::value_size(7, v, ::protobuf::wire_format::WireTypeVarint); + }, + }; + } + if let ::std::option::Option::Some(ref v) = self.one_of_type_options { + match v { + &FieldChangeset_oneof_one_of_type_options::type_options(ref v) => { + let len = v.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<()> { + if !self.field_id.is_empty() { + os.write_string(1, &self.field_id)?; + } + if let ::std::option::Option::Some(ref v) = self.one_of_name { + match v { + &FieldChangeset_oneof_one_of_name::name(ref v) => { + os.write_string(2, v)?; + }, + }; + } + if let ::std::option::Option::Some(ref v) = self.one_of_desc { + match v { + &FieldChangeset_oneof_one_of_desc::desc(ref v) => { + os.write_string(3, v)?; + }, + }; + } + if let ::std::option::Option::Some(ref v) = self.one_of_field_type { + match v { + &FieldChangeset_oneof_one_of_field_type::field_type(v) => { + os.write_enum(4, ::protobuf::ProtobufEnum::value(&v))?; + }, + }; + } + if let ::std::option::Option::Some(ref v) = self.one_of_frozen { + match v { + &FieldChangeset_oneof_one_of_frozen::frozen(v) => { + os.write_bool(5, v)?; + }, + }; + } + if let ::std::option::Option::Some(ref v) = self.one_of_visibility { + match v { + &FieldChangeset_oneof_one_of_visibility::visibility(v) => { + os.write_bool(6, v)?; + }, + }; + } + if let ::std::option::Option::Some(ref v) = self.one_of_width { + match v { + &FieldChangeset_oneof_one_of_width::width(v) => { + os.write_int32(7, v)?; + }, + }; + } + if let ::std::option::Option::Some(ref v) = self.one_of_type_options { + match v { + &FieldChangeset_oneof_one_of_type_options::type_options(ref v) => { + os.write_tag(8, ::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) -> ::std::boxed::Box { + self + } + + fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { + Self::descriptor_static() + } + + fn new() -> FieldChangeset { + FieldChangeset::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>( + "field_id", + |m: &FieldChangeset| { &m.field_id }, + |m: &mut FieldChangeset| { &mut m.field_id }, + )); + fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>( + "name", + FieldChangeset::has_name, + FieldChangeset::get_name, + )); + fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>( + "desc", + FieldChangeset::has_desc, + FieldChangeset::get_desc, + )); + fields.push(::protobuf::reflect::accessor::make_singular_enum_accessor::<_, FieldType>( + "field_type", + FieldChangeset::has_field_type, + FieldChangeset::get_field_type, + )); + fields.push(::protobuf::reflect::accessor::make_singular_bool_accessor::<_>( + "frozen", + FieldChangeset::has_frozen, + FieldChangeset::get_frozen, + )); + fields.push(::protobuf::reflect::accessor::make_singular_bool_accessor::<_>( + "visibility", + FieldChangeset::has_visibility, + FieldChangeset::get_visibility, + )); + fields.push(::protobuf::reflect::accessor::make_singular_i32_accessor::<_>( + "width", + FieldChangeset::has_width, + FieldChangeset::get_width, + )); + fields.push(::protobuf::reflect::accessor::make_singular_message_accessor::<_, AnyData>( + "type_options", + FieldChangeset::has_type_options, + FieldChangeset::get_type_options, + )); + ::protobuf::reflect::MessageDescriptor::new_pb_name::( + "FieldChangeset", + fields, + file_descriptor_proto() + ) + }) + } + + fn default_instance() -> &'static FieldChangeset { + static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; + instance.get(FieldChangeset::new) + } +} + +impl ::protobuf::Clear for FieldChangeset { + fn clear(&mut self) { + self.field_id.clear(); + self.one_of_name = ::std::option::Option::None; + self.one_of_desc = ::std::option::Option::None; + self.one_of_field_type = ::std::option::Option::None; + self.one_of_frozen = ::std::option::Option::None; + self.one_of_visibility = ::std::option::Option::None; + self.one_of_width = ::std::option::Option::None; + self.one_of_type_options = ::std::option::Option::None; + self.unknown_fields.clear(); + } +} + +impl ::std::fmt::Debug for FieldChangeset { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for FieldChangeset { + fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { + ::protobuf::reflect::ReflectValueRef::Message(self) + } +} + #[derive(PartialEq,Clone,Default)] pub struct AnyData { // message fields @@ -2537,43 +3176,53 @@ impl ::protobuf::reflect::ProtobufValue for FieldType { } static file_descriptor_proto_data: &'static [u8] = b"\ - \n\nmeta.proto\"c\n\x08GridMeta\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\ + \n\nmeta.proto\"g\n\x08GridMeta\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\ \x06gridId\x12\x1e\n\x06fields\x18\x02\x20\x03(\x0b2\x06.FieldR\x06field\ - s\x12\x1e\n\x06blocks\x18\x03\x20\x03(\x0b2\x06.BlockR\x06blocks\"\\\n\ - \x05Block\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12&\n\x0fstart_row_\ - index\x18\x02\x20\x01(\x05R\rstartRowIndex\x12\x1b\n\trow_count\x18\x03\ - \x20\x01(\x05R\x08rowCount\"D\n\tBlockMeta\x12\x19\n\x08block_id\x18\x01\ - \x20\x01(\tR\x07blockId\x12\x1c\n\x04rows\x18\x02\x20\x03(\x0b2\x08.RowM\ - etaR\x04rows\"\xe5\x01\n\x05Field\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\ - \x02id\x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\x12\x12\n\x04desc\ + s\x12\"\n\x06blocks\x18\x03\x20\x03(\x0b2\n.GridBlockR\x06blocks\"`\n\tG\ + ridBlock\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12&\n\x0fstart_row_i\ + ndex\x18\x02\x20\x01(\x05R\rstartRowIndex\x12\x1b\n\trow_count\x18\x03\ + \x20\x01(\x05R\x08rowCount\"H\n\rGridBlockMeta\x12\x19\n\x08block_id\x18\ + \x01\x20\x01(\tR\x07blockId\x12\x1c\n\x04rows\x18\x02\x20\x03(\x0b2\x08.\ + RowMetaR\x04rows\"\xe5\x01\n\x05Field\x12\x0e\n\x02id\x18\x01\x20\x01(\t\ + R\x02id\x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\x12\x12\n\x04desc\ \x18\x03\x20\x01(\tR\x04desc\x12)\n\nfield_type\x18\x04\x20\x01(\x0e2\n.\ FieldTypeR\tfieldType\x12\x16\n\x06frozen\x18\x05\x20\x01(\x08R\x06froze\ n\x12\x1e\n\nvisibility\x18\x06\x20\x01(\x08R\nvisibility\x12\x14\n\x05w\ idth\x18\x07\x20\x01(\x05R\x05width\x12+\n\x0ctype_options\x18\x08\x20\ \x01(\x0b2\x08.AnyDataR\x0btypeOptions\"-\n\rRepeatedField\x12\x1c\n\x05\ - items\x18\x01\x20\x03(\x0b2\x06.FieldR\x05items\"8\n\x07AnyData\x12\x17\ - \n\x07type_id\x18\x01\x20\x01(\tR\x06typeId\x12\x14\n\x05value\x18\x02\ - \x20\x01(\x0cR\x05value\"\xff\x01\n\x07RowMeta\x12\x0e\n\x02id\x18\x01\ - \x20\x01(\tR\x02id\x12\x19\n\x08block_id\x18\x02\x20\x01(\tR\x07blockId\ - \x12D\n\x10cell_by_field_id\x18\x03\x20\x03(\x0b2\x1b.RowMeta.CellByFiel\ - dIdEntryR\rcellByFieldId\x12\x16\n\x06height\x18\x04\x20\x01(\x05R\x06he\ - ight\x12\x1e\n\nvisibility\x18\x05\x20\x01(\x08R\nvisibility\x1aK\n\x12C\ - ellByFieldIdEntry\x12\x10\n\x03key\x18\x01\x20\x01(\tR\x03key\x12\x1f\n\ - \x05value\x18\x02\x20\x01(\x0b2\t.CellMetaR\x05value:\x028\x01\"\xa7\x02\ - \n\x10RowMetaChangeset\x12\x15\n\x06row_id\x18\x01\x20\x01(\tR\x05rowId\ - \x12\x18\n\x06height\x18\x02\x20\x01(\x05H\0R\x06height\x12\x20\n\nvisib\ - ility\x18\x03\x20\x01(\x08H\x01R\nvisibility\x12M\n\x10cell_by_field_id\ - \x18\x04\x20\x03(\x0b2$.RowMetaChangeset.CellByFieldIdEntryR\rcellByFiel\ - dId\x1aK\n\x12CellByFieldIdEntry\x12\x10\n\x03key\x18\x01\x20\x01(\tR\ - \x03key\x12\x1f\n\x05value\x18\x02\x20\x01(\x0b2\t.CellMetaR\x05value:\ - \x028\x01B\x0f\n\rone_of_heightB\x13\n\x11one_of_visibility\"\x82\x01\n\ - \x08CellMeta\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(\t\ - R\x07fieldId\x12\x1c\n\x04data\x18\x04\x20\x01(\x0b2\x08.AnyDataR\x04dat\ - a\x12\x16\n\x06height\x18\x05\x20\x01(\x05R\x06height*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\ + items\x18\x01\x20\x03(\x0b2\x06.FieldR\x05items\"\x87\x03\n\x0eFieldChan\ + geset\x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x14\n\x04\ + name\x18\x02\x20\x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\x03\x20\x01(\t\ + H\x01R\x04desc\x12+\n\nfield_type\x18\x04\x20\x01(\x0e2\n.FieldTypeH\x02\ + R\tfieldType\x12\x18\n\x06frozen\x18\x05\x20\x01(\x08H\x03R\x06frozen\ + \x12\x20\n\nvisibility\x18\x06\x20\x01(\x08H\x04R\nvisibility\x12\x16\n\ + \x05width\x18\x07\x20\x01(\x05H\x05R\x05width\x12-\n\x0ctype_options\x18\ + \x08\x20\x01(\x0b2\x08.AnyDataH\x06R\x0btypeOptionsB\r\n\x0bone_of_nameB\ + \r\n\x0bone_of_descB\x13\n\x11one_of_field_typeB\x0f\n\rone_of_frozenB\ + \x13\n\x11one_of_visibilityB\x0e\n\x0cone_of_widthB\x15\n\x13one_of_type\ + _options\"8\n\x07AnyData\x12\x17\n\x07type_id\x18\x01\x20\x01(\tR\x06typ\ + eId\x12\x14\n\x05value\x18\x02\x20\x01(\x0cR\x05value\"\xff\x01\n\x07Row\ + Meta\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x19\n\x08block_id\x18\ + \x02\x20\x01(\tR\x07blockId\x12D\n\x10cell_by_field_id\x18\x03\x20\x03(\ + \x0b2\x1b.RowMeta.CellByFieldIdEntryR\rcellByFieldId\x12\x16\n\x06height\ + \x18\x04\x20\x01(\x05R\x06height\x12\x1e\n\nvisibility\x18\x05\x20\x01(\ + \x08R\nvisibility\x1aK\n\x12CellByFieldIdEntry\x12\x10\n\x03key\x18\x01\ + \x20\x01(\tR\x03key\x12\x1f\n\x05value\x18\x02\x20\x01(\x0b2\t.CellMetaR\ + \x05value:\x028\x01\"\xa7\x02\n\x10RowMetaChangeset\x12\x15\n\x06row_id\ + \x18\x01\x20\x01(\tR\x05rowId\x12\x18\n\x06height\x18\x02\x20\x01(\x05H\ + \0R\x06height\x12\x20\n\nvisibility\x18\x03\x20\x01(\x08H\x01R\nvisibili\ + ty\x12M\n\x10cell_by_field_id\x18\x04\x20\x03(\x0b2$.RowMetaChangeset.Ce\ + llByFieldIdEntryR\rcellByFieldId\x1aK\n\x12CellByFieldIdEntry\x12\x10\n\ + \x03key\x18\x01\x20\x01(\tR\x03key\x12\x1f\n\x05value\x18\x02\x20\x01(\ + \x0b2\t.CellMetaR\x05value:\x028\x01B\x0f\n\rone_of_heightB\x13\n\x11one\ + _of_visibility\"\x82\x01\n\x08CellMeta\x12\x0e\n\x02id\x18\x01\x20\x01(\ + \tR\x02id\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\x12\x19\n\x08\ + field_id\x18\x03\x20\x01(\tR\x07fieldId\x12\x1c\n\x04data\x18\x04\x20\ + \x01(\x0b2\x08.AnyDataR\x04data\x12\x16\n\x06height\x18\x05\x20\x01(\x05\ + R\x06height*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\x06prot\ + o3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/shared-lib/flowy-grid-data-model/src/protobuf/proto/meta.proto b/shared-lib/flowy-grid-data-model/src/protobuf/proto/meta.proto index dc00b1e72c..bba2c8c8dc 100644 --- a/shared-lib/flowy-grid-data-model/src/protobuf/proto/meta.proto +++ b/shared-lib/flowy-grid-data-model/src/protobuf/proto/meta.proto @@ -3,14 +3,14 @@ syntax = "proto3"; message GridMeta { string grid_id = 1; repeated Field fields = 2; - repeated Block blocks = 3; + repeated GridBlock blocks = 3; } -message Block { +message GridBlock { string id = 1; int32 start_row_index = 2; int32 row_count = 3; } -message BlockMeta { +message GridBlockMeta { string block_id = 1; repeated RowMeta rows = 2; } @@ -27,6 +27,16 @@ message Field { message RepeatedField { repeated Field items = 1; } +message FieldChangeset { + string field_id = 1; + oneof one_of_name { string name = 2; }; + oneof one_of_desc { string desc = 3; }; + oneof one_of_field_type { FieldType field_type = 4; }; + oneof one_of_frozen { bool frozen = 5; }; + oneof one_of_visibility { bool visibility = 6; }; + oneof one_of_width { int32 width = 7; }; + oneof one_of_type_options { AnyData type_options = 8; }; +} message AnyData { string type_id = 1; bytes value = 2;