From 1e6d82c0ec158b772cf82e332705ede92c83626e Mon Sep 17 00:00:00 2001 From: appflowy Date: Sat, 12 Mar 2022 21:06:15 +0800 Subject: [PATCH] chore: add field builder --- .../lib/protobuf/flowy-grid/protobuf.dart | 2 +- .../protobuf/flowy-grid/type_options.pb.dart | 458 ++++++++++++++++++ .../flowy-grid/type_options.pbenum.dart | 62 +++ .../flowy-grid/type_options.pbjson.dart | 125 +++++ .../flowy-grid/type_options.pbserver.dart | 9 + frontend/rust-lib/Cargo.lock | 2 + frontend/rust-lib/flowy-block/src/editor.rs | 14 +- frontend/rust-lib/flowy-block/src/queue.rs | 6 +- .../flowy-block/tests/document/mod.rs | 4 +- .../document/{edit_script.rs => script.rs} | 10 +- .../{document_test.rs => text_block_test.rs} | 34 +- .../src/services/folder_editor.rs | 2 +- .../tests/workspace/folder_test.rs | 17 +- .../flowy-folder/tests/workspace/helper.rs | 212 -------- .../flowy-folder/tests/workspace/main.rs | 1 - .../flowy-folder/tests/workspace/script.rs | 250 +++++++++- frontend/rust-lib/flowy-grid/Cargo.toml | 7 +- frontend/rust-lib/flowy-grid/Flowy.toml | 2 +- frontend/rust-lib/flowy-grid/src/lib.rs | 1 + frontend/rust-lib/flowy-grid/src/macros.rs | 35 +- frontend/rust-lib/flowy-grid/src/manager.rs | 2 +- .../flowy-grid/src/protobuf/model/mod.rs | 4 +- .../model/{cell_data.rs => type_options.rs} | 167 +++---- .../{cell_data.proto => type_options.proto} | 8 +- .../src/services/field/cell_stringify.rs | 34 ++ .../src/services/field/field_builder.rs | 217 +++++++++ .../flowy-grid/src/services/field/mod.rs | 7 + .../{cell_data.rs => field/type_options.rs} | 175 +++---- .../flowy-grid/src/services/grid_editor.rs | 15 +- .../src/services/grid_meta_editor.rs | 2 +- .../rust-lib/flowy-grid/src/services/mod.rs | 3 +- .../flowy-grid/src/services/stringify.rs | 29 -- .../rust-lib/flowy-grid/src/services/util.rs | 10 +- frontend/rust-lib/flowy-grid/src/util.rs | 30 ++ .../flowy-grid/tests/grid/grid_test.rs | 16 + .../rust-lib/flowy-grid/tests/grid/mod.rs | 2 + .../rust-lib/flowy-grid/tests/grid/script.rs | 75 +++ .../rust-lib/flowy-grid/tests/grid_test.rs | 0 frontend/rust-lib/flowy-grid/tests/main.rs | 1 + .../flowy-sdk/src/deps_resolve/folder_deps.rs | 2 +- .../rust-lib/flowy-sync/src/rev_manager.rs | 4 +- frontend/rust-lib/flowy-test/src/helper.rs | 19 +- .../src/client_grid/grid_builder.rs | 20 +- .../src/entities/meta.rs | 19 +- 44 files changed, 1545 insertions(+), 569 deletions(-) create mode 100644 frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/type_options.pb.dart create mode 100644 frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/type_options.pbenum.dart create mode 100644 frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/type_options.pbjson.dart create mode 100644 frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/type_options.pbserver.dart rename frontend/rust-lib/flowy-block/tests/document/{edit_script.rs => script.rs} (91%) rename frontend/rust-lib/flowy-block/tests/document/{document_test.rs => text_block_test.rs} (69%) delete mode 100644 frontend/rust-lib/flowy-folder/tests/workspace/helper.rs rename frontend/rust-lib/flowy-grid/src/protobuf/model/{cell_data.rs => type_options.rs} (91%) rename frontend/rust-lib/flowy-grid/src/protobuf/proto/{cell_data.proto => type_options.proto} (87%) create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/cell_stringify.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/field_builder.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/mod.rs rename frontend/rust-lib/flowy-grid/src/services/{cell_data.rs => field/type_options.rs} (65%) delete mode 100644 frontend/rust-lib/flowy-grid/src/services/stringify.rs create mode 100644 frontend/rust-lib/flowy-grid/src/util.rs create mode 100644 frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs create mode 100644 frontend/rust-lib/flowy-grid/tests/grid/mod.rs create mode 100644 frontend/rust-lib/flowy-grid/tests/grid/script.rs delete mode 100644 frontend/rust-lib/flowy-grid/tests/grid_test.rs create mode 100644 frontend/rust-lib/flowy-grid/tests/main.rs diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/protobuf.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/protobuf.dart index 39a722893a..b7eb77d660 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/protobuf.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/protobuf.dart @@ -1,3 +1,3 @@ // Auto-generated, do not edit -export './cell_data.pb.dart'; +export './type_options.pb.dart'; export './event_map.pb.dart'; diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/type_options.pb.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/type_options.pb.dart new file mode 100644 index 0000000000..152eef0c74 --- /dev/null +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/type_options.pb.dart @@ -0,0 +1,458 @@ +/// +// Generated code. Do not modify. +// source: type_options.proto +// +// @dart = 2.12 +// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields + +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +import 'type_options.pbenum.dart'; + +export 'type_options.pbenum.dart'; + +class RichTextDescription extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RichTextDescription', createEmptyInstance: create) + ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'format') + ..hasRequiredFields = false + ; + + RichTextDescription._() : super(); + factory RichTextDescription({ + $core.String? format, + }) { + final _result = create(); + if (format != null) { + _result.format = format; + } + return _result; + } + factory RichTextDescription.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory RichTextDescription.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') + RichTextDescription clone() => RichTextDescription()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + RichTextDescription copyWith(void Function(RichTextDescription) updates) => super.copyWith((message) => updates(message as RichTextDescription)) as RichTextDescription; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static RichTextDescription create() => RichTextDescription._(); + RichTextDescription createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static RichTextDescription getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static RichTextDescription? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get format => $_getSZ(0); + @$pb.TagNumber(1) + set format($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasFormat() => $_has(0); + @$pb.TagNumber(1) + void clearFormat() => clearField(1); +} + +class CheckboxDescription extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CheckboxDescription', createEmptyInstance: create) + ..aOB(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isSelected') + ..hasRequiredFields = false + ; + + CheckboxDescription._() : super(); + factory CheckboxDescription({ + $core.bool? isSelected, + }) { + final _result = create(); + if (isSelected != null) { + _result.isSelected = isSelected; + } + return _result; + } + factory CheckboxDescription.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory CheckboxDescription.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') + CheckboxDescription clone() => CheckboxDescription()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + CheckboxDescription copyWith(void Function(CheckboxDescription) updates) => super.copyWith((message) => updates(message as CheckboxDescription)) as CheckboxDescription; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static CheckboxDescription create() => CheckboxDescription._(); + CheckboxDescription createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static CheckboxDescription getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static CheckboxDescription? _defaultInstance; + + @$pb.TagNumber(1) + $core.bool get isSelected => $_getBF(0); + @$pb.TagNumber(1) + set isSelected($core.bool v) { $_setBool(0, v); } + @$pb.TagNumber(1) + $core.bool hasIsSelected() => $_has(0); + @$pb.TagNumber(1) + void clearIsSelected() => clearField(1); +} + +class DateDescription extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'DateDescription', createEmptyInstance: create) + ..e(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dateFormat', $pb.PbFieldType.OE, defaultOrMaker: DateFormat.Local, valueOf: DateFormat.valueOf, enumValues: DateFormat.values) + ..e(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'timeFormat', $pb.PbFieldType.OE, defaultOrMaker: TimeFormat.TwelveHour, valueOf: TimeFormat.valueOf, enumValues: TimeFormat.values) + ..hasRequiredFields = false + ; + + DateDescription._() : super(); + factory DateDescription({ + DateFormat? dateFormat, + TimeFormat? timeFormat, + }) { + final _result = create(); + if (dateFormat != null) { + _result.dateFormat = dateFormat; + } + if (timeFormat != null) { + _result.timeFormat = timeFormat; + } + return _result; + } + factory DateDescription.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory DateDescription.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') + DateDescription clone() => DateDescription()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + DateDescription copyWith(void Function(DateDescription) updates) => super.copyWith((message) => updates(message as DateDescription)) as DateDescription; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static DateDescription create() => DateDescription._(); + DateDescription createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static DateDescription getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static DateDescription? _defaultInstance; + + @$pb.TagNumber(1) + DateFormat get dateFormat => $_getN(0); + @$pb.TagNumber(1) + set dateFormat(DateFormat v) { setField(1, v); } + @$pb.TagNumber(1) + $core.bool hasDateFormat() => $_has(0); + @$pb.TagNumber(1) + void clearDateFormat() => clearField(1); + + @$pb.TagNumber(2) + TimeFormat get timeFormat => $_getN(1); + @$pb.TagNumber(2) + set timeFormat(TimeFormat v) { setField(2, v); } + @$pb.TagNumber(2) + $core.bool hasTimeFormat() => $_has(1); + @$pb.TagNumber(2) + void clearTimeFormat() => clearField(2); +} + +class SingleSelectDescription extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SingleSelectDescription', createEmptyInstance: create) + ..pc(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'options', $pb.PbFieldType.PM, subBuilder: SelectOption.create) + ..aOB(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'disableColor') + ..hasRequiredFields = false + ; + + SingleSelectDescription._() : super(); + factory SingleSelectDescription({ + $core.Iterable? options, + $core.bool? disableColor, + }) { + final _result = create(); + if (options != null) { + _result.options.addAll(options); + } + if (disableColor != null) { + _result.disableColor = disableColor; + } + return _result; + } + factory SingleSelectDescription.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory SingleSelectDescription.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') + SingleSelectDescription clone() => SingleSelectDescription()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + SingleSelectDescription copyWith(void Function(SingleSelectDescription) updates) => super.copyWith((message) => updates(message as SingleSelectDescription)) as SingleSelectDescription; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static SingleSelectDescription create() => SingleSelectDescription._(); + SingleSelectDescription createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static SingleSelectDescription getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static SingleSelectDescription? _defaultInstance; + + @$pb.TagNumber(1) + $core.List get options => $_getList(0); + + @$pb.TagNumber(2) + $core.bool get disableColor => $_getBF(1); + @$pb.TagNumber(2) + set disableColor($core.bool v) { $_setBool(1, v); } + @$pb.TagNumber(2) + $core.bool hasDisableColor() => $_has(1); + @$pb.TagNumber(2) + void clearDisableColor() => clearField(2); +} + +class MultiSelectDescription extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'MultiSelectDescription', createEmptyInstance: create) + ..pc(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'options', $pb.PbFieldType.PM, subBuilder: SelectOption.create) + ..aOB(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'disableColor') + ..hasRequiredFields = false + ; + + MultiSelectDescription._() : super(); + factory MultiSelectDescription({ + $core.Iterable? options, + $core.bool? disableColor, + }) { + final _result = create(); + if (options != null) { + _result.options.addAll(options); + } + if (disableColor != null) { + _result.disableColor = disableColor; + } + return _result; + } + factory MultiSelectDescription.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory MultiSelectDescription.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') + MultiSelectDescription clone() => MultiSelectDescription()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + MultiSelectDescription copyWith(void Function(MultiSelectDescription) updates) => super.copyWith((message) => updates(message as MultiSelectDescription)) as MultiSelectDescription; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static MultiSelectDescription create() => MultiSelectDescription._(); + MultiSelectDescription createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static MultiSelectDescription getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static MultiSelectDescription? _defaultInstance; + + @$pb.TagNumber(1) + $core.List get options => $_getList(0); + + @$pb.TagNumber(2) + $core.bool get disableColor => $_getBF(1); + @$pb.TagNumber(2) + set disableColor($core.bool v) { $_setBool(1, v); } + @$pb.TagNumber(2) + $core.bool hasDisableColor() => $_has(1); + @$pb.TagNumber(2) + void clearDisableColor() => clearField(2); +} + +class SelectOption extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SelectOption', createEmptyInstance: create) + ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id') + ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name') + ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'color') + ..hasRequiredFields = false + ; + + SelectOption._() : super(); + factory SelectOption({ + $core.String? id, + $core.String? name, + $core.String? color, + }) { + final _result = create(); + if (id != null) { + _result.id = id; + } + if (name != null) { + _result.name = name; + } + if (color != null) { + _result.color = color; + } + return _result; + } + factory SelectOption.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory SelectOption.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') + SelectOption clone() => SelectOption()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + SelectOption copyWith(void Function(SelectOption) updates) => super.copyWith((message) => updates(message as SelectOption)) as SelectOption; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static SelectOption create() => SelectOption._(); + SelectOption createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static SelectOption getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static SelectOption? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get id => $_getSZ(0); + @$pb.TagNumber(1) + set id($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasId() => $_has(0); + @$pb.TagNumber(1) + void clearId() => clearField(1); + + @$pb.TagNumber(2) + $core.String get name => $_getSZ(1); + @$pb.TagNumber(2) + set name($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasName() => $_has(1); + @$pb.TagNumber(2) + void clearName() => clearField(2); + + @$pb.TagNumber(3) + $core.String get color => $_getSZ(2); + @$pb.TagNumber(3) + set color($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasColor() => $_has(2); + @$pb.TagNumber(3) + void clearColor() => clearField(3); +} + +class NumberDescription extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'NumberDescription', createEmptyInstance: create) + ..e(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'money', $pb.PbFieldType.OE, defaultOrMaker: MoneySymbol.CNY, valueOf: MoneySymbol.valueOf, enumValues: MoneySymbol.values) + ..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'scale', $pb.PbFieldType.OU3) + ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'symbol') + ..aOB(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'signPositive') + ..aOS(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name') + ..hasRequiredFields = false + ; + + NumberDescription._() : super(); + factory NumberDescription({ + MoneySymbol? money, + $core.int? scale, + $core.String? symbol, + $core.bool? signPositive, + $core.String? name, + }) { + final _result = create(); + if (money != null) { + _result.money = money; + } + if (scale != null) { + _result.scale = scale; + } + if (symbol != null) { + _result.symbol = symbol; + } + if (signPositive != null) { + _result.signPositive = signPositive; + } + if (name != null) { + _result.name = name; + } + return _result; + } + factory NumberDescription.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory NumberDescription.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') + NumberDescription clone() => NumberDescription()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + NumberDescription copyWith(void Function(NumberDescription) updates) => super.copyWith((message) => updates(message as NumberDescription)) as NumberDescription; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static NumberDescription create() => NumberDescription._(); + NumberDescription createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static NumberDescription getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static NumberDescription? _defaultInstance; + + @$pb.TagNumber(1) + MoneySymbol get money => $_getN(0); + @$pb.TagNumber(1) + set money(MoneySymbol v) { setField(1, v); } + @$pb.TagNumber(1) + $core.bool hasMoney() => $_has(0); + @$pb.TagNumber(1) + void clearMoney() => clearField(1); + + @$pb.TagNumber(2) + $core.int get scale => $_getIZ(1); + @$pb.TagNumber(2) + set scale($core.int v) { $_setUnsignedInt32(1, v); } + @$pb.TagNumber(2) + $core.bool hasScale() => $_has(1); + @$pb.TagNumber(2) + void clearScale() => clearField(2); + + @$pb.TagNumber(3) + $core.String get symbol => $_getSZ(2); + @$pb.TagNumber(3) + set symbol($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasSymbol() => $_has(2); + @$pb.TagNumber(3) + void clearSymbol() => clearField(3); + + @$pb.TagNumber(4) + $core.bool get signPositive => $_getBF(3); + @$pb.TagNumber(4) + set signPositive($core.bool v) { $_setBool(3, v); } + @$pb.TagNumber(4) + $core.bool hasSignPositive() => $_has(3); + @$pb.TagNumber(4) + void clearSignPositive() => clearField(4); + + @$pb.TagNumber(5) + $core.String get name => $_getSZ(4); + @$pb.TagNumber(5) + set name($core.String v) { $_setString(4, v); } + @$pb.TagNumber(5) + $core.bool hasName() => $_has(4); + @$pb.TagNumber(5) + void clearName() => clearField(5); +} + diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/type_options.pbenum.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/type_options.pbenum.dart new file mode 100644 index 0000000000..c3815cf5e1 --- /dev/null +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/type_options.pbenum.dart @@ -0,0 +1,62 @@ +/// +// Generated code. Do not modify. +// source: type_options.proto +// +// @dart = 2.12 +// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields + +// ignore_for_file: UNDEFINED_SHOWN_NAME +import 'dart:core' as $core; +import 'package:protobuf/protobuf.dart' as $pb; + +class DateFormat extends $pb.ProtobufEnum { + static const DateFormat Local = DateFormat._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Local'); + static const DateFormat US = DateFormat._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'US'); + static const DateFormat ISO = DateFormat._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ISO'); + static const DateFormat Friendly = DateFormat._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Friendly'); + + static const $core.List values = [ + Local, + US, + ISO, + Friendly, + ]; + + static final $core.Map<$core.int, DateFormat> _byValue = $pb.ProtobufEnum.initByValue(values); + static DateFormat? valueOf($core.int value) => _byValue[value]; + + const DateFormat._($core.int v, $core.String n) : super(v, n); +} + +class TimeFormat extends $pb.ProtobufEnum { + static const TimeFormat TwelveHour = TimeFormat._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'TwelveHour'); + static const TimeFormat TwentyFourHour = TimeFormat._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'TwentyFourHour'); + + static const $core.List values = [ + TwelveHour, + TwentyFourHour, + ]; + + static final $core.Map<$core.int, TimeFormat> _byValue = $pb.ProtobufEnum.initByValue(values); + static TimeFormat? valueOf($core.int value) => _byValue[value]; + + const TimeFormat._($core.int v, $core.String n) : super(v, n); +} + +class MoneySymbol extends $pb.ProtobufEnum { + static const MoneySymbol CNY = MoneySymbol._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CNY'); + static const MoneySymbol EUR = MoneySymbol._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'EUR'); + static const MoneySymbol USD = MoneySymbol._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'USD'); + + static const $core.List values = [ + CNY, + EUR, + USD, + ]; + + static final $core.Map<$core.int, MoneySymbol> _byValue = $pb.ProtobufEnum.initByValue(values); + static MoneySymbol? valueOf($core.int value) => _byValue[value]; + + const MoneySymbol._($core.int v, $core.String n) : super(v, n); +} + diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/type_options.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/type_options.pbjson.dart new file mode 100644 index 0000000000..71e0cf884b --- /dev/null +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/type_options.pbjson.dart @@ -0,0 +1,125 @@ +/// +// Generated code. Do not modify. +// source: type_options.proto +// +// @dart = 2.12 +// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package + +import 'dart:core' as $core; +import 'dart:convert' as $convert; +import 'dart:typed_data' as $typed_data; +@$core.Deprecated('Use dateFormatDescriptor instead') +const DateFormat$json = const { + '1': 'DateFormat', + '2': const [ + const {'1': 'Local', '2': 0}, + const {'1': 'US', '2': 1}, + const {'1': 'ISO', '2': 2}, + const {'1': 'Friendly', '2': 3}, + ], +}; + +/// Descriptor for `DateFormat`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List dateFormatDescriptor = $convert.base64Decode('CgpEYXRlRm9ybWF0EgkKBUxvY2FsEAASBgoCVVMQARIHCgNJU08QAhIMCghGcmllbmRseRAD'); +@$core.Deprecated('Use timeFormatDescriptor instead') +const TimeFormat$json = const { + '1': 'TimeFormat', + '2': const [ + const {'1': 'TwelveHour', '2': 0}, + const {'1': 'TwentyFourHour', '2': 1}, + ], +}; + +/// Descriptor for `TimeFormat`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List timeFormatDescriptor = $convert.base64Decode('CgpUaW1lRm9ybWF0Eg4KClR3ZWx2ZUhvdXIQABISCg5Ud2VudHlGb3VySG91chAB'); +@$core.Deprecated('Use moneySymbolDescriptor instead') +const MoneySymbol$json = const { + '1': 'MoneySymbol', + '2': const [ + const {'1': 'CNY', '2': 0}, + const {'1': 'EUR', '2': 1}, + const {'1': 'USD', '2': 2}, + ], +}; + +/// Descriptor for `MoneySymbol`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List moneySymbolDescriptor = $convert.base64Decode('CgtNb25leVN5bWJvbBIHCgNDTlkQABIHCgNFVVIQARIHCgNVU0QQAg=='); +@$core.Deprecated('Use richTextDescriptionDescriptor instead') +const RichTextDescription$json = const { + '1': 'RichTextDescription', + '2': const [ + const {'1': 'format', '3': 1, '4': 1, '5': 9, '10': 'format'}, + ], +}; + +/// Descriptor for `RichTextDescription`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List richTextDescriptionDescriptor = $convert.base64Decode('ChNSaWNoVGV4dERlc2NyaXB0aW9uEhYKBmZvcm1hdBgBIAEoCVIGZm9ybWF0'); +@$core.Deprecated('Use checkboxDescriptionDescriptor instead') +const CheckboxDescription$json = const { + '1': 'CheckboxDescription', + '2': const [ + const {'1': 'is_selected', '3': 1, '4': 1, '5': 8, '10': 'isSelected'}, + ], +}; + +/// Descriptor for `CheckboxDescription`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List checkboxDescriptionDescriptor = $convert.base64Decode('ChNDaGVja2JveERlc2NyaXB0aW9uEh8KC2lzX3NlbGVjdGVkGAEgASgIUgppc1NlbGVjdGVk'); +@$core.Deprecated('Use dateDescriptionDescriptor instead') +const DateDescription$json = const { + '1': 'DateDescription', + '2': const [ + const {'1': 'date_format', '3': 1, '4': 1, '5': 14, '6': '.DateFormat', '10': 'dateFormat'}, + const {'1': 'time_format', '3': 2, '4': 1, '5': 14, '6': '.TimeFormat', '10': 'timeFormat'}, + ], +}; + +/// Descriptor for `DateDescription`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List dateDescriptionDescriptor = $convert.base64Decode('Cg9EYXRlRGVzY3JpcHRpb24SLAoLZGF0ZV9mb3JtYXQYASABKA4yCy5EYXRlRm9ybWF0UgpkYXRlRm9ybWF0EiwKC3RpbWVfZm9ybWF0GAIgASgOMgsuVGltZUZvcm1hdFIKdGltZUZvcm1hdA=='); +@$core.Deprecated('Use singleSelectDescriptionDescriptor instead') +const SingleSelectDescription$json = const { + '1': 'SingleSelectDescription', + '2': const [ + const {'1': 'options', '3': 1, '4': 3, '5': 11, '6': '.SelectOption', '10': 'options'}, + const {'1': 'disable_color', '3': 2, '4': 1, '5': 8, '10': 'disableColor'}, + ], +}; + +/// Descriptor for `SingleSelectDescription`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List singleSelectDescriptionDescriptor = $convert.base64Decode('ChdTaW5nbGVTZWxlY3REZXNjcmlwdGlvbhInCgdvcHRpb25zGAEgAygLMg0uU2VsZWN0T3B0aW9uUgdvcHRpb25zEiMKDWRpc2FibGVfY29sb3IYAiABKAhSDGRpc2FibGVDb2xvcg=='); +@$core.Deprecated('Use multiSelectDescriptionDescriptor instead') +const MultiSelectDescription$json = const { + '1': 'MultiSelectDescription', + '2': const [ + const {'1': 'options', '3': 1, '4': 3, '5': 11, '6': '.SelectOption', '10': 'options'}, + const {'1': 'disable_color', '3': 2, '4': 1, '5': 8, '10': 'disableColor'}, + ], +}; + +/// Descriptor for `MultiSelectDescription`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List multiSelectDescriptionDescriptor = $convert.base64Decode('ChZNdWx0aVNlbGVjdERlc2NyaXB0aW9uEicKB29wdGlvbnMYASADKAsyDS5TZWxlY3RPcHRpb25SB29wdGlvbnMSIwoNZGlzYWJsZV9jb2xvchgCIAEoCFIMZGlzYWJsZUNvbG9y'); +@$core.Deprecated('Use selectOptionDescriptor instead') +const SelectOption$json = const { + '1': 'SelectOption', + '2': const [ + const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, + const {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'}, + const {'1': 'color', '3': 3, '4': 1, '5': 9, '10': 'color'}, + ], +}; + +/// Descriptor for `SelectOption`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List selectOptionDescriptor = $convert.base64Decode('CgxTZWxlY3RPcHRpb24SDgoCaWQYASABKAlSAmlkEhIKBG5hbWUYAiABKAlSBG5hbWUSFAoFY29sb3IYAyABKAlSBWNvbG9y'); +@$core.Deprecated('Use numberDescriptionDescriptor instead') +const NumberDescription$json = const { + '1': 'NumberDescription', + '2': const [ + const {'1': 'money', '3': 1, '4': 1, '5': 14, '6': '.MoneySymbol', '10': 'money'}, + const {'1': 'scale', '3': 2, '4': 1, '5': 13, '10': 'scale'}, + const {'1': 'symbol', '3': 3, '4': 1, '5': 9, '10': 'symbol'}, + const {'1': 'sign_positive', '3': 4, '4': 1, '5': 8, '10': 'signPositive'}, + const {'1': 'name', '3': 5, '4': 1, '5': 9, '10': 'name'}, + ], +}; + +/// Descriptor for `NumberDescription`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List numberDescriptionDescriptor = $convert.base64Decode('ChFOdW1iZXJEZXNjcmlwdGlvbhIiCgVtb25leRgBIAEoDjIMLk1vbmV5U3ltYm9sUgVtb25leRIUCgVzY2FsZRgCIAEoDVIFc2NhbGUSFgoGc3ltYm9sGAMgASgJUgZzeW1ib2wSIwoNc2lnbl9wb3NpdGl2ZRgEIAEoCFIMc2lnblBvc2l0aXZlEhIKBG5hbWUYBSABKAlSBG5hbWU='); diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/type_options.pbserver.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/type_options.pbserver.dart new file mode 100644 index 0000000000..b46de00bf5 --- /dev/null +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/type_options.pbserver.dart @@ -0,0 +1,9 @@ +/// +// Generated code. Do not modify. +// source: type_options.proto +// +// @dart = 2.12 +// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package + +export 'type_options.pb.dart'; + diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 6b22e06e0a..5ae075afa3 100755 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -1059,8 +1059,10 @@ dependencies = [ "flowy-database", "flowy-derive", "flowy-error", + "flowy-grid", "flowy-grid-data-model", "flowy-sync", + "flowy-test", "lazy_static", "lib-dispatch", "lib-infra", diff --git a/frontend/rust-lib/flowy-block/src/editor.rs b/frontend/rust-lib/flowy-block/src/editor.rs index b5e9e81810..8622c60730 100644 --- a/frontend/rust-lib/flowy-block/src/editor.rs +++ b/frontend/rust-lib/flowy-block/src/editor.rs @@ -39,7 +39,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::(Some(cloud_service)).await?; let delta = document_info.delta()?; let rev_manager = Arc::new(rev_manager); let doc_id = doc_id.to_string(); @@ -191,17 +191,9 @@ fn spawn_edit_queue( #[cfg(feature = "flowy_unit_test")] impl ClientTextBlockEditor { - pub async fn doc_json(&self) -> FlowyResult { - let (ret, rx) = oneshot::channel::>(); - let msg = EditorCommand::ReadDeltaStr { ret }; - let _ = self.edit_cmd_tx.send(msg).await; - let s = rx.await.map_err(internal_error)??; - Ok(s) - } - - pub async fn doc_delta(&self) -> FlowyResult { + pub async fn text_block_delta(&self) -> FlowyResult { let (ret, rx) = oneshot::channel::>(); - let msg = EditorCommand::ReadBlockDelta { ret }; + let msg = EditorCommand::ReadDelta { ret }; let _ = self.edit_cmd_tx.send(msg).await; let delta = rx.await.map_err(internal_error)??; Ok(delta) diff --git a/frontend/rust-lib/flowy-block/src/queue.rs b/frontend/rust-lib/flowy-block/src/queue.rs index 087ed9d189..94955f38d8 100644 --- a/frontend/rust-lib/flowy-block/src/queue.rs +++ b/frontend/rust-lib/flowy-block/src/queue.rs @@ -166,7 +166,7 @@ impl EditBlockQueue { let data = self.document.read().await.delta_str(); let _ = ret.send(Ok(data)); } - EditorCommand::ReadBlockDelta { ret } => { + EditorCommand::ReadDelta { ret } => { let delta = self.document.read().await.delta().clone(); let _ = ret.send(Ok(delta)); } @@ -256,7 +256,7 @@ pub(crate) enum EditorCommand { ret: Ret, }, #[allow(dead_code)] - ReadBlockDelta { + ReadDelta { ret: Ret, }, } @@ -277,7 +277,7 @@ impl std::fmt::Debug for EditorCommand { EditorCommand::Undo { .. } => "Undo", EditorCommand::Redo { .. } => "Redo", EditorCommand::ReadDeltaStr { .. } => "ReadDeltaStr", - EditorCommand::ReadBlockDelta { .. } => "ReadDocumentAsDelta", + EditorCommand::ReadDelta { .. } => "ReadDocumentAsDelta", }; f.write_str(s) } diff --git a/frontend/rust-lib/flowy-block/tests/document/mod.rs b/frontend/rust-lib/flowy-block/tests/document/mod.rs index 252787043a..8aa4c774ed 100644 --- a/frontend/rust-lib/flowy-block/tests/document/mod.rs +++ b/frontend/rust-lib/flowy-block/tests/document/mod.rs @@ -1,2 +1,2 @@ -mod document_test; -mod edit_script; +mod script; +mod text_block_test; diff --git a/frontend/rust-lib/flowy-block/tests/document/edit_script.rs b/frontend/rust-lib/flowy-block/tests/document/script.rs similarity index 91% rename from frontend/rust-lib/flowy-block/tests/document/edit_script.rs rename to frontend/rust-lib/flowy-block/tests/document/script.rs index c30ccd8d46..87f52a3e46 100644 --- a/frontend/rust-lib/flowy-block/tests/document/edit_script.rs +++ b/frontend/rust-lib/flowy-block/tests/document/script.rs @@ -17,16 +17,16 @@ pub enum EditorScript { AssertJson(&'static str), } -pub struct EditorTest { +pub struct TextBlockEditorTest { pub sdk: FlowySDKTest, pub editor: Arc, } -impl EditorTest { +impl TextBlockEditorTest { pub async fn new() -> Self { let sdk = FlowySDKTest::default(); let _ = sdk.init_user().await; - let test = ViewTest::new(&sdk).await; + let test = ViewTest::new_grid_view(&sdk).await; let editor = sdk.text_block_manager.open_block(&test.view.id).await.unwrap(); Self { sdk, editor } } @@ -41,8 +41,6 @@ impl EditorTest { let rev_manager = self.editor.rev_manager(); let cache = rev_manager.revision_cache().await; let _user_id = self.sdk.user_session.user_id().unwrap(); - // let ws_manager = self.sdk.ws_conn.clone(); - // let token = self.sdk.user_session.token().unwrap(); match script { EditorScript::InsertText(s, offset) => { @@ -74,7 +72,7 @@ impl EditorTest { } EditorScript::AssertJson(expected) => { let expected_delta: RichTextDelta = serde_json::from_str(expected).unwrap(); - let delta = self.editor.doc_delta().await.unwrap(); + let delta = self.editor.text_block_delta().await.unwrap(); if expected_delta != delta { eprintln!("✅ expect: {}", expected,); eprintln!("❌ receive: {}", delta.to_delta_str()); diff --git a/frontend/rust-lib/flowy-block/tests/document/document_test.rs b/frontend/rust-lib/flowy-block/tests/document/text_block_test.rs similarity index 69% rename from frontend/rust-lib/flowy-block/tests/document/document_test.rs rename to frontend/rust-lib/flowy-block/tests/document/text_block_test.rs index df2282d01f..93fb957cdb 100644 --- a/frontend/rust-lib/flowy-block/tests/document/document_test.rs +++ b/frontend/rust-lib/flowy-block/tests/document/text_block_test.rs @@ -1,9 +1,9 @@ -use crate::document::edit_script::{EditorScript::*, *}; +use crate::document::script::{EditorScript::*, *}; use flowy_sync::disk::RevisionState; use lib_ot::core::{count_utf16_code_units, Interval}; #[tokio::test] -async fn document_sync_current_rev_id_check() { +async fn text_block_sync_current_rev_id_check() { let scripts = vec![ InsertText("1", 0), AssertCurrentRevId(1), @@ -14,11 +14,11 @@ async fn document_sync_current_rev_id_check() { AssertNextSyncRevId(None), AssertJson(r#"[{"insert":"123\n"}]"#), ]; - EditorTest::new().await.run_scripts(scripts).await; + TextBlockEditorTest::new().await.run_scripts(scripts).await; } #[tokio::test] -async fn document_sync_state_check() { +async fn text_block_sync_state_check() { let scripts = vec![ InsertText("1", 0), InsertText("2", 1), @@ -28,11 +28,11 @@ async fn document_sync_state_check() { AssertRevisionState(3, RevisionState::Ack), AssertJson(r#"[{"insert":"123\n"}]"#), ]; - EditorTest::new().await.run_scripts(scripts).await; + TextBlockEditorTest::new().await.run_scripts(scripts).await; } #[tokio::test] -async fn document_sync_insert_test() { +async fn text_block_sync_insert_test() { let scripts = vec![ InsertText("1", 0), InsertText("2", 1), @@ -40,11 +40,11 @@ async fn document_sync_insert_test() { AssertJson(r#"[{"insert":"123\n"}]"#), AssertNextSyncRevId(None), ]; - EditorTest::new().await.run_scripts(scripts).await; + TextBlockEditorTest::new().await.run_scripts(scripts).await; } #[tokio::test] -async fn document_sync_insert_in_chinese() { +async fn text_block_sync_insert_in_chinese() { let s = "好".to_owned(); let offset = count_utf16_code_units(&s); let scripts = vec![ @@ -52,11 +52,11 @@ async fn document_sync_insert_in_chinese() { InsertText("好", offset), AssertJson(r#"[{"insert":"你好\n"}]"#), ]; - EditorTest::new().await.run_scripts(scripts).await; + TextBlockEditorTest::new().await.run_scripts(scripts).await; } #[tokio::test] -async fn document_sync_insert_with_emoji() { +async fn text_block_sync_insert_with_emoji() { let s = "😁".to_owned(); let offset = count_utf16_code_units(&s); let scripts = vec![ @@ -64,11 +64,11 @@ async fn document_sync_insert_with_emoji() { InsertText("☺️", offset), AssertJson(r#"[{"insert":"😁☺️\n"}]"#), ]; - EditorTest::new().await.run_scripts(scripts).await; + TextBlockEditorTest::new().await.run_scripts(scripts).await; } #[tokio::test] -async fn document_sync_delete_in_english() { +async fn text_block_sync_delete_in_english() { let scripts = vec![ InsertText("1", 0), InsertText("2", 1), @@ -76,11 +76,11 @@ async fn document_sync_delete_in_english() { Delete(Interval::new(0, 2)), AssertJson(r#"[{"insert":"3\n"}]"#), ]; - EditorTest::new().await.run_scripts(scripts).await; + TextBlockEditorTest::new().await.run_scripts(scripts).await; } #[tokio::test] -async fn document_sync_delete_in_chinese() { +async fn text_block_sync_delete_in_chinese() { let s = "好".to_owned(); let offset = count_utf16_code_units(&s); let scripts = vec![ @@ -89,11 +89,11 @@ async fn document_sync_delete_in_chinese() { Delete(Interval::new(0, offset)), AssertJson(r#"[{"insert":"好\n"}]"#), ]; - EditorTest::new().await.run_scripts(scripts).await; + TextBlockEditorTest::new().await.run_scripts(scripts).await; } #[tokio::test] -async fn document_sync_replace_test() { +async fn text_block_sync_replace_test() { let scripts = vec![ InsertText("1", 0), InsertText("2", 1), @@ -101,5 +101,5 @@ async fn document_sync_replace_test() { Replace(Interval::new(0, 3), "abc"), AssertJson(r#"[{"insert":"abc\n"}]"#), ]; - EditorTest::new().await.run_scripts(scripts).await; + TextBlockEditorTest::new().await.run_scripts(scripts).await; } 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 0a5dd7f794..7aaa1de7cd 100644 --- a/frontend/rust-lib/flowy-folder/src/services/folder_editor.rs +++ b/frontend/rust-lib/flowy-folder/src/services/folder_editor.rs @@ -38,7 +38,7 @@ impl ClientFolderEditor { 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::(Some(cloud)).await?)); let rev_manager = Arc::new(rev_manager); let ws_manager = make_folder_ws_manager( user_id, diff --git a/frontend/rust-lib/flowy-folder/tests/workspace/folder_test.rs b/frontend/rust-lib/flowy-folder/tests/workspace/folder_test.rs index 7dd5b66e2f..9f9bae4a82 100644 --- a/frontend/rust-lib/flowy-folder/tests/workspace/folder_test.rs +++ b/frontend/rust-lib/flowy-folder/tests/workspace/folder_test.rs @@ -1,6 +1,7 @@ use crate::script::{invalid_workspace_name_test_case, FolderScript::*, FolderTest}; use flowy_folder::entities::workspace::CreateWorkspacePayload; +use flowy_folder_data_model::entities::view::ViewDataType; use flowy_sync::disk::RevisionState; use flowy_test::{event_builder::*, FlowySDKTest}; @@ -136,10 +137,12 @@ async fn app_create_with_view() { CreateView { name: "View A".to_owned(), desc: "View A description".to_owned(), + data_type: ViewDataType::TextBlock, }, CreateView { - name: "View B".to_owned(), - desc: "View B description".to_owned(), + name: "Grid".to_owned(), + desc: "Grid description".to_owned(), + data_type: ViewDataType::Grid, }, ReadApp(app.id), ]) @@ -148,7 +151,7 @@ async fn app_create_with_view() { app = test.app.clone(); assert_eq!(app.belongings.len(), 3); assert_eq!(app.belongings[1].name, "View A"); - assert_eq!(app.belongings[2].name, "View B") + assert_eq!(app.belongings[2].name, "Grid") } #[tokio::test] @@ -198,10 +201,12 @@ async fn view_delete_all() { CreateView { name: "View A".to_owned(), desc: "View A description".to_owned(), + data_type: ViewDataType::TextBlock, }, CreateView { - name: "View B".to_owned(), - desc: "View B description".to_owned(), + name: "Grid".to_owned(), + desc: "Grid description".to_owned(), + data_type: ViewDataType::Grid, }, ReadApp(app.id.clone()), ]) @@ -229,6 +234,7 @@ async fn view_delete_all_permanent() { CreateView { name: "View A".to_owned(), desc: "View A description".to_owned(), + data_type: ViewDataType::TextBlock, }, ReadApp(app.id.clone()), ]) @@ -327,6 +333,7 @@ async fn folder_sync_revision_with_new_view() { CreateView { name: view_name.clone(), desc: view_desc.clone(), + data_type: ViewDataType::TextBlock, }, AssertCurrentRevId(3), AssertNextSyncRevId(Some(3)), diff --git a/frontend/rust-lib/flowy-folder/tests/workspace/helper.rs b/frontend/rust-lib/flowy-folder/tests/workspace/helper.rs deleted file mode 100644 index 93e2ff2b7f..0000000000 --- a/frontend/rust-lib/flowy-folder/tests/workspace/helper.rs +++ /dev/null @@ -1,212 +0,0 @@ -use flowy_collaboration::entities::text_block_info::TextBlockInfo; -use flowy_folder::event_map::FolderEvent::*; -use flowy_folder_data_model::entities::view::{RepeatedViewId, ViewId}; -use flowy_folder_data_model::entities::workspace::WorkspaceId; -use flowy_folder_data_model::entities::{ - app::{App, AppId, CreateAppPayload, UpdateAppPayload}, - trash::{RepeatedTrash, TrashId, TrashType}, - view::{CreateViewPayload, UpdateViewPayload, View, ViewDataType}, - workspace::{CreateWorkspacePayload, RepeatedWorkspace, Workspace}, -}; -use flowy_test::{event_builder::*, FlowySDKTest}; - -pub async fn create_workspace(sdk: &FlowySDKTest, name: &str, desc: &str) -> Workspace { - let request = CreateWorkspacePayload { - name: name.to_owned(), - desc: desc.to_owned(), - }; - - let workspace = FolderEventBuilder::new(sdk.clone()) - .event(CreateWorkspace) - .payload(request) - .async_send() - .await - .parse::(); - workspace -} - -pub async fn read_workspace(sdk: &FlowySDKTest, workspace_id: Option) -> Vec { - let request = WorkspaceId { value: workspace_id }; - let repeated_workspace = FolderEventBuilder::new(sdk.clone()) - .event(ReadWorkspaces) - .payload(request.clone()) - .async_send() - .await - .parse::(); - - let workspaces; - if let Some(workspace_id) = &request.value { - workspaces = repeated_workspace - .into_inner() - .into_iter() - .filter(|workspace| &workspace.id == workspace_id) - .collect::>(); - debug_assert_eq!(workspaces.len(), 1); - } else { - workspaces = repeated_workspace.items; - } - - workspaces -} - -pub async fn create_app(sdk: &FlowySDKTest, workspace_id: &str, name: &str, desc: &str) -> App { - let create_app_request = CreateAppPayload { - workspace_id: workspace_id.to_owned(), - name: name.to_string(), - desc: desc.to_string(), - color_style: Default::default(), - }; - - let app = FolderEventBuilder::new(sdk.clone()) - .event(CreateApp) - .payload(create_app_request) - .async_send() - .await - .parse::(); - app -} - -pub async fn read_app(sdk: &FlowySDKTest, app_id: &str) -> App { - let request = AppId { - value: app_id.to_owned(), - }; - - let app = FolderEventBuilder::new(sdk.clone()) - .event(ReadApp) - .payload(request) - .async_send() - .await - .parse::(); - - app -} - -pub async fn update_app(sdk: &FlowySDKTest, app_id: &str, name: Option, desc: Option) { - let request = UpdateAppPayload { - app_id: app_id.to_string(), - name, - desc, - color_style: None, - is_trash: None, - }; - - FolderEventBuilder::new(sdk.clone()) - .event(UpdateApp) - .payload(request) - .async_send() - .await; -} - -pub async fn delete_app(sdk: &FlowySDKTest, app_id: &str) { - let request = AppId { - value: app_id.to_string(), - }; - - FolderEventBuilder::new(sdk.clone()) - .event(DeleteApp) - .payload(request) - .async_send() - .await; -} - -pub async fn create_view(sdk: &FlowySDKTest, app_id: &str, name: &str, desc: &str, data_type: ViewDataType) -> View { - let request = CreateViewPayload { - belong_to_id: app_id.to_string(), - name: name.to_string(), - desc: desc.to_string(), - thumbnail: None, - data_type, - ext_data: "".to_string(), - plugin_type: 0, - }; - let view = FolderEventBuilder::new(sdk.clone()) - .event(CreateView) - .payload(request) - .async_send() - .await - .parse::(); - view -} - -pub async fn read_view(sdk: &FlowySDKTest, view_id: &str) -> View { - let view_id: ViewId = view_id.into(); - FolderEventBuilder::new(sdk.clone()) - .event(ReadView) - .payload(view_id) - .async_send() - .await - .parse::() -} - -pub async fn update_view(sdk: &FlowySDKTest, view_id: &str, name: Option, desc: Option) { - let request = UpdateViewPayload { - view_id: view_id.to_string(), - name, - desc, - thumbnail: None, - }; - FolderEventBuilder::new(sdk.clone()) - .event(UpdateView) - .payload(request) - .async_send() - .await; -} - -pub async fn delete_view(sdk: &FlowySDKTest, view_ids: Vec) { - let request = RepeatedViewId { items: view_ids }; - FolderEventBuilder::new(sdk.clone()) - .event(DeleteView) - .payload(request) - .async_send() - .await; -} - -#[allow(dead_code)] -pub async fn set_latest_view(sdk: &FlowySDKTest, view_id: &str) -> TextBlockInfo { - let view_id: ViewId = view_id.into(); - FolderEventBuilder::new(sdk.clone()) - .event(SetLatestView) - .payload(view_id) - .async_send() - .await - .parse::() -} - -pub async fn read_trash(sdk: &FlowySDKTest) -> RepeatedTrash { - FolderEventBuilder::new(sdk.clone()) - .event(ReadTrash) - .async_send() - .await - .parse::() -} - -pub async fn restore_app_from_trash(sdk: &FlowySDKTest, app_id: &str) { - let id = TrashId { - id: app_id.to_owned(), - ty: TrashType::TrashApp, - }; - FolderEventBuilder::new(sdk.clone()) - .event(PutbackTrash) - .payload(id) - .async_send() - .await; -} - -pub async fn restore_view_from_trash(sdk: &FlowySDKTest, view_id: &str) { - let id = TrashId { - id: view_id.to_owned(), - ty: TrashType::TrashView, - }; - FolderEventBuilder::new(sdk.clone()) - .event(PutbackTrash) - .payload(id) - .async_send() - .await; -} - -pub async fn delete_all_trash(sdk: &FlowySDKTest) { - FolderEventBuilder::new(sdk.clone()) - .event(DeleteAllTrash) - .async_send() - .await; -} diff --git a/frontend/rust-lib/flowy-folder/tests/workspace/main.rs b/frontend/rust-lib/flowy-folder/tests/workspace/main.rs index 553449ee4b..ff48b349e1 100644 --- a/frontend/rust-lib/flowy-folder/tests/workspace/main.rs +++ b/frontend/rust-lib/flowy-folder/tests/workspace/main.rs @@ -1,3 +1,2 @@ mod folder_test; -mod helper; mod script; diff --git a/frontend/rust-lib/flowy-folder/tests/workspace/script.rs b/frontend/rust-lib/flowy-folder/tests/workspace/script.rs index 7674ca0a54..9c2c8ecbed 100644 --- a/frontend/rust-lib/flowy-folder/tests/workspace/script.rs +++ b/frontend/rust-lib/flowy-folder/tests/workspace/script.rs @@ -1,39 +1,63 @@ -use crate::helper::*; - +use flowy_collaboration::entities::text_block_info::TextBlockInfo; +use flowy_folder::event_map::FolderEvent::*; use flowy_folder::{errors::ErrorCode, services::folder_editor::ClientFolderEditor}; +use flowy_folder_data_model::entities::view::{RepeatedViewId, ViewId}; +use flowy_folder_data_model::entities::workspace::WorkspaceId; use flowy_folder_data_model::entities::{ app::{App, RepeatedApp}, trash::Trash, view::{RepeatedView, View, ViewDataType}, workspace::Workspace, }; +use flowy_folder_data_model::entities::{ + app::{AppId, CreateAppPayload, UpdateAppPayload}, + trash::{RepeatedTrash, TrashId, TrashType}, + view::{CreateViewPayload, UpdateViewPayload}, + workspace::{CreateWorkspacePayload, RepeatedWorkspace}, +}; use flowy_sync::disk::RevisionState; use flowy_sync::REVISION_WRITE_INTERVAL_IN_MILLIS; -use flowy_test::FlowySDKTest; +use flowy_test::{event_builder::*, FlowySDKTest}; use std::{sync::Arc, time::Duration}; use tokio::time::sleep; pub enum FolderScript { // Workspace ReadAllWorkspaces, - CreateWorkspace { name: String, desc: String }, + CreateWorkspace { + name: String, + desc: String, + }, AssertWorkspaceJson(String), AssertWorkspace(Workspace), ReadWorkspace(Option), // App - CreateApp { name: String, desc: String }, + CreateApp { + name: String, + desc: String, + }, AssertAppJson(String), AssertApp(App), ReadApp(String), - UpdateApp { name: Option, desc: Option }, + UpdateApp { + name: Option, + desc: Option, + }, DeleteApp, // View - CreateView { name: String, desc: String }, + CreateView { + name: String, + desc: String, + data_type: ViewDataType, + }, AssertView(View), ReadView(String), - UpdateView { name: Option, desc: Option }, + UpdateView { + name: Option, + desc: Option, + }, DeleteView, DeleteViews(Vec), @@ -46,7 +70,10 @@ pub enum FolderScript { // Sync AssertCurrentRevId(i64), AssertNextSyncRevId(Option), - AssertRevisionState { rev_id: i64, state: RevisionState }, + AssertRevisionState { + rev_id: i64, + state: RevisionState, + }, } pub struct FolderTest { @@ -148,8 +175,8 @@ impl FolderTest { delete_app(sdk, &self.app.id).await; } - FolderScript::CreateView { name, desc } => { - let view = create_view(sdk, &self.app.id, &name, &desc, ViewDataType::TextBlock).await; + FolderScript::CreateView { name, desc, data_type } => { + let view = create_view(sdk, &self.app.id, &name, &desc, data_type).await; self.view = view; } FolderScript::AssertView(view) => { @@ -216,3 +243,204 @@ pub fn invalid_workspace_name_test_case() -> Vec<(String, ErrorCode)> { ("1234".repeat(100), ErrorCode::WorkspaceNameTooLong), ] } + +pub async fn create_workspace(sdk: &FlowySDKTest, name: &str, desc: &str) -> Workspace { + let request = CreateWorkspacePayload { + name: name.to_owned(), + desc: desc.to_owned(), + }; + + let workspace = FolderEventBuilder::new(sdk.clone()) + .event(CreateWorkspace) + .payload(request) + .async_send() + .await + .parse::(); + workspace +} + +pub async fn read_workspace(sdk: &FlowySDKTest, workspace_id: Option) -> Vec { + let request = WorkspaceId { value: workspace_id }; + let repeated_workspace = FolderEventBuilder::new(sdk.clone()) + .event(ReadWorkspaces) + .payload(request.clone()) + .async_send() + .await + .parse::(); + + let workspaces; + if let Some(workspace_id) = &request.value { + workspaces = repeated_workspace + .into_inner() + .into_iter() + .filter(|workspace| &workspace.id == workspace_id) + .collect::>(); + debug_assert_eq!(workspaces.len(), 1); + } else { + workspaces = repeated_workspace.items; + } + + workspaces +} + +pub async fn create_app(sdk: &FlowySDKTest, workspace_id: &str, name: &str, desc: &str) -> App { + let create_app_request = CreateAppPayload { + workspace_id: workspace_id.to_owned(), + name: name.to_string(), + desc: desc.to_string(), + color_style: Default::default(), + }; + + let app = FolderEventBuilder::new(sdk.clone()) + .event(CreateApp) + .payload(create_app_request) + .async_send() + .await + .parse::(); + app +} + +pub async fn read_app(sdk: &FlowySDKTest, app_id: &str) -> App { + let request = AppId { + value: app_id.to_owned(), + }; + + let app = FolderEventBuilder::new(sdk.clone()) + .event(ReadApp) + .payload(request) + .async_send() + .await + .parse::(); + + app +} + +pub async fn update_app(sdk: &FlowySDKTest, app_id: &str, name: Option, desc: Option) { + let request = UpdateAppPayload { + app_id: app_id.to_string(), + name, + desc, + color_style: None, + is_trash: None, + }; + + FolderEventBuilder::new(sdk.clone()) + .event(UpdateApp) + .payload(request) + .async_send() + .await; +} + +pub async fn delete_app(sdk: &FlowySDKTest, app_id: &str) { + let request = AppId { + value: app_id.to_string(), + }; + + FolderEventBuilder::new(sdk.clone()) + .event(DeleteApp) + .payload(request) + .async_send() + .await; +} + +pub async fn create_view(sdk: &FlowySDKTest, app_id: &str, name: &str, desc: &str, data_type: ViewDataType) -> View { + let request = CreateViewPayload { + belong_to_id: app_id.to_string(), + name: name.to_string(), + desc: desc.to_string(), + thumbnail: None, + data_type, + ext_data: "".to_string(), + plugin_type: 0, + }; + let view = FolderEventBuilder::new(sdk.clone()) + .event(CreateView) + .payload(request) + .async_send() + .await + .parse::(); + view +} + +pub async fn read_view(sdk: &FlowySDKTest, view_id: &str) -> View { + let view_id: ViewId = view_id.into(); + FolderEventBuilder::new(sdk.clone()) + .event(ReadView) + .payload(view_id) + .async_send() + .await + .parse::() +} + +pub async fn update_view(sdk: &FlowySDKTest, view_id: &str, name: Option, desc: Option) { + let request = UpdateViewPayload { + view_id: view_id.to_string(), + name, + desc, + thumbnail: None, + }; + FolderEventBuilder::new(sdk.clone()) + .event(UpdateView) + .payload(request) + .async_send() + .await; +} + +pub async fn delete_view(sdk: &FlowySDKTest, view_ids: Vec) { + let request = RepeatedViewId { items: view_ids }; + FolderEventBuilder::new(sdk.clone()) + .event(DeleteView) + .payload(request) + .async_send() + .await; +} + +#[allow(dead_code)] +pub async fn set_latest_view(sdk: &FlowySDKTest, view_id: &str) -> TextBlockInfo { + let view_id: ViewId = view_id.into(); + FolderEventBuilder::new(sdk.clone()) + .event(SetLatestView) + .payload(view_id) + .async_send() + .await + .parse::() +} + +pub async fn read_trash(sdk: &FlowySDKTest) -> RepeatedTrash { + FolderEventBuilder::new(sdk.clone()) + .event(ReadTrash) + .async_send() + .await + .parse::() +} + +pub async fn restore_app_from_trash(sdk: &FlowySDKTest, app_id: &str) { + let id = TrashId { + id: app_id.to_owned(), + ty: TrashType::TrashApp, + }; + FolderEventBuilder::new(sdk.clone()) + .event(PutbackTrash) + .payload(id) + .async_send() + .await; +} + +pub async fn restore_view_from_trash(sdk: &FlowySDKTest, view_id: &str) { + let id = TrashId { + id: view_id.to_owned(), + ty: TrashType::TrashView, + }; + FolderEventBuilder::new(sdk.clone()) + .event(PutbackTrash) + .payload(id) + .async_send() + .await; +} + +pub async fn delete_all_trash(sdk: &FlowySDKTest) { + FolderEventBuilder::new(sdk.clone()) + .event(DeleteAllTrash) + .async_send() + .await; +} diff --git a/frontend/rust-lib/flowy-grid/Cargo.toml b/frontend/rust-lib/flowy-grid/Cargo.toml index 95a5899f5e..48c7e346a4 100644 --- a/frontend/rust-lib/flowy-grid/Cargo.toml +++ b/frontend/rust-lib/flowy-grid/Cargo.toml @@ -34,10 +34,15 @@ tokio = {version = "1", features = ["sync"]} rayon = "1.5" parking_lot = "0.11" +[dev-dependencies] +flowy-test = { path = "../flowy-test" } +flowy-grid = { path = "../flowy-grid", features = ["flowy_unit_test"]} + [build-dependencies] lib-infra = { path = "../../../shared-lib/lib-infra", features = ["protobuf_file_gen", "proto_gen"] } [features] default = [] -dart = ["lib-infra/dart"] \ No newline at end of file +dart = ["lib-infra/dart"] +flowy_unit_test = ["flowy-sync/flowy_unit_test"] \ No newline at end of file diff --git a/frontend/rust-lib/flowy-grid/Flowy.toml b/frontend/rust-lib/flowy-grid/Flowy.toml index d7c3d8260d..fc5c01c773 100644 --- a/frontend/rust-lib/flowy-grid/Flowy.toml +++ b/frontend/rust-lib/flowy-grid/Flowy.toml @@ -1,3 +1,3 @@ -proto_crates = ["src/event_map.rs", "src/services/cell_data.rs"] +proto_crates = ["src/event_map.rs", "src/services/field/type_options.rs"] event_files = ["src/event_map.rs"] \ No newline at end of file diff --git a/frontend/rust-lib/flowy-grid/src/lib.rs b/frontend/rust-lib/flowy-grid/src/lib.rs index 262fe1d1c2..526e1e4267 100644 --- a/frontend/rust-lib/flowy-grid/src/lib.rs +++ b/frontend/rust-lib/flowy-grid/src/lib.rs @@ -7,3 +7,4 @@ pub mod manager; mod protobuf; pub mod services; +pub mod util; diff --git a/frontend/rust-lib/flowy-grid/src/macros.rs b/frontend/rust-lib/flowy-grid/src/macros.rs index 043dcccb44..9f2fedddc7 100644 --- a/frontend/rust-lib/flowy-grid/src/macros.rs +++ b/frontend/rust-lib/flowy-grid/src/macros.rs @@ -1,29 +1,17 @@ #[macro_export] -macro_rules! impl_any_data { +macro_rules! impl_from_and_to_type_option { ($target: ident, $field_type:expr) => { - impl_field_type_data_from_field!($target); - impl_field_type_data_from_field_type_option!($target); - impl_type_option_from_field_data!($target, $field_type); + impl_from_field_type_option!($target); + impl_to_field_type_option!($target, $field_type); }; } #[macro_export] -macro_rules! impl_field_type_data_from_field { +macro_rules! impl_from_field_type_option { ($target: ident) => { impl std::convert::From<&Field> for $target { fn from(field: &Field) -> $target { - $target::from(&field.type_options) - } - } - }; -} - -#[macro_export] -macro_rules! impl_field_type_data_from_field_type_option { - ($target: ident) => { - impl std::convert::From<&AnyData> for $target { - fn from(any_data: &AnyData) -> $target { - match $target::try_from(Bytes::from(any_data.value.clone())) { + match $target::try_from(Bytes::from(field.type_options.value.clone())) { Ok(obj) => obj, Err(err) => { tracing::error!("{} convert from any data failed, {:?}", stringify!($target), err); @@ -36,26 +24,27 @@ macro_rules! impl_field_type_data_from_field_type_option { } #[macro_export] -macro_rules! impl_type_option_from_field_data { +macro_rules! impl_to_field_type_option { ($target: ident, $field_type:expr) => { impl $target { - pub fn field_type() -> FieldType { + pub fn field_type(&self) -> FieldType { $field_type } } impl std::convert::From<$target> for AnyData { - fn from(field_data: $target) -> Self { - match field_data.try_into() { + fn from(field_description: $target) -> Self { + let field_type = field_description.field_type(); + match field_description.try_into() { Ok(bytes) => { let bytes: Bytes = bytes; - AnyData::from_bytes(&$target::field_type(), bytes) + AnyData::from_bytes(field_type, bytes) } Err(e) => { tracing::error!("Field type data convert to AnyData fail, error: {:?}", e); // it's impossible to fail when unwrapping the default field type data let default_bytes: Bytes = $target::default().try_into().unwrap(); - AnyData::from_bytes(&$target::field_type(), default_bytes) + AnyData::from_bytes(field_type, default_bytes) } } } diff --git a/frontend/rust-lib/flowy-grid/src/manager.rs b/frontend/rust-lib/flowy-grid/src/manager.rs index 05e61b26ce..fc79fcf5c0 100644 --- a/frontend/rust-lib/flowy-grid/src/manager.rs +++ b/frontend/rust-lib/flowy-grid/src/manager.rs @@ -113,7 +113,7 @@ impl GridManager { Ok(grid_editor) } - fn make_grid_rev_manager(&self, grid_id: &str, pool: Arc) -> FlowyResult { + pub fn make_grid_rev_manager(&self, grid_id: &str, pool: Arc) -> FlowyResult { let user_id = self.grid_user.user_id()?; let disk_cache = Arc::new(SQLiteGridRevisionPersistence::new(&user_id, pool)); diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/model/mod.rs b/frontend/rust-lib/flowy-grid/src/protobuf/model/mod.rs index 55aeddc863..d9c75efb74 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/model/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/protobuf/model/mod.rs @@ -1,8 +1,8 @@ #![cfg_attr(rustfmt, rustfmt::skip)] // Auto-generated, do not edit -mod cell_data; -pub use cell_data::*; +mod type_options; +pub use type_options::*; mod event_map; pub use event_map::*; diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/model/cell_data.rs b/frontend/rust-lib/flowy-grid/src/protobuf/model/type_options.rs similarity index 91% rename from frontend/rust-lib/flowy-grid/src/protobuf/model/cell_data.rs rename to frontend/rust-lib/flowy-grid/src/protobuf/model/type_options.rs index d173fe22d0..cda2c556c3 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/model/cell_data.rs +++ b/frontend/rust-lib/flowy-grid/src/protobuf/model/type_options.rs @@ -17,7 +17,7 @@ #![allow(trivial_casts)] #![allow(unused_imports)] #![allow(unused_results)] -//! Generated file from `cell_data.proto` +//! Generated file from `type_options.proto` /// Generated files are compatible only with the same version /// of protobuf runtime. @@ -514,7 +514,7 @@ impl ::protobuf::reflect::ProtobufValue for DateDescription { } #[derive(PartialEq,Clone,Default)] -pub struct SingleSelect { +pub struct SingleSelectDescription { // message fields pub options: ::protobuf::RepeatedField, pub disable_color: bool, @@ -523,14 +523,14 @@ pub struct SingleSelect { pub cached_size: ::protobuf::CachedSize, } -impl<'a> ::std::default::Default for &'a SingleSelect { - fn default() -> &'a SingleSelect { - ::default_instance() +impl<'a> ::std::default::Default for &'a SingleSelectDescription { + fn default() -> &'a SingleSelectDescription { + ::default_instance() } } -impl SingleSelect { - pub fn new() -> SingleSelect { +impl SingleSelectDescription { + pub fn new() -> SingleSelectDescription { ::std::default::Default::default() } @@ -575,7 +575,7 @@ impl SingleSelect { } } -impl ::protobuf::Message for SingleSelect { +impl ::protobuf::Message for SingleSelectDescription { fn is_initialized(&self) -> bool { for v in &self.options { if !v.is_initialized() { @@ -662,8 +662,8 @@ impl ::protobuf::Message for SingleSelect { Self::descriptor_static() } - fn new() -> SingleSelect { - SingleSelect::new() + fn new() -> SingleSelectDescription { + SingleSelectDescription::new() } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { @@ -672,29 +672,29 @@ impl ::protobuf::Message for SingleSelect { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( "options", - |m: &SingleSelect| { &m.options }, - |m: &mut SingleSelect| { &mut m.options }, + |m: &SingleSelectDescription| { &m.options }, + |m: &mut SingleSelectDescription| { &mut m.options }, )); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBool>( "disable_color", - |m: &SingleSelect| { &m.disable_color }, - |m: &mut SingleSelect| { &mut m.disable_color }, + |m: &SingleSelectDescription| { &m.disable_color }, + |m: &mut SingleSelectDescription| { &mut m.disable_color }, )); - ::protobuf::reflect::MessageDescriptor::new_pb_name::( - "SingleSelect", + ::protobuf::reflect::MessageDescriptor::new_pb_name::( + "SingleSelectDescription", fields, file_descriptor_proto() ) }) } - fn default_instance() -> &'static SingleSelect { - static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; - instance.get(SingleSelect::new) + fn default_instance() -> &'static SingleSelectDescription { + static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; + instance.get(SingleSelectDescription::new) } } -impl ::protobuf::Clear for SingleSelect { +impl ::protobuf::Clear for SingleSelectDescription { fn clear(&mut self) { self.options.clear(); self.disable_color = false; @@ -702,20 +702,20 @@ impl ::protobuf::Clear for SingleSelect { } } -impl ::std::fmt::Debug for SingleSelect { +impl ::std::fmt::Debug for SingleSelectDescription { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } -impl ::protobuf::reflect::ProtobufValue for SingleSelect { +impl ::protobuf::reflect::ProtobufValue for SingleSelectDescription { fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { ::protobuf::reflect::ReflectValueRef::Message(self) } } #[derive(PartialEq,Clone,Default)] -pub struct MultiSelect { +pub struct MultiSelectDescription { // message fields pub options: ::protobuf::RepeatedField, pub disable_color: bool, @@ -724,14 +724,14 @@ pub struct MultiSelect { pub cached_size: ::protobuf::CachedSize, } -impl<'a> ::std::default::Default for &'a MultiSelect { - fn default() -> &'a MultiSelect { - ::default_instance() +impl<'a> ::std::default::Default for &'a MultiSelectDescription { + fn default() -> &'a MultiSelectDescription { + ::default_instance() } } -impl MultiSelect { - pub fn new() -> MultiSelect { +impl MultiSelectDescription { + pub fn new() -> MultiSelectDescription { ::std::default::Default::default() } @@ -776,7 +776,7 @@ impl MultiSelect { } } -impl ::protobuf::Message for MultiSelect { +impl ::protobuf::Message for MultiSelectDescription { fn is_initialized(&self) -> bool { for v in &self.options { if !v.is_initialized() { @@ -863,8 +863,8 @@ impl ::protobuf::Message for MultiSelect { Self::descriptor_static() } - fn new() -> MultiSelect { - MultiSelect::new() + fn new() -> MultiSelectDescription { + MultiSelectDescription::new() } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { @@ -873,29 +873,29 @@ impl ::protobuf::Message for MultiSelect { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( "options", - |m: &MultiSelect| { &m.options }, - |m: &mut MultiSelect| { &mut m.options }, + |m: &MultiSelectDescription| { &m.options }, + |m: &mut MultiSelectDescription| { &mut m.options }, )); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBool>( "disable_color", - |m: &MultiSelect| { &m.disable_color }, - |m: &mut MultiSelect| { &mut m.disable_color }, + |m: &MultiSelectDescription| { &m.disable_color }, + |m: &mut MultiSelectDescription| { &mut m.disable_color }, )); - ::protobuf::reflect::MessageDescriptor::new_pb_name::( - "MultiSelect", + ::protobuf::reflect::MessageDescriptor::new_pb_name::( + "MultiSelectDescription", fields, file_descriptor_proto() ) }) } - fn default_instance() -> &'static MultiSelect { - static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; - instance.get(MultiSelect::new) + fn default_instance() -> &'static MultiSelectDescription { + static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; + instance.get(MultiSelectDescription::new) } } -impl ::protobuf::Clear for MultiSelect { +impl ::protobuf::Clear for MultiSelectDescription { fn clear(&mut self) { self.options.clear(); self.disable_color = false; @@ -903,13 +903,13 @@ impl ::protobuf::Clear for MultiSelect { } } -impl ::std::fmt::Debug for MultiSelect { +impl ::std::fmt::Debug for MultiSelectDescription { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } -impl ::protobuf::reflect::ProtobufValue for MultiSelect { +impl ::protobuf::reflect::ProtobufValue for MultiSelectDescription { fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { ::protobuf::reflect::ReflectValueRef::Message(self) } @@ -1161,7 +1161,7 @@ impl ::protobuf::reflect::ProtobufValue for SelectOption { #[derive(PartialEq,Clone,Default)] pub struct NumberDescription { // message fields - pub money: FlowyMoney, + pub money: MoneySymbol, pub scale: u32, pub symbol: ::std::string::String, pub sign_positive: bool, @@ -1182,18 +1182,18 @@ impl NumberDescription { ::std::default::Default::default() } - // .FlowyMoney money = 1; + // .MoneySymbol money = 1; - pub fn get_money(&self) -> FlowyMoney { + pub fn get_money(&self) -> MoneySymbol { self.money } pub fn clear_money(&mut self) { - self.money = FlowyMoney::CNY; + self.money = MoneySymbol::CNY; } // Param is passed by value, moved - pub fn set_money(&mut self, v: FlowyMoney) { + pub fn set_money(&mut self, v: MoneySymbol) { self.money = v; } @@ -1324,7 +1324,7 @@ impl ::protobuf::Message for NumberDescription { #[allow(unused_variables)] fn compute_size(&self) -> u32 { let mut my_size = 0; - if self.money != FlowyMoney::CNY { + if self.money != MoneySymbol::CNY { my_size += ::protobuf::rt::enum_size(1, self.money); } if self.scale != 0 { @@ -1345,7 +1345,7 @@ impl ::protobuf::Message for NumberDescription { } fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { - if self.money != FlowyMoney::CNY { + if self.money != MoneySymbol::CNY { os.write_enum(1, ::protobuf::ProtobufEnum::value(&self.money))?; } if self.scale != 0 { @@ -1398,7 +1398,7 @@ impl ::protobuf::Message for NumberDescription { 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::ProtobufTypeEnum>( + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum>( "money", |m: &NumberDescription| { &m.money }, |m: &mut NumberDescription| { &mut m.money }, @@ -1439,7 +1439,7 @@ impl ::protobuf::Message for NumberDescription { impl ::protobuf::Clear for NumberDescription { fn clear(&mut self) { - self.money = FlowyMoney::CNY; + self.money = MoneySymbol::CNY; self.scale = 0; self.symbol.clear(); self.sign_positive = false; @@ -1567,31 +1567,31 @@ impl ::protobuf::reflect::ProtobufValue for TimeFormat { } #[derive(Clone,PartialEq,Eq,Debug,Hash)] -pub enum FlowyMoney { +pub enum MoneySymbol { CNY = 0, EUR = 1, USD = 2, } -impl ::protobuf::ProtobufEnum for FlowyMoney { +impl ::protobuf::ProtobufEnum for MoneySymbol { fn value(&self) -> i32 { *self as i32 } - fn from_i32(value: i32) -> ::std::option::Option { + fn from_i32(value: i32) -> ::std::option::Option { match value { - 0 => ::std::option::Option::Some(FlowyMoney::CNY), - 1 => ::std::option::Option::Some(FlowyMoney::EUR), - 2 => ::std::option::Option::Some(FlowyMoney::USD), + 0 => ::std::option::Option::Some(MoneySymbol::CNY), + 1 => ::std::option::Option::Some(MoneySymbol::EUR), + 2 => ::std::option::Option::Some(MoneySymbol::USD), _ => ::std::option::Option::None } } fn values() -> &'static [Self] { - static values: &'static [FlowyMoney] = &[ - FlowyMoney::CNY, - FlowyMoney::EUR, - FlowyMoney::USD, + static values: &'static [MoneySymbol] = &[ + MoneySymbol::CNY, + MoneySymbol::EUR, + MoneySymbol::USD, ]; values } @@ -1599,47 +1599,48 @@ impl ::protobuf::ProtobufEnum for FlowyMoney { fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor { static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT; descriptor.get(|| { - ::protobuf::reflect::EnumDescriptor::new_pb_name::("FlowyMoney", file_descriptor_proto()) + ::protobuf::reflect::EnumDescriptor::new_pb_name::("MoneySymbol", file_descriptor_proto()) }) } } -impl ::std::marker::Copy for FlowyMoney { +impl ::std::marker::Copy for MoneySymbol { } -impl ::std::default::Default for FlowyMoney { +impl ::std::default::Default for MoneySymbol { fn default() -> Self { - FlowyMoney::CNY + MoneySymbol::CNY } } -impl ::protobuf::reflect::ProtobufValue for FlowyMoney { +impl ::protobuf::reflect::ProtobufValue for MoneySymbol { fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { ::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self)) } } static file_descriptor_proto_data: &'static [u8] = b"\ - \n\x0fcell_data.proto\"-\n\x13RichTextDescription\x12\x16\n\x06format\ + \n\x12type_options.proto\"-\n\x13RichTextDescription\x12\x16\n\x06format\ \x18\x01\x20\x01(\tR\x06format\"6\n\x13CheckboxDescription\x12\x1f\n\x0b\ is_selected\x18\x01\x20\x01(\x08R\nisSelected\"m\n\x0fDateDescription\ \x12,\n\x0bdate_format\x18\x01\x20\x01(\x0e2\x0b.DateFormatR\ndateFormat\ \x12,\n\x0btime_format\x18\x02\x20\x01(\x0e2\x0b.TimeFormatR\ntimeFormat\ - \"\\\n\x0cSingleSelect\x12'\n\x07options\x18\x01\x20\x03(\x0b2\r.SelectO\ - ptionR\x07options\x12#\n\rdisable_color\x18\x02\x20\x01(\x08R\x0cdisable\ - Color\"[\n\x0bMultiSelect\x12'\n\x07options\x18\x01\x20\x03(\x0b2\r.Sele\ - ctOptionR\x07options\x12#\n\rdisable_color\x18\x02\x20\x01(\x08R\x0cdisa\ - bleColor\"H\n\x0cSelectOption\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\ - \x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\x12\x14\n\x05color\x18\ - \x03\x20\x01(\tR\x05color\"\x9d\x01\n\x11NumberDescription\x12!\n\x05mon\ - ey\x18\x01\x20\x01(\x0e2\x0b.FlowyMoneyR\x05money\x12\x14\n\x05scale\x18\ - \x02\x20\x01(\rR\x05scale\x12\x16\n\x06symbol\x18\x03\x20\x01(\tR\x06sym\ - bol\x12#\n\rsign_positive\x18\x04\x20\x01(\x08R\x0csignPositive\x12\x12\ - \n\x04name\x18\x05\x20\x01(\tR\x04name*6\n\nDateFormat\x12\t\n\x05Local\ - \x10\0\x12\x06\n\x02US\x10\x01\x12\x07\n\x03ISO\x10\x02\x12\x0c\n\x08Fri\ - endly\x10\x03*0\n\nTimeFormat\x12\x0e\n\nTwelveHour\x10\0\x12\x12\n\x0eT\ - wentyFourHour\x10\x01*'\n\nFlowyMoney\x12\x07\n\x03CNY\x10\0\x12\x07\n\ - \x03EUR\x10\x01\x12\x07\n\x03USD\x10\x02b\x06proto3\ + \"g\n\x17SingleSelectDescription\x12'\n\x07options\x18\x01\x20\x03(\x0b2\ + \r.SelectOptionR\x07options\x12#\n\rdisable_color\x18\x02\x20\x01(\x08R\ + \x0cdisableColor\"f\n\x16MultiSelectDescription\x12'\n\x07options\x18\ + \x01\x20\x03(\x0b2\r.SelectOptionR\x07options\x12#\n\rdisable_color\x18\ + \x02\x20\x01(\x08R\x0cdisableColor\"H\n\x0cSelectOption\x12\x0e\n\x02id\ + \x18\x01\x20\x01(\tR\x02id\x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\ + \x12\x14\n\x05color\x18\x03\x20\x01(\tR\x05color\"\x9e\x01\n\x11NumberDe\ + scription\x12\"\n\x05money\x18\x01\x20\x01(\x0e2\x0c.MoneySymbolR\x05mon\ + ey\x12\x14\n\x05scale\x18\x02\x20\x01(\rR\x05scale\x12\x16\n\x06symbol\ + \x18\x03\x20\x01(\tR\x06symbol\x12#\n\rsign_positive\x18\x04\x20\x01(\ + \x08R\x0csignPositive\x12\x12\n\x04name\x18\x05\x20\x01(\tR\x04name*6\n\ + \nDateFormat\x12\t\n\x05Local\x10\0\x12\x06\n\x02US\x10\x01\x12\x07\n\ + \x03ISO\x10\x02\x12\x0c\n\x08Friendly\x10\x03*0\n\nTimeFormat\x12\x0e\n\ + \nTwelveHour\x10\0\x12\x12\n\x0eTwentyFourHour\x10\x01*(\n\x0bMoneySymbo\ + l\x12\x07\n\x03CNY\x10\0\x12\x07\n\x03EUR\x10\x01\x12\x07\n\x03USD\x10\ + \x02b\x06proto3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/proto/cell_data.proto b/frontend/rust-lib/flowy-grid/src/protobuf/proto/type_options.proto similarity index 87% rename from frontend/rust-lib/flowy-grid/src/protobuf/proto/cell_data.proto rename to frontend/rust-lib/flowy-grid/src/protobuf/proto/type_options.proto index dc146af19e..170035085c 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/proto/cell_data.proto +++ b/frontend/rust-lib/flowy-grid/src/protobuf/proto/type_options.proto @@ -10,11 +10,11 @@ message DateDescription { DateFormat date_format = 1; TimeFormat time_format = 2; } -message SingleSelect { +message SingleSelectDescription { repeated SelectOption options = 1; bool disable_color = 2; } -message MultiSelect { +message MultiSelectDescription { repeated SelectOption options = 1; bool disable_color = 2; } @@ -24,7 +24,7 @@ message SelectOption { string color = 3; } message NumberDescription { - FlowyMoney money = 1; + MoneySymbol money = 1; uint32 scale = 2; string symbol = 3; bool sign_positive = 4; @@ -40,7 +40,7 @@ enum TimeFormat { TwelveHour = 0; TwentyFourHour = 1; } -enum FlowyMoney { +enum MoneySymbol { CNY = 0; EUR = 1; USD = 2; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/cell_stringify.rs b/frontend/rust-lib/flowy-grid/src/services/field/cell_stringify.rs new file mode 100644 index 0000000000..a75506e339 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/cell_stringify.rs @@ -0,0 +1,34 @@ +use crate::services::field::*; +use crate::services::util::*; +use flowy_error::FlowyError; +use flowy_grid_data_model::entities::{AnyData, Field, FieldType}; + +pub trait StringifyCellData { + fn str_from_cell_data(&self, data: AnyData) -> String; + fn str_to_cell_data(&self, s: &str) -> Result; +} + +#[allow(dead_code)] +pub fn stringify_serialize(field: &Field, s: &str) -> Result { + match field.field_type { + FieldType::RichText => RichTextDescription::from(field).str_to_cell_data(s), + FieldType::Number => NumberDescription::from(field).str_to_cell_data(s), + FieldType::DateTime => DateDescription::from(field).str_to_cell_data(s), + FieldType::SingleSelect => SingleSelectDescription::from(field).str_to_cell_data(s), + FieldType::MultiSelect => MultiSelectDescription::from(field).str_to_cell_data(s), + FieldType::Checkbox => CheckboxDescription::from(field).str_to_cell_data(s), + } +} + +pub(crate) fn stringify_deserialize(data: AnyData, field: &Field) -> Result { + let _ = check_type_id(&data, field)?; + let s = match field.field_type { + FieldType::RichText => RichTextDescription::from(field).str_from_cell_data(data), + FieldType::Number => NumberDescription::from(field).str_from_cell_data(data), + FieldType::DateTime => DateDescription::from(field).str_from_cell_data(data), + FieldType::SingleSelect => SingleSelectDescription::from(field).str_from_cell_data(data), + FieldType::MultiSelect => MultiSelectDescription::from(field).str_from_cell_data(data), + FieldType::Checkbox => CheckboxDescription::from(field).str_from_cell_data(data), + }; + Ok(s) +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/field_builder.rs b/frontend/rust-lib/flowy-grid/src/services/field/field_builder.rs new file mode 100644 index 0000000000..3870fc1359 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/field_builder.rs @@ -0,0 +1,217 @@ +use crate::services::field::{ + CheckboxDescription, DateDescription, DateFormat, MoneySymbol, MultiSelectDescription, NumberDescription, + RichTextDescription, SelectOption, SingleSelectDescription, TimeFormat, +}; +use flowy_grid_data_model::entities::{AnyData, Field, FieldType}; + +pub struct FieldBuilder { + field: Field, + type_options_builder: Box, +} + +impl FieldBuilder { + pub fn new(type_options_builder: T) -> Self { + let field = Field::new("Name", "", FieldType::RichText); + Self { + field, + type_options_builder: Box::new(type_options_builder), + } + } + + pub fn name(mut self, name: &str) -> Self { + self.field.name = name.to_owned(); + self + } + + pub fn desc(mut self, desc: &str) -> Self { + self.field.desc = desc.to_owned(); + self + } + + pub fn field_type(mut self, field_type: FieldType) -> Self { + self.field.field_type = field_type; + self + } + + pub fn visibility(mut self, visibility: bool) -> Self { + self.field.visibility = visibility; + self + } + + pub fn width(mut self, width: i32) -> Self { + self.field.width = width; + self + } + + pub fn frozen(mut self, frozen: bool) -> Self { + self.field.frozen = frozen; + self + } + + pub fn build(mut self) -> Field { + assert_eq!(self.field.field_type, self.type_options_builder.field_type()); + + let type_options = self.type_options_builder.build(); + self.field.type_options = type_options; + self.field + } +} + +pub trait TypeOptionsBuilder { + fn field_type(&self) -> FieldType; + fn build(&self) -> AnyData; +} + +// Text +pub struct RichTextTypeOptionsBuilder(RichTextDescription); + +impl RichTextTypeOptionsBuilder { + pub fn new() -> Self { + Self(RichTextDescription::default()) + } +} + +impl TypeOptionsBuilder for RichTextTypeOptionsBuilder { + fn field_type(&self) -> FieldType { + self.0.field_type() + } + + fn build(&self) -> AnyData { + self.0.clone().into() + } +} + +// Number +pub struct NumberTypeOptionsBuilder(NumberDescription); + +impl NumberTypeOptionsBuilder { + pub fn new() -> Self { + Self(NumberDescription::default()) + } + + pub fn name(mut self, name: &str) -> Self { + self.0.name = name.to_string(); + self + } + + pub fn set_money_symbol(mut self, money_symbol: MoneySymbol) -> Self { + self.0.set_money_symbol(money_symbol); + self + } + + pub fn scale(mut self, scale: u32) -> Self { + self.0.scale = scale; + self + } + + pub fn positive(mut self, positive: bool) -> Self { + self.0.sign_positive = positive; + self + } +} + +impl TypeOptionsBuilder for NumberTypeOptionsBuilder { + fn field_type(&self) -> FieldType { + self.0.field_type() + } + + fn build(&self) -> AnyData { + self.0.clone().into() + } +} + +// Date +pub struct DateTypeOptionsBuilder(DateDescription); +impl DateTypeOptionsBuilder { + pub fn new() -> Self { + Self(DateDescription::default()) + } + + pub fn date_format(mut self, date_format: DateFormat) -> Self { + self.0.date_format = date_format; + self + } + + pub fn time_format(mut self, time_format: TimeFormat) -> Self { + self.0.time_format = time_format; + self + } +} +impl TypeOptionsBuilder for DateTypeOptionsBuilder { + fn field_type(&self) -> FieldType { + self.0.field_type() + } + + fn build(&self) -> AnyData { + self.0.clone().into() + } +} + +// Single Select +pub struct SingleSelectTypeOptionsBuilder(SingleSelectDescription); + +impl SingleSelectTypeOptionsBuilder { + pub fn new() -> Self { + Self(SingleSelectDescription::default()) + } + + pub fn option(mut self, opt: SelectOption) -> Self { + self.0.options.push(opt); + self + } +} +impl TypeOptionsBuilder for SingleSelectTypeOptionsBuilder { + fn field_type(&self) -> FieldType { + self.0.field_type() + } + + fn build(&self) -> AnyData { + self.0.clone().into() + } +} + +// Multi Select +pub struct MultiSelectTypeOptionsBuilder(MultiSelectDescription); + +impl MultiSelectTypeOptionsBuilder { + pub fn new() -> Self { + Self(MultiSelectDescription::default()) + } + + pub fn option(mut self, opt: SelectOption) -> Self { + self.0.options.push(opt); + self + } +} + +impl TypeOptionsBuilder for MultiSelectTypeOptionsBuilder { + fn field_type(&self) -> FieldType { + self.0.field_type() + } + + fn build(&self) -> AnyData { + self.0.clone().into() + } +} + +// Checkbox +pub struct CheckboxTypeOptionsBuilder(CheckboxDescription); +impl CheckboxTypeOptionsBuilder { + pub fn new() -> Self { + Self(CheckboxDescription::default()) + } + + pub fn set_selected(mut self, is_selected: bool) -> Self { + self.0.is_selected = is_selected; + self + } +} +impl TypeOptionsBuilder for CheckboxTypeOptionsBuilder { + fn field_type(&self) -> FieldType { + self.0.field_type() + } + + fn build(&self) -> AnyData { + self.0.clone().into() + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/mod.rs new file mode 100644 index 0000000000..157e5145c2 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/mod.rs @@ -0,0 +1,7 @@ +mod cell_stringify; +mod field_builder; +mod type_options; + +pub use cell_stringify::*; +pub use field_builder::*; +pub use type_options::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/cell_data.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options.rs similarity index 65% rename from frontend/rust-lib/flowy-grid/src/services/cell_data.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options.rs index ce65fb005a..1a417abe20 100644 --- a/frontend/rust-lib/flowy-grid/src/services/cell_data.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options.rs @@ -1,5 +1,6 @@ #![allow(clippy::upper_case_acronyms)] -use crate::impl_any_data; +use crate::impl_from_and_to_type_option; +use crate::services::field::StringifyCellData; use crate::services::util::*; use bytes::Bytes; use chrono::format::strftime::StrftimeItems; @@ -15,63 +16,42 @@ use rusty_money::{ use std::str::FromStr; use strum_macros::EnumIter; -pub trait StringifyAnyData { - fn stringify_any_data(&self, data: AnyData) -> String; - fn str_to_any_data(&self, s: &str) -> Result; -} - -pub trait DisplayCell { - fn display_content(&self, s: &str) -> String; -} - #[derive(Debug, Clone, ProtoBuf, Default)] pub struct RichTextDescription { #[pb(index = 1)] pub format: String, } -impl_any_data!(RichTextDescription, FieldType::RichText); +impl_from_and_to_type_option!(RichTextDescription, FieldType::RichText); -impl StringifyAnyData for RichTextDescription { - fn stringify_any_data(&self, data: AnyData) -> String { +impl StringifyCellData for RichTextDescription { + fn str_from_cell_data(&self, data: AnyData) -> String { data.to_string() } - fn str_to_any_data(&self, s: &str) -> Result { - Ok(AnyData::from_str(&RichTextDescription::field_type(), s)) - } -} - -impl DisplayCell for RichTextDescription { - fn display_content(&self, s: &str) -> String { - s.to_string() + fn str_to_cell_data(&self, s: &str) -> Result { + Ok(AnyData::from_str(self.field_type(), s)) } } // Checkbox -#[derive(Debug, ProtoBuf, Default)] +#[derive(Debug, Clone, ProtoBuf, Default)] pub struct CheckboxDescription { #[pb(index = 1)] pub is_selected: bool, } -impl_any_data!(CheckboxDescription, FieldType::Checkbox); +impl_from_and_to_type_option!(CheckboxDescription, FieldType::Checkbox); -impl StringifyAnyData for CheckboxDescription { - fn stringify_any_data(&self, data: AnyData) -> String { +impl StringifyCellData for CheckboxDescription { + fn str_from_cell_data(&self, data: AnyData) -> String { data.to_string() } - fn str_to_any_data(&self, s: &str) -> Result { + fn str_to_cell_data(&self, s: &str) -> Result { let s = match string_to_bool(s) { true => "1", false => "0", }; - Ok(AnyData::from_str(&CheckboxDescription::field_type(), s)) - } -} - -impl DisplayCell for CheckboxDescription { - fn display_content(&self, s: &str) -> String { - s.to_string() + Ok(AnyData::from_str(self.field_type(), s)) } } @@ -84,7 +64,7 @@ pub struct DateDescription { #[pb(index = 2)] pub time_format: TimeFormat, } -impl_any_data!(DateDescription, FieldType::DateTime); +impl_from_and_to_type_option!(DateDescription, FieldType::DateTime); impl DateDescription { fn date_time_format_str(&self) -> String { @@ -107,23 +87,8 @@ impl DateDescription { } } -impl DisplayCell for DateDescription { - fn display_content(&self, s: &str) -> String { - match s.parse::() { - Ok(timestamp) => { - let native = NaiveDateTime::from_timestamp(timestamp, 0); - self.today_from_native(native) - } - Err(e) => { - tracing::debug!("DateDescription format {} fail. error: {:?}", s, e); - String::new() - } - } - } -} - -impl StringifyAnyData for DateDescription { - fn stringify_any_data(&self, data: AnyData) -> String { +impl StringifyCellData for DateDescription { + fn str_from_cell_data(&self, data: AnyData) -> String { match String::from_utf8(data.value) { Ok(s) => match s.parse::() { Ok(timestamp) => { @@ -142,14 +107,11 @@ impl StringifyAnyData for DateDescription { } } - fn str_to_any_data(&self, s: &str) -> Result { + fn str_to_cell_data(&self, s: &str) -> Result { let timestamp = s .parse::() .map_err(|e| FlowyError::internal().context(format!("Parse {} to i64 failed: {}", s, e)))?; - Ok(AnyData::from_str( - &DateDescription::field_type(), - &format!("{}", timestamp), - )) + Ok(AnyData::from_str(self.field_type(), &format!("{}", timestamp))) } } @@ -237,54 +199,42 @@ impl std::default::Default for TimeFormat { // Single select #[derive(Clone, Debug, ProtoBuf, Default)] -pub struct SingleSelect { +pub struct SingleSelectDescription { #[pb(index = 1)] pub options: Vec, #[pb(index = 2)] pub disable_color: bool, } -impl_any_data!(SingleSelect, FieldType::SingleSelect); +impl_from_and_to_type_option!(SingleSelectDescription, FieldType::SingleSelect); -impl StringifyAnyData for SingleSelect { - fn stringify_any_data(&self, data: AnyData) -> String { +impl StringifyCellData for SingleSelectDescription { + fn str_from_cell_data(&self, data: AnyData) -> String { data.to_string() } - fn str_to_any_data(&self, s: &str) -> Result { - Ok(AnyData::from_str(&SingleSelect::field_type(), s)) - } -} - -impl DisplayCell for SingleSelect { - fn display_content(&self, s: &str) -> String { - s.to_string() + fn str_to_cell_data(&self, s: &str) -> Result { + Ok(AnyData::from_str(self.field_type(), s)) } } // Multiple select #[derive(Clone, Debug, ProtoBuf, Default)] -pub struct MultiSelect { +pub struct MultiSelectDescription { #[pb(index = 1)] pub options: Vec, #[pb(index = 2)] pub disable_color: bool, } -impl_any_data!(MultiSelect, FieldType::MultiSelect); -impl StringifyAnyData for MultiSelect { - fn stringify_any_data(&self, data: AnyData) -> String { +impl_from_and_to_type_option!(MultiSelectDescription, FieldType::MultiSelect); +impl StringifyCellData for MultiSelectDescription { + fn str_from_cell_data(&self, data: AnyData) -> String { data.to_string() } - fn str_to_any_data(&self, s: &str) -> Result { - Ok(AnyData::from_str(&MultiSelect::field_type(), s)) - } -} - -impl DisplayCell for MultiSelect { - fn display_content(&self, s: &str) -> String { - s.to_string() + fn str_to_cell_data(&self, s: &str) -> Result { + Ok(AnyData::from_str(self.field_type(), s)) } } @@ -314,7 +264,7 @@ impl SelectOption { #[derive(Clone, Debug, ProtoBuf)] pub struct NumberDescription { #[pb(index = 1)] - pub money: FlowyMoney, + pub money: MoneySymbol, #[pb(index = 2)] pub scale: u32, @@ -328,24 +278,26 @@ pub struct NumberDescription { #[pb(index = 5)] pub name: String, } -impl_any_data!(NumberDescription, FieldType::Number); +impl_from_and_to_type_option!(NumberDescription, FieldType::Number); impl std::default::Default for NumberDescription { fn default() -> Self { + let money = MoneySymbol::default(); + let symbol = money.symbol_str(); NumberDescription { - money: FlowyMoney::default(), + money, scale: 0, - symbol: String::new(), + symbol, sign_positive: true, - name: String::new(), + name: "Number".to_string(), } } } impl NumberDescription { - pub fn set_money(&mut self, money: FlowyMoney) { - self.money = money; - self.symbol = money.symbol(); + pub fn set_money_symbol(&mut self, money_symbol: MoneySymbol) { + self.money = money_symbol; + self.symbol = money_symbol.symbol_str(); } fn money_from_str(&self, s: &str) -> Option { @@ -368,17 +320,8 @@ impl NumberDescription { } } -impl DisplayCell for NumberDescription { - fn display_content(&self, s: &str) -> String { - match self.money_from_str(s) { - Some(money_str) => money_str, - None => String::default(), - } - } -} - -impl StringifyAnyData for NumberDescription { - fn stringify_any_data(&self, data: AnyData) -> String { +impl StringifyCellData for NumberDescription { + fn str_from_cell_data(&self, data: AnyData) -> String { match String::from_utf8(data.value) { Ok(s) => match self.money_from_str(&s) { Some(money_str) => money_str, @@ -391,47 +334,47 @@ impl StringifyAnyData for NumberDescription { } } - fn str_to_any_data(&self, s: &str) -> Result { + fn str_to_cell_data(&self, s: &str) -> Result { let strip_symbol_money = strip_money_symbol(s); let decimal = Decimal::from_str(&strip_symbol_money).map_err(|err| FlowyError::internal().context(err))?; let money_str = decimal.to_string(); - Ok(AnyData::from_str(&NumberDescription::field_type(), &money_str)) + Ok(AnyData::from_str(self.field_type(), &money_str)) } } #[derive(Clone, Copy, Debug, EnumIter, ProtoBuf_Enum)] -pub enum FlowyMoney { +pub enum MoneySymbol { CNY = 0, EUR = 1, USD = 2, } -impl std::default::Default for FlowyMoney { +impl std::default::Default for MoneySymbol { fn default() -> Self { - FlowyMoney::USD + MoneySymbol::USD } } -impl FlowyMoney { +impl MoneySymbol { // Currency list https://docs.rs/rusty-money/0.4.0/rusty_money/iso/index.html - pub fn from_symbol_str(s: &str) -> FlowyMoney { + pub fn from_symbol_str(s: &str) -> MoneySymbol { match s { - "CNY" => FlowyMoney::CNY, - "EUR" => FlowyMoney::EUR, - "USD" => FlowyMoney::USD, - _ => FlowyMoney::CNY, + "CNY" => MoneySymbol::CNY, + "EUR" => MoneySymbol::EUR, + "USD" => MoneySymbol::USD, + _ => MoneySymbol::CNY, } } - pub fn from_money(money: &rusty_money::Money) -> FlowyMoney { - FlowyMoney::from_symbol_str(&money.currency().symbol.to_string()) + pub fn from_money(money: &rusty_money::Money) -> MoneySymbol { + MoneySymbol::from_symbol_str(&money.currency().symbol.to_string()) } pub fn currency(&self) -> &'static Currency { match self { - FlowyMoney::CNY => CNY, - FlowyMoney::EUR => EUR, - FlowyMoney::USD => USD, + MoneySymbol::CNY => CNY, + MoneySymbol::EUR => EUR, + MoneySymbol::USD => USD, } } @@ -442,7 +385,7 @@ impl FlowyMoney { self.currency().iso_alpha_code.to_string() } - pub fn symbol(&self) -> String { + pub fn symbol_str(&self) -> String { self.currency().symbol.to_string() } 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 f9cf627d83..b6f55e6cfa 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -38,7 +38,7 @@ 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::(Some(cloud)).await?; let rev_manager = Arc::new(rev_manager); let grid_meta_pad = Arc::new(RwLock::new(grid_pad)); @@ -55,12 +55,12 @@ impl ClientGridEditor { })) } - pub async fn create_field(&mut self, field: Field) -> FlowyResult<()> { + pub async fn create_field(&self, field: Field) -> FlowyResult<()> { let _ = self.modify(|grid| Ok(grid.create_field(field)?)).await?; Ok(()) } - pub async fn delete_field(&mut self, field_id: &str) -> FlowyResult<()> { + pub async fn delete_field(&self, field_id: &str) -> FlowyResult<()> { let _ = self.modify(|grid| Ok(grid.delete_field(field_id)?)).await?; Ok(()) } @@ -125,6 +125,13 @@ impl ClientGridEditor { } } +#[cfg(feature = "flowy_unit_test")] +impl ClientGridEditor { + pub fn rev_manager(&self) -> Arc { + self.rev_manager.clone() + } +} + async fn load_all_fields( grid_pad: &GridMetaPad, kv_persistence: &Arc, @@ -143,7 +150,7 @@ async fn load_all_fields( Ok(map) } -struct GridPadBuilder(); +pub struct GridPadBuilder(); impl RevisionObjectBuilder for GridPadBuilder { type Output = GridMetaPad; 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 index 6704d6a718..10160e115a 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_meta_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_meta_editor.rs @@ -27,7 +27,7 @@ impl ClientGridBlockMetaEditor { let cloud = Arc::new(GridBlockMetaRevisionCloudService { token: token.to_owned(), }); - let block_meta_pad = rev_manager.load::(cloud).await?; + let block_meta_pad = rev_manager.load::(Some(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(); diff --git a/frontend/rust-lib/flowy-grid/src/services/mod.rs b/frontend/rust-lib/flowy-grid/src/services/mod.rs index 340a260b09..552f8cfe56 100644 --- a/frontend/rust-lib/flowy-grid/src/services/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/mod.rs @@ -1,7 +1,6 @@ mod util; -pub mod cell_data; +pub mod field; pub mod grid_editor; pub mod grid_meta_editor; pub mod kv_persistence; -pub mod stringify; diff --git a/frontend/rust-lib/flowy-grid/src/services/stringify.rs b/frontend/rust-lib/flowy-grid/src/services/stringify.rs deleted file mode 100644 index a9154a5809..0000000000 --- a/frontend/rust-lib/flowy-grid/src/services/stringify.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::services::cell_data::*; -use crate::services::util::*; -use flowy_error::FlowyError; -use flowy_grid_data_model::entities::{AnyData, Field, FieldType}; - -#[allow(dead_code)] -pub fn stringify_serialize(field: &Field, s: &str) -> Result { - match field.field_type { - FieldType::RichText => RichTextDescription::from(field).str_to_any_data(s), - FieldType::Number => NumberDescription::from(field).str_to_any_data(s), - FieldType::DateTime => DateDescription::from(field).str_to_any_data(s), - FieldType::SingleSelect => SingleSelect::from(field).str_to_any_data(s), - FieldType::MultiSelect => MultiSelect::from(field).str_to_any_data(s), - FieldType::Checkbox => CheckboxDescription::from(field).str_to_any_data(s), - } -} - -pub(crate) fn stringify_deserialize(data: AnyData, field: &Field) -> Result { - let _ = check_type_id(&data, field)?; - let s = match field.field_type { - FieldType::RichText => RichTextDescription::from(field).stringify_any_data(data), - FieldType::Number => NumberDescription::from(field).stringify_any_data(data), - FieldType::DateTime => DateDescription::from(field).stringify_any_data(data), - FieldType::SingleSelect => SingleSelect::from(field).stringify_any_data(data), - FieldType::MultiSelect => MultiSelect::from(field).stringify_any_data(data), - FieldType::Checkbox => CheckboxDescription::from(field).stringify_any_data(data), - }; - Ok(s) -} diff --git a/frontend/rust-lib/flowy-grid/src/services/util.rs b/frontend/rust-lib/flowy-grid/src/services/util.rs index 8fa196956d..a82e748a3e 100644 --- a/frontend/rust-lib/flowy-grid/src/services/util.rs +++ b/frontend/rust-lib/flowy-grid/src/services/util.rs @@ -1,4 +1,4 @@ -use crate::services::cell_data::FlowyMoney; +use crate::services::field::MoneySymbol; use flowy_error::FlowyError; use flowy_grid_data_model::entities::{AnyData, Field, FieldType}; use lazy_static::lazy_static; @@ -16,8 +16,8 @@ lazy_static! { fn generate_currency_by_symbol() -> HashMap { let mut map: HashMap = HashMap::new(); - for money in FlowyMoney::iter() { - map.insert(money.symbol(), money.currency()); + for money in MoneySymbol::iter() { + map.insert(money.symbol_str(), money.currency()); } map } @@ -25,7 +25,7 @@ fn generate_currency_by_symbol() -> HashMap { #[allow(dead_code)] pub fn string_to_money(money_str: &str) -> Option> { let mut process_money_str = String::from(money_str); - let default_currency = FlowyMoney::from_symbol_str("CNY").currency(); + let default_currency = MoneySymbol::from_symbol_str("CNY").currency(); if process_money_str.is_empty() { return None; @@ -65,7 +65,7 @@ pub fn money_from_str(s: &str) -> Option { } } decimal.set_sign_positive(true); - Some(FlowyMoney::USD.with_decimal(decimal).to_string()) + Some(MoneySymbol::USD.with_decimal(decimal).to_string()) } Err(e) => { tracing::debug!("Format {} to money failed, {:?}", s, e); diff --git a/frontend/rust-lib/flowy-grid/src/util.rs b/frontend/rust-lib/flowy-grid/src/util.rs new file mode 100644 index 0000000000..8cf876a052 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/util.rs @@ -0,0 +1,30 @@ +use crate::services::field::*; +use flowy_collaboration::client_grid::{BuildGridInfo, GridBuilder}; +use flowy_grid_data_model::entities::{Field, FieldType}; + +pub fn make_default_grid(grid_id: &str) -> BuildGridInfo { + let text_field = FieldBuilder::new(RichTextTypeOptionsBuilder::new()) + .name("Name") + .visibility(true) + .field_type(FieldType::RichText) + .build(); + + let single_select = SingleSelectTypeOptionsBuilder::new() + .option(SelectOption::new("Done")) + .option(SelectOption::new("Progress")); + + let single_select_field = FieldBuilder::new(single_select) + .name("Name") + .visibility(true) + .field_type(FieldType::SingleSelect) + .build(); + + GridBuilder::new(grid_id) + .add_field(text_field) + .add_field(single_select_field) + .add_empty_row() + .add_empty_row() + .add_empty_row() + .build() + .unwrap() +} diff --git a/frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs b/frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs new file mode 100644 index 0000000000..797315bb10 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs @@ -0,0 +1,16 @@ +use crate::grid::script::EditorScript::*; +use crate::grid::script::*; + +#[tokio::test] +async fn grid_creat_field_test() { + let scripts = vec![ + CreateField { + field: create_text_field(), + }, + CreateField { + field: create_single_select_field(), + }, + AssertGridMetaPad, + ]; + GridEditorTest::new().await.run_scripts(scripts).await; +} diff --git a/frontend/rust-lib/flowy-grid/tests/grid/mod.rs b/frontend/rust-lib/flowy-grid/tests/grid/mod.rs new file mode 100644 index 0000000000..04b16720e5 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/tests/grid/mod.rs @@ -0,0 +1,2 @@ +mod grid_test; +mod script; diff --git a/frontend/rust-lib/flowy-grid/tests/grid/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/script.rs new file mode 100644 index 0000000000..4e31aef2c4 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/tests/grid/script.rs @@ -0,0 +1,75 @@ +use flowy_grid::services::field::*; +use flowy_grid::services::grid_editor::{ClientGridEditor, GridPadBuilder}; +use flowy_grid_data_model::entities::{AnyData, Field, FieldType}; +use flowy_test::event_builder::FolderEventBuilder; +use flowy_test::helper::ViewTest; +use flowy_test::FlowySDKTest; +use std::sync::Arc; + +pub enum EditorScript { + CreateField { field: Field }, + CreateRow, + AssertGridMetaPad, +} + +pub struct GridEditorTest { + pub sdk: FlowySDKTest, + pub grid_id: String, + pub editor: Arc, +} + +impl GridEditorTest { + pub async fn new() -> Self { + let sdk = FlowySDKTest::default(); + let _ = sdk.init_user().await; + let test = ViewTest::new_grid_view(&sdk).await; + let editor = sdk.grid_manager.open_grid(&test.view.id).await.unwrap(); + let grid_id = test.view.id; + Self { sdk, grid_id, editor } + } + + pub async fn run_scripts(&mut self, scripts: Vec) { + for script in scripts { + self.run_script(script).await; + } + } + + pub async fn run_script(&mut self, script: EditorScript) { + let grid_manager = self.sdk.grid_manager.clone(); + let pool = self.sdk.user_session.db_pool().unwrap(); + let rev_manager = self.editor.rev_manager(); + let cache = rev_manager.revision_cache().await; + + match script { + EditorScript::CreateField { field } => { + self.editor.create_field(field).await.unwrap(); + } + EditorScript::CreateRow => {} + EditorScript::AssertGridMetaPad => { + let mut grid_rev_manager = grid_manager.make_grid_rev_manager(&self.grid_id, pool.clone()).unwrap(); + let grid_pad = grid_rev_manager.load::(None).await.unwrap(); + println!("{}", grid_pad.delta_str()); + } + } + } +} + +pub fn create_text_field() -> Field { + FieldBuilder::new(RichTextTypeOptionsBuilder::new()) + .name("Name") + .visibility(true) + .field_type(FieldType::RichText) + .build() +} + +pub fn create_single_select_field() -> Field { + let single_select = SingleSelectTypeOptionsBuilder::new() + .option(SelectOption::new("Done")) + .option(SelectOption::new("Progress")); + + FieldBuilder::new(single_select) + .name("Name") + .visibility(true) + .field_type(FieldType::SingleSelect) + .build() +} diff --git a/frontend/rust-lib/flowy-grid/tests/grid_test.rs b/frontend/rust-lib/flowy-grid/tests/grid_test.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frontend/rust-lib/flowy-grid/tests/main.rs b/frontend/rust-lib/flowy-grid/tests/main.rs new file mode 100644 index 0000000000..ec055c3bb9 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/tests/main.rs @@ -0,0 +1 @@ +mod grid; 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 e0906cc7f1..0646dbcc5b 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 @@ -1,7 +1,6 @@ use bytes::Bytes; use flowy_block::TextBlockManager; use flowy_collaboration::client_document::default::initial_quill_delta_string; -use flowy_collaboration::client_grid::make_default_grid; use flowy_collaboration::entities::revision::{RepeatedRevision, Revision}; use flowy_collaboration::entities::ws_data::ClientRevisionWSData; use flowy_database::ConnectionPool; @@ -13,6 +12,7 @@ use flowy_folder::{ manager::FolderManager, }; use flowy_grid::manager::GridManager; +use flowy_grid::util::make_default_grid; use flowy_net::ClientServerConfiguration; use flowy_net::{ http_server::folder::FolderHttpCloudService, local_server::LocalServer, ws::connection::FlowyWebSocketConnect, diff --git a/frontend/rust-lib/flowy-sync/src/rev_manager.rs b/frontend/rust-lib/flowy-sync/src/rev_manager.rs index d0ed5240c8..7b34bb8115 100644 --- a/frontend/rust-lib/flowy-sync/src/rev_manager.rs +++ b/frontend/rust-lib/flowy-sync/src/rev_manager.rs @@ -67,14 +67,14 @@ impl RevisionManager { } } - pub async fn load(&mut self, cloud: Arc) -> FlowyResult + pub async fn load(&mut self, cloud: Option>) -> FlowyResult where B: RevisionObjectBuilder, { let (revisions, rev_id) = RevisionLoader { object_id: self.object_id.clone(), user_id: self.user_id.clone(), - cloud: Some(cloud), + cloud, rev_persistence: self.rev_persistence.clone(), } .load() diff --git a/frontend/rust-lib/flowy-test/src/helper.rs b/frontend/rust-lib/flowy-test/src/helper.rs index 83277b22c6..9c03c09bb5 100644 --- a/frontend/rust-lib/flowy-test/src/helper.rs +++ b/frontend/rust-lib/flowy-test/src/helper.rs @@ -25,11 +25,12 @@ pub struct ViewTest { } impl ViewTest { - pub async fn new(sdk: &FlowySDKTest) -> Self { + #[allow(dead_code)] + pub async fn new(sdk: &FlowySDKTest, data_type: ViewDataType) -> Self { let workspace = create_workspace(sdk, "Workspace", "").await; open_workspace(sdk, &workspace.id).await; let app = create_app(sdk, "App", "AppFlowy GitHub Project", &workspace.id).await; - let view = create_view(sdk, &app.id).await; + let view = create_view(sdk, &app.id, data_type).await; Self { sdk: sdk.clone(), workspace, @@ -37,6 +38,16 @@ impl ViewTest { view, } } + + #[allow(dead_code)] + pub async fn new_grid_view(sdk: &FlowySDKTest) -> Self { + Self::new(sdk, ViewDataType::Grid).await + } + + #[allow(dead_code)] + pub async fn new_text_block_view(sdk: &FlowySDKTest) -> Self { + Self::new(sdk, ViewDataType::TextBlock).await + } } async fn create_workspace(sdk: &FlowySDKTest, name: &str, desc: &str) -> Workspace { @@ -82,13 +93,13 @@ async fn create_app(sdk: &FlowySDKTest, name: &str, desc: &str, workspace_id: &s app } -async fn create_view(sdk: &FlowySDKTest, app_id: &str) -> View { +async fn create_view(sdk: &FlowySDKTest, app_id: &str, data_type: ViewDataType) -> View { let request = CreateViewPayload { belong_to_id: app_id.to_string(), name: "View A".to_string(), desc: "".to_string(), thumbnail: Some("http://1.png".to_string()), - data_type: ViewDataType::TextBlock, + data_type, ext_data: "".to_string(), plugin_type: 0, }; diff --git a/shared-lib/flowy-collaboration/src/client_grid/grid_builder.rs b/shared-lib/flowy-collaboration/src/client_grid/grid_builder.rs index dbc41e28ae..30cd8cd2e0 100644 --- a/shared-lib/flowy-collaboration/src/client_grid/grid_builder.rs +++ b/shared-lib/flowy-collaboration/src/client_grid/grid_builder.rs @@ -25,8 +25,7 @@ impl GridBuilder { } } - pub fn add_field(mut self, name: &str, desc: &str, field_type: FieldType) -> Self { - let field = Field::new(name, desc, field_type); + pub fn add_field(mut self, field: Field) -> Self { self.fields.push(field); self } @@ -74,27 +73,16 @@ fn check_rows(fields: &[Field], rows: &[RowMeta]) -> CollaborateResult<()> { Ok(()) } -pub fn make_default_grid(grid_id: &str) -> BuildGridInfo { - GridBuilder::new(grid_id) - .add_field("Name", "", FieldType::RichText) - .add_field("Tags", "", FieldType::SingleSelect) - .add_empty_row() - .add_empty_row() - .add_empty_row() - .build() - .unwrap() -} - #[cfg(test)] mod tests { use crate::client_grid::GridBuilder; - use flowy_grid_data_model::entities::{FieldType, GridBlockMeta, GridMeta}; + use flowy_grid_data_model::entities::{Field, FieldType, GridBlockMeta, GridMeta}; #[test] fn create_default_grid_test() { let info = GridBuilder::new("1") - .add_field("Name", "", FieldType::RichText) - .add_field("Tags", "", FieldType::SingleSelect) + .add_field(Field::new("Name", "", FieldType::RichText)) + .add_field(Field::new("Tags", "", FieldType::SingleSelect)) .add_empty_row() .add_empty_row() .add_empty_row() 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 b5cdc96e31..7c19cb40af 100644 --- a/shared-lib/flowy-grid-data-model/src/entities/meta.rs +++ b/shared-lib/flowy-grid-data-model/src/entities/meta.rs @@ -163,8 +163,19 @@ impl std::default::Default for FieldType { } } +impl AsRef for FieldType { + fn as_ref(&self) -> &FieldType { + &self + } +} + +impl Into for &FieldType { + fn into(self) -> FieldType { + self.clone() + } +} + impl FieldType { - #[allow(dead_code)] pub fn type_id(&self) -> String { let ty = self.clone(); format!("{}", ty as u8) @@ -193,13 +204,13 @@ pub struct AnyData { } impl AnyData { - pub fn from_str(field_type: &FieldType, s: &str) -> AnyData { + pub fn from_str>(field_type: F, s: &str) -> AnyData { Self::from_bytes(field_type, s.as_bytes().to_vec()) } - pub fn from_bytes>(field_type: &FieldType, bytes: T) -> AnyData { + pub fn from_bytes, F: Into>(field_type: F, bytes: T) -> AnyData { AnyData { - type_id: field_type.type_id(), + type_id: field_type.into().type_id(), value: bytes.as_ref().to_vec(), } }