chore: add field builder

This commit is contained in:
appflowy 2022-03-12 21:06:15 +08:00
parent 6579940dc8
commit 1e6d82c0ec
44 changed files with 1545 additions and 569 deletions

View File

@ -1,3 +1,3 @@
// Auto-generated, do not edit
export './cell_data.pb.dart';
export './type_options.pb.dart';
export './event_map.pb.dart';

View File

@ -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<RichTextDescription> createRepeated() => $pb.PbList<RichTextDescription>();
@$core.pragma('dart2js:noInline')
static RichTextDescription getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RichTextDescription>(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<CheckboxDescription> createRepeated() => $pb.PbList<CheckboxDescription>();
@$core.pragma('dart2js:noInline')
static CheckboxDescription getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<CheckboxDescription>(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<DateFormat>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dateFormat', $pb.PbFieldType.OE, defaultOrMaker: DateFormat.Local, valueOf: DateFormat.valueOf, enumValues: DateFormat.values)
..e<TimeFormat>(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<DateDescription> createRepeated() => $pb.PbList<DateDescription>();
@$core.pragma('dart2js:noInline')
static DateDescription getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<DateDescription>(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<SelectOption>(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<SelectOption>? 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<SingleSelectDescription> createRepeated() => $pb.PbList<SingleSelectDescription>();
@$core.pragma('dart2js:noInline')
static SingleSelectDescription getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SingleSelectDescription>(create);
static SingleSelectDescription? _defaultInstance;
@$pb.TagNumber(1)
$core.List<SelectOption> 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<SelectOption>(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<SelectOption>? 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<MultiSelectDescription> createRepeated() => $pb.PbList<MultiSelectDescription>();
@$core.pragma('dart2js:noInline')
static MultiSelectDescription getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<MultiSelectDescription>(create);
static MultiSelectDescription? _defaultInstance;
@$pb.TagNumber(1)
$core.List<SelectOption> 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<SelectOption> createRepeated() => $pb.PbList<SelectOption>();
@$core.pragma('dart2js:noInline')
static SelectOption getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SelectOption>(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<MoneySymbol>(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<NumberDescription> createRepeated() => $pb.PbList<NumberDescription>();
@$core.pragma('dart2js:noInline')
static NumberDescription getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<NumberDescription>(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);
}

View File

@ -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<DateFormat> values = <DateFormat> [
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<TimeFormat> values = <TimeFormat> [
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<MoneySymbol> values = <MoneySymbol> [
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);
}

View File

@ -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=');

View File

@ -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';

View File

@ -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",

View File

@ -39,7 +39,7 @@ impl ClientTextBlockEditor {
rev_web_socket: Arc<dyn RevisionWebSocket>,
cloud_service: Arc<dyn RevisionCloudService>,
) -> FlowyResult<Arc<Self>> {
let document_info = rev_manager.load::<TextBlockInfoBuilder>(cloud_service).await?;
let document_info = rev_manager.load::<TextBlockInfoBuilder>(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<String> {
let (ret, rx) = oneshot::channel::<CollaborateResult<String>>();
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<RichTextDelta> {
pub async fn text_block_delta(&self) -> FlowyResult<RichTextDelta> {
let (ret, rx) = oneshot::channel::<CollaborateResult<RichTextDelta>>();
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)

View File

@ -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<String>,
},
#[allow(dead_code)]
ReadBlockDelta {
ReadDelta {
ret: Ret<RichTextDelta>,
},
}
@ -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)
}

View File

@ -1,2 +1,2 @@
mod document_test;
mod edit_script;
mod script;
mod text_block_test;

View File

@ -17,16 +17,16 @@ pub enum EditorScript {
AssertJson(&'static str),
}
pub struct EditorTest {
pub struct TextBlockEditorTest {
pub sdk: FlowySDKTest,
pub editor: Arc<ClientTextBlockEditor>,
}
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());

View File

@ -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;
}

View File

@ -38,7 +38,7 @@ impl ClientFolderEditor {
let cloud = Arc::new(FolderRevisionCloudService {
token: token.to_string(),
});
let folder = Arc::new(RwLock::new(rev_manager.load::<FolderPadBuilder>(cloud).await?));
let folder = Arc::new(RwLock::new(rev_manager.load::<FolderPadBuilder>(Some(cloud)).await?));
let rev_manager = Arc::new(rev_manager);
let ws_manager = make_folder_ws_manager(
user_id,

View File

@ -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)),

View File

@ -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>();
workspace
}
pub async fn read_workspace(sdk: &FlowySDKTest, workspace_id: Option<String>) -> Vec<Workspace> {
let request = WorkspaceId { value: workspace_id };
let repeated_workspace = FolderEventBuilder::new(sdk.clone())
.event(ReadWorkspaces)
.payload(request.clone())
.async_send()
.await
.parse::<RepeatedWorkspace>();
let workspaces;
if let Some(workspace_id) = &request.value {
workspaces = repeated_workspace
.into_inner()
.into_iter()
.filter(|workspace| &workspace.id == workspace_id)
.collect::<Vec<Workspace>>();
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>();
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>();
app
}
pub async fn update_app(sdk: &FlowySDKTest, app_id: &str, name: Option<String>, desc: Option<String>) {
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>();
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::<View>()
}
pub async fn update_view(sdk: &FlowySDKTest, view_id: &str, name: Option<String>, desc: Option<String>) {
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<String>) {
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::<TextBlockInfo>()
}
pub async fn read_trash(sdk: &FlowySDKTest) -> RepeatedTrash {
FolderEventBuilder::new(sdk.clone())
.event(ReadTrash)
.async_send()
.await
.parse::<RepeatedTrash>()
}
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;
}

View File

@ -1,3 +1,2 @@
mod folder_test;
mod helper;
mod script;

View File

@ -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<String>),
// App
CreateApp { name: String, desc: String },
CreateApp {
name: String,
desc: String,
},
AssertAppJson(String),
AssertApp(App),
ReadApp(String),
UpdateApp { name: Option<String>, desc: Option<String> },
UpdateApp {
name: Option<String>,
desc: Option<String>,
},
DeleteApp,
// View
CreateView { name: String, desc: String },
CreateView {
name: String,
desc: String,
data_type: ViewDataType,
},
AssertView(View),
ReadView(String),
UpdateView { name: Option<String>, desc: Option<String> },
UpdateView {
name: Option<String>,
desc: Option<String>,
},
DeleteView,
DeleteViews(Vec<String>),
@ -46,7 +70,10 @@ pub enum FolderScript {
// Sync
AssertCurrentRevId(i64),
AssertNextSyncRevId(Option<i64>),
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>();
workspace
}
pub async fn read_workspace(sdk: &FlowySDKTest, workspace_id: Option<String>) -> Vec<Workspace> {
let request = WorkspaceId { value: workspace_id };
let repeated_workspace = FolderEventBuilder::new(sdk.clone())
.event(ReadWorkspaces)
.payload(request.clone())
.async_send()
.await
.parse::<RepeatedWorkspace>();
let workspaces;
if let Some(workspace_id) = &request.value {
workspaces = repeated_workspace
.into_inner()
.into_iter()
.filter(|workspace| &workspace.id == workspace_id)
.collect::<Vec<Workspace>>();
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>();
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>();
app
}
pub async fn update_app(sdk: &FlowySDKTest, app_id: &str, name: Option<String>, desc: Option<String>) {
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>();
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::<View>()
}
pub async fn update_view(sdk: &FlowySDKTest, view_id: &str, name: Option<String>, desc: Option<String>) {
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<String>) {
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::<TextBlockInfo>()
}
pub async fn read_trash(sdk: &FlowySDKTest) -> RepeatedTrash {
FolderEventBuilder::new(sdk.clone())
.event(ReadTrash)
.async_send()
.await
.parse::<RepeatedTrash>()
}
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;
}

View File

@ -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"]
dart = ["lib-infra/dart"]
flowy_unit_test = ["flowy-sync/flowy_unit_test"]

View File

@ -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"]

View File

@ -7,3 +7,4 @@ pub mod manager;
mod protobuf;
pub mod services;
pub mod util;

View File

@ -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)
}
}
}

View File

@ -113,7 +113,7 @@ impl GridManager {
Ok(grid_editor)
}
fn make_grid_rev_manager(&self, grid_id: &str, pool: Arc<ConnectionPool>) -> FlowyResult<RevisionManager> {
pub fn make_grid_rev_manager(&self, grid_id: &str, pool: Arc<ConnectionPool>) -> FlowyResult<RevisionManager> {
let user_id = self.grid_user.user_id()?;
let disk_cache = Arc::new(SQLiteGridRevisionPersistence::new(&user_id, pool));

View File

@ -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::*;

View File

@ -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<SelectOption>,
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 {
<SingleSelect as ::protobuf::Message>::default_instance()
impl<'a> ::std::default::Default for &'a SingleSelectDescription {
fn default() -> &'a SingleSelectDescription {
<SingleSelectDescription as ::protobuf::Message>::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<SelectOption>>(
"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>(
"SingleSelect",
::protobuf::reflect::MessageDescriptor::new_pb_name::<SingleSelectDescription>(
"SingleSelectDescription",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static SingleSelect {
static instance: ::protobuf::rt::LazyV2<SingleSelect> = ::protobuf::rt::LazyV2::INIT;
instance.get(SingleSelect::new)
fn default_instance() -> &'static SingleSelectDescription {
static instance: ::protobuf::rt::LazyV2<SingleSelectDescription> = ::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<SelectOption>,
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 {
<MultiSelect as ::protobuf::Message>::default_instance()
impl<'a> ::std::default::Default for &'a MultiSelectDescription {
fn default() -> &'a MultiSelectDescription {
<MultiSelectDescription as ::protobuf::Message>::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<SelectOption>>(
"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>(
"MultiSelect",
::protobuf::reflect::MessageDescriptor::new_pb_name::<MultiSelectDescription>(
"MultiSelectDescription",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static MultiSelect {
static instance: ::protobuf::rt::LazyV2<MultiSelect> = ::protobuf::rt::LazyV2::INIT;
instance.get(MultiSelect::new)
fn default_instance() -> &'static MultiSelectDescription {
static instance: ::protobuf::rt::LazyV2<MultiSelectDescription> = ::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<FlowyMoney>>(
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<MoneySymbol>>(
"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<FlowyMoney> {
fn from_i32(value: i32) -> ::std::option::Option<MoneySymbol> {
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>("FlowyMoney", file_descriptor_proto())
::protobuf::reflect::EnumDescriptor::new_pb_name::<MoneySymbol>("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;

View File

@ -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;

View File

@ -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<AnyData, FlowyError>;
}
#[allow(dead_code)]
pub fn stringify_serialize(field: &Field, s: &str) -> Result<AnyData, FlowyError> {
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<String, FlowyError> {
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)
}

View File

@ -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<dyn TypeOptionsBuilder>,
}
impl FieldBuilder {
pub fn new<T: TypeOptionsBuilder + 'static>(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()
}
}

View File

@ -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::*;

View File

@ -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<AnyData, FlowyError>;
}
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<AnyData, FlowyError> {
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<AnyData, FlowyError> {
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<AnyData, FlowyError> {
fn str_to_cell_data(&self, s: &str) -> Result<AnyData, FlowyError> {
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::<i64>() {
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::<i64>() {
Ok(timestamp) => {
@ -142,14 +107,11 @@ impl StringifyAnyData for DateDescription {
}
}
fn str_to_any_data(&self, s: &str) -> Result<AnyData, FlowyError> {
fn str_to_cell_data(&self, s: &str) -> Result<AnyData, FlowyError> {
let timestamp = s
.parse::<i64>()
.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<SelectOption>,
#[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<AnyData, FlowyError> {
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<AnyData, FlowyError> {
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<SelectOption>,
#[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<AnyData, FlowyError> {
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<AnyData, FlowyError> {
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<String> {
@ -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<AnyData, FlowyError> {
fn str_to_cell_data(&self, s: &str) -> Result<AnyData, FlowyError> {
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<Currency>) -> FlowyMoney {
FlowyMoney::from_symbol_str(&money.currency().symbol.to_string())
pub fn from_money(money: &rusty_money::Money<Currency>) -> 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()
}

View File

@ -38,7 +38,7 @@ impl ClientGridEditor {
) -> FlowyResult<Arc<Self>> {
let token = user.token()?;
let cloud = Arc::new(GridRevisionCloudService { token });
let grid_pad = rev_manager.load::<GridPadBuilder>(cloud).await?;
let grid_pad = rev_manager.load::<GridPadBuilder>(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<RevisionManager> {
self.rev_manager.clone()
}
}
async fn load_all_fields(
grid_pad: &GridMetaPad,
kv_persistence: &Arc<GridKVPersistence>,
@ -143,7 +150,7 @@ async fn load_all_fields(
Ok(map)
}
struct GridPadBuilder();
pub struct GridPadBuilder();
impl RevisionObjectBuilder for GridPadBuilder {
type Output = GridMetaPad;

View File

@ -27,7 +27,7 @@ impl ClientGridBlockMetaEditor {
let cloud = Arc::new(GridBlockMetaRevisionCloudService {
token: token.to_owned(),
});
let block_meta_pad = rev_manager.load::<GridBlockMetaPadBuilder>(cloud).await?;
let block_meta_pad = rev_manager.load::<GridBlockMetaPadBuilder>(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();

View File

@ -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;

View File

@ -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<AnyData, FlowyError> {
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<String, FlowyError> {
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)
}

View File

@ -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<String, &'static Currency> {
let mut map: HashMap<String, &'static Currency> = 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<String, &'static Currency> {
#[allow(dead_code)]
pub fn string_to_money(money_str: &str) -> Option<Money<Currency>> {
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<String> {
}
}
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);

View File

@ -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()
}

View File

@ -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;
}

View File

@ -0,0 +1,2 @@
mod grid_test;
mod script;

View File

@ -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<ClientGridEditor>,
}
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<EditorScript>) {
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::<GridPadBuilder>(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()
}

View File

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

View File

@ -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,

View File

@ -67,14 +67,14 @@ impl RevisionManager {
}
}
pub async fn load<B>(&mut self, cloud: Arc<dyn RevisionCloudService>) -> FlowyResult<B::Output>
pub async fn load<B>(&mut self, cloud: Option<Arc<dyn RevisionCloudService>>) -> FlowyResult<B::Output>
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()

View File

@ -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,
};

View File

@ -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()

View File

@ -163,8 +163,19 @@ impl std::default::Default for FieldType {
}
}
impl AsRef<FieldType> for FieldType {
fn as_ref(&self) -> &FieldType {
&self
}
}
impl Into<FieldType> 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<F: Into<FieldType>>(field_type: F, s: &str) -> AnyData {
Self::from_bytes(field_type, s.as_bytes().to_vec())
}
pub fn from_bytes<T: AsRef<[u8]>>(field_type: &FieldType, bytes: T) -> AnyData {
pub fn from_bytes<T: AsRef<[u8]>, F: Into<FieldType>>(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(),
}
}