chore: config update cell

This commit is contained in:
appflowy 2022-03-14 23:16:25 +08:00
parent 196d254278
commit 46be04f94e
18 changed files with 486 additions and 294 deletions

View File

@ -15,7 +15,7 @@ export 'number_description.pbenum.dart';
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)
..e<NumberFormat>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'format', $pb.PbFieldType.OE, defaultOrMaker: NumberFormat.Number, valueOf: NumberFormat.valueOf, enumValues: NumberFormat.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')
@ -25,15 +25,15 @@ class NumberDescription extends $pb.GeneratedMessage {
NumberDescription._() : super();
factory NumberDescription({
MoneySymbol? money,
NumberFormat? format,
$core.int? scale,
$core.String? symbol,
$core.bool? signPositive,
$core.String? name,
}) {
final _result = create();
if (money != null) {
_result.money = money;
if (format != null) {
_result.format = format;
}
if (scale != null) {
_result.scale = scale;
@ -71,13 +71,13 @@ class NumberDescription extends $pb.GeneratedMessage {
static NumberDescription? _defaultInstance;
@$pb.TagNumber(1)
MoneySymbol get money => $_getN(0);
NumberFormat get format => $_getN(0);
@$pb.TagNumber(1)
set money(MoneySymbol v) { setField(1, v); }
set format(NumberFormat v) { setField(1, v); }
@$pb.TagNumber(1)
$core.bool hasMoney() => $_has(0);
$core.bool hasFormat() => $_has(0);
@$pb.TagNumber(1)
void clearMoney() => clearField(1);
void clearFormat() => clearField(1);
@$pb.TagNumber(2)
$core.int get scale => $_getIZ(1);

View File

@ -9,20 +9,22 @@
import 'dart:core' as $core;
import 'package:protobuf/protobuf.dart' as $pb;
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');
class NumberFormat extends $pb.ProtobufEnum {
static const NumberFormat Number = NumberFormat._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Number');
static const NumberFormat USD = NumberFormat._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'USD');
static const NumberFormat CNY = NumberFormat._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CNY');
static const NumberFormat EUR = NumberFormat._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'EUR');
static const $core.List<MoneySymbol> values = <MoneySymbol> [
static const $core.List<NumberFormat> values = <NumberFormat> [
Number,
USD,
CNY,
EUR,
USD,
];
static final $core.Map<$core.int, MoneySymbol> _byValue = $pb.ProtobufEnum.initByValue(values);
static MoneySymbol? valueOf($core.int value) => _byValue[value];
static final $core.Map<$core.int, NumberFormat> _byValue = $pb.ProtobufEnum.initByValue(values);
static NumberFormat? valueOf($core.int value) => _byValue[value];
const MoneySymbol._($core.int v, $core.String n) : super(v, n);
const NumberFormat._($core.int v, $core.String n) : super(v, n);
}

View File

@ -8,23 +8,24 @@
import 'dart:core' as $core;
import 'dart:convert' as $convert;
import 'dart:typed_data' as $typed_data;
@$core.Deprecated('Use moneySymbolDescriptor instead')
const MoneySymbol$json = const {
'1': 'MoneySymbol',
@$core.Deprecated('Use numberFormatDescriptor instead')
const NumberFormat$json = const {
'1': 'NumberFormat',
'2': const [
const {'1': 'CNY', '2': 0},
const {'1': 'EUR', '2': 1},
const {'1': 'USD', '2': 2},
const {'1': 'Number', '2': 0},
const {'1': 'USD', '2': 1},
const {'1': 'CNY', '2': 2},
const {'1': 'EUR', '2': 3},
],
};
/// Descriptor for `MoneySymbol`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List moneySymbolDescriptor = $convert.base64Decode('CgtNb25leVN5bWJvbBIHCgNDTlkQABIHCgNFVVIQARIHCgNVU0QQAg==');
/// Descriptor for `NumberFormat`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List numberFormatDescriptor = $convert.base64Decode('CgxOdW1iZXJGb3JtYXQSCgoGTnVtYmVyEAASBwoDVVNEEAESBwoDQ05ZEAISBwoDRVVSEAM=');
@$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': 'format', '3': 1, '4': 1, '5': 14, '6': '.NumberFormat', '10': 'format'},
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'},
@ -33,4 +34,4 @@ const NumberDescription$json = const {
};
/// Descriptor for `NumberDescription`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List numberDescriptionDescriptor = $convert.base64Decode('ChFOdW1iZXJEZXNjcmlwdGlvbhIiCgVtb25leRgBIAEoDjIMLk1vbmV5U3ltYm9sUgVtb25leRIUCgVzY2FsZRgCIAEoDVIFc2NhbGUSFgoGc3ltYm9sGAMgASgJUgZzeW1ib2wSIwoNc2lnbl9wb3NpdGl2ZRgEIAEoCFIMc2lnblBvc2l0aXZlEhIKBG5hbWUYBSABKAlSBG5hbWU=');
final $typed_data.Uint8List numberDescriptionDescriptor = $convert.base64Decode('ChFOdW1iZXJEZXNjcmlwdGlvbhIlCgZmb3JtYXQYASABKA4yDS5OdW1iZXJGb3JtYXRSBmZvcm1hdBIUCgVzY2FsZRgCIAEoDVIFc2NhbGUSFgoGc3ltYm9sGAMgASgJUgZzeW1ib2wSIwoNc2lnbl9wb3NpdGl2ZRgEIAEoCFIMc2lnblBvc2l0aXZlEhIKBG5hbWUYBSABKAlSBG5hbWU=');

View File

@ -26,7 +26,7 @@
#[derive(PartialEq,Clone,Default)]
pub struct NumberDescription {
// message fields
pub money: MoneySymbol,
pub format: NumberFormat,
pub scale: u32,
pub symbol: ::std::string::String,
pub sign_positive: bool,
@ -47,19 +47,19 @@ impl NumberDescription {
::std::default::Default::default()
}
// .MoneySymbol money = 1;
// .NumberFormat format = 1;
pub fn get_money(&self) -> MoneySymbol {
self.money
pub fn get_format(&self) -> NumberFormat {
self.format
}
pub fn clear_money(&mut self) {
self.money = MoneySymbol::CNY;
pub fn clear_format(&mut self) {
self.format = NumberFormat::Number;
}
// Param is passed by value, moved
pub fn set_money(&mut self, v: MoneySymbol) {
self.money = v;
pub fn set_format(&mut self, v: NumberFormat) {
self.format = v;
}
// uint32 scale = 2;
@ -155,7 +155,7 @@ impl ::protobuf::Message for NumberDescription {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.money, 1, &mut self.unknown_fields)?
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.format, 1, &mut self.unknown_fields)?
},
2 => {
if wire_type != ::protobuf::wire_format::WireTypeVarint {
@ -189,8 +189,8 @@ impl ::protobuf::Message for NumberDescription {
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if self.money != MoneySymbol::CNY {
my_size += ::protobuf::rt::enum_size(1, self.money);
if self.format != NumberFormat::Number {
my_size += ::protobuf::rt::enum_size(1, self.format);
}
if self.scale != 0 {
my_size += ::protobuf::rt::value_size(2, self.scale, ::protobuf::wire_format::WireTypeVarint);
@ -210,8 +210,8 @@ impl ::protobuf::Message for NumberDescription {
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
if self.money != MoneySymbol::CNY {
os.write_enum(1, ::protobuf::ProtobufEnum::value(&self.money))?;
if self.format != NumberFormat::Number {
os.write_enum(1, ::protobuf::ProtobufEnum::value(&self.format))?;
}
if self.scale != 0 {
os.write_uint32(2, self.scale)?;
@ -263,10 +263,10 @@ 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<MoneySymbol>>(
"money",
|m: &NumberDescription| { &m.money },
|m: &mut NumberDescription| { &mut m.money },
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<NumberFormat>>(
"format",
|m: &NumberDescription| { &m.format },
|m: &mut NumberDescription| { &mut m.format },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeUint32>(
"scale",
@ -304,7 +304,7 @@ impl ::protobuf::Message for NumberDescription {
impl ::protobuf::Clear for NumberDescription {
fn clear(&mut self) {
self.money = MoneySymbol::CNY;
self.format = NumberFormat::Number;
self.scale = 0;
self.symbol.clear();
self.sign_positive = false;
@ -326,31 +326,34 @@ impl ::protobuf::reflect::ProtobufValue for NumberDescription {
}
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum MoneySymbol {
CNY = 0,
EUR = 1,
USD = 2,
pub enum NumberFormat {
Number = 0,
USD = 1,
CNY = 2,
EUR = 3,
}
impl ::protobuf::ProtobufEnum for MoneySymbol {
impl ::protobuf::ProtobufEnum for NumberFormat {
fn value(&self) -> i32 {
*self as i32
}
fn from_i32(value: i32) -> ::std::option::Option<MoneySymbol> {
fn from_i32(value: i32) -> ::std::option::Option<NumberFormat> {
match value {
0 => ::std::option::Option::Some(MoneySymbol::CNY),
1 => ::std::option::Option::Some(MoneySymbol::EUR),
2 => ::std::option::Option::Some(MoneySymbol::USD),
0 => ::std::option::Option::Some(NumberFormat::Number),
1 => ::std::option::Option::Some(NumberFormat::USD),
2 => ::std::option::Option::Some(NumberFormat::CNY),
3 => ::std::option::Option::Some(NumberFormat::EUR),
_ => ::std::option::Option::None
}
}
fn values() -> &'static [Self] {
static values: &'static [MoneySymbol] = &[
MoneySymbol::CNY,
MoneySymbol::EUR,
MoneySymbol::USD,
static values: &'static [NumberFormat] = &[
NumberFormat::Number,
NumberFormat::USD,
NumberFormat::CNY,
NumberFormat::EUR,
];
values
}
@ -358,34 +361,34 @@ impl ::protobuf::ProtobufEnum for MoneySymbol {
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::<MoneySymbol>("MoneySymbol", file_descriptor_proto())
::protobuf::reflect::EnumDescriptor::new_pb_name::<NumberFormat>("NumberFormat", file_descriptor_proto())
})
}
}
impl ::std::marker::Copy for MoneySymbol {
impl ::std::marker::Copy for NumberFormat {
}
impl ::std::default::Default for MoneySymbol {
impl ::std::default::Default for NumberFormat {
fn default() -> Self {
MoneySymbol::CNY
NumberFormat::Number
}
}
impl ::protobuf::reflect::ProtobufValue for MoneySymbol {
impl ::protobuf::reflect::ProtobufValue for NumberFormat {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
}
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x18number_description.proto\"\x9e\x01\n\x11NumberDescription\x12\"\n\
\x05money\x18\x01\x20\x01(\x0e2\x0c.MoneySymbolR\x05money\x12\x14\n\x05s\
cale\x18\x02\x20\x01(\rR\x05scale\x12\x16\n\x06symbol\x18\x03\x20\x01(\t\
R\x06symbol\x12#\n\rsign_positive\x18\x04\x20\x01(\x08R\x0csignPositive\
\x12\x12\n\x04name\x18\x05\x20\x01(\tR\x04name*(\n\x0bMoneySymbol\x12\
\x07\n\x03CNY\x10\0\x12\x07\n\x03EUR\x10\x01\x12\x07\n\x03USD\x10\x02b\
\x06proto3\
\n\x18number_description.proto\"\xa1\x01\n\x11NumberDescription\x12%\n\
\x06format\x18\x01\x20\x01(\x0e2\r.NumberFormatR\x06format\x12\x14\n\x05\
scale\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\x0csignPositiv\
e\x12\x12\n\x04name\x18\x05\x20\x01(\tR\x04name*5\n\x0cNumberFormat\x12\
\n\n\x06Number\x10\0\x12\x07\n\x03USD\x10\x01\x12\x07\n\x03CNY\x10\x02\
\x12\x07\n\x03EUR\x10\x03b\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -1,14 +1,15 @@
syntax = "proto3";
message NumberDescription {
MoneySymbol money = 1;
NumberFormat format = 1;
uint32 scale = 2;
string symbol = 3;
bool sign_positive = 4;
string name = 5;
}
enum MoneySymbol {
CNY = 0;
EUR = 1;
USD = 2;
enum NumberFormat {
Number = 0;
USD = 1;
CNY = 2;
EUR = 3;
}

View File

@ -19,7 +19,6 @@ use lib_ot::core::PlainTextAttributes;
use std::collections::HashMap;
use dashmap::mapref::one::Ref;
use std::sync::Arc;
use tokio::sync::RwLock;
@ -84,12 +83,11 @@ impl GridBlockMetaEditorManager {
pub(crate) async fn delete_rows(&self, row_ids: Vec<String>) -> FlowyResult<Vec<GridBlockChangeset>> {
let row_orders = row_ids
.into_iter()
.flat_map(|row_id| match self.block_id_by_row_id.get(&row_id) {
None => None,
Some(block_id) => Some(RowOrder {
.flat_map(|row_id| {
self.block_id_by_row_id.get(&row_id).map(|block_id| RowOrder {
row_id,
block_id: block_id.clone(),
}),
})
})
.collect::<Vec<RowOrder>>();
let mut changesets = vec![];

View File

@ -26,8 +26,8 @@ impl NumberTypeOptionsBuilder {
self
}
pub fn set_money_symbol(mut self, money_symbol: MoneySymbol) -> Self {
self.0.set_money_symbol(money_symbol);
pub fn set_format(mut self, format: NumberFormat) -> Self {
self.0.set_format(format);
self
}

View File

@ -1,6 +1,6 @@
use crate::impl_from_and_to_type_option;
use crate::services::row::StringifyCellData;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_derive::ProtoBuf;
use flowy_error::FlowyError;
use flowy_grid_data_model::entities::{Field, FieldType};
use serde::{Deserialize, Serialize};
@ -38,3 +38,23 @@ fn string_to_bool(bool_str: &str) -> bool {
_ => false,
}
}
#[cfg(test)]
mod tests {
use crate::services::cell::CheckboxDescription;
use crate::services::row::StringifyCellData;
#[test]
fn checkout_box_description_test() {
let description = CheckboxDescription::default();
assert_eq!(description.str_to_cell_data("true").unwrap(), "1".to_owned());
assert_eq!(description.str_to_cell_data("1").unwrap(), "1".to_owned());
assert_eq!(description.str_to_cell_data("yes").unwrap(), "1".to_owned());
assert_eq!(description.str_to_cell_data("false").unwrap(), "0".to_owned());
assert_eq!(description.str_to_cell_data("no").unwrap(), "0".to_owned());
assert_eq!(description.str_to_cell_data("123").unwrap(), "0".to_owned());
assert_eq!(description.str_from_cell_data("1".to_owned()), "1".to_owned());
}
}

View File

@ -1,6 +1,6 @@
use crate::impl_from_and_to_type_option;
use crate::services::row::StringifyCellData;
use crate::services::util::*;
use chrono::format::strftime::StrftimeItems;
use chrono::NaiveDateTime;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
@ -8,6 +8,8 @@ use flowy_error::FlowyError;
use flowy_grid_data_model::entities::{Field, FieldType};
use serde::{Deserialize, Serialize};
use strum_macros::EnumIter;
// Date
#[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)]
pub struct DateDescription {
@ -20,10 +22,6 @@ pub struct DateDescription {
impl_from_and_to_type_option!(DateDescription, FieldType::DateTime);
impl DateDescription {
fn date_time_format_str(&self) -> String {
format!("{} {}", self.date_format.format_str(), self.time_format.format_str())
}
#[allow(dead_code)]
fn today_from_timestamp(&self, timestamp: i64) -> String {
let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0);
@ -34,7 +32,7 @@ impl DateDescription {
let utc: chrono::DateTime<chrono::Utc> = chrono::DateTime::from_utc(naive, chrono::Utc);
let local: chrono::DateTime<chrono::Local> = chrono::DateTime::from(utc);
let fmt_str = self.date_time_format_str();
let fmt_str = format!("{} {}", self.date_format.format_str(), self.time_format.format_str());
let output = format!("{}", local.format_with_items(StrftimeItems::new(&fmt_str)));
output
}
@ -55,14 +53,18 @@ impl StringifyCellData for DateDescription {
}
fn str_to_cell_data(&self, s: &str) -> Result<String, FlowyError> {
let timestamp = s
.parse::<i64>()
.map_err(|e| FlowyError::internal().context(format!("Parse {} to i64 failed: {}", s, e)))?;
let timestamp = match s.parse::<i64>() {
Ok(timestamp) => timestamp,
Err(e) => {
tracing::error!("Parse {} to i64 failed: {}", s, e);
chrono::Utc::now().timestamp()
}
};
Ok(format!("{}", timestamp))
}
}
#[derive(Clone, Debug, Copy, Serialize, Deserialize, ProtoBuf_Enum)]
#[derive(Clone, Debug, Copy, EnumIter, Serialize, Deserialize, ProtoBuf_Enum)]
pub enum DateFormat {
Local = 0,
US = 1,
@ -105,7 +107,7 @@ impl DateFormat {
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize, ProtoBuf_Enum)]
#[derive(Clone, Copy, PartialEq, Eq, EnumIter, Debug, Hash, Serialize, Deserialize, ProtoBuf_Enum)]
pub enum TimeFormat {
TwelveHour = 0,
TwentyFourHour = 1,
@ -143,3 +145,98 @@ impl std::default::Default for TimeFormat {
TimeFormat::TwentyFourHour
}
}
#[cfg(test)]
mod tests {
use crate::services::cell::{DateDescription, DateFormat, TimeFormat};
use crate::services::row::StringifyCellData;
use strum::IntoEnumIterator;
#[test]
fn date_description_date_format_test() {
let mut description = DateDescription::default();
let _timestamp = 1647251762;
for date_format in DateFormat::iter() {
description.date_format = date_format;
match date_format {
DateFormat::Friendly => {
assert_eq!(
"Mar 14,2022 17:56".to_owned(),
description.today_from_timestamp(1647251762)
);
assert_eq!(
"Mar 14,2022 17:56".to_owned(),
description.str_from_cell_data("1647251762".to_owned())
);
}
DateFormat::US => {
assert_eq!(
"2022/03/14 17:56".to_owned(),
description.today_from_timestamp(1647251762)
);
assert_eq!(
"2022/03/14 17:56".to_owned(),
description.str_from_cell_data("1647251762".to_owned())
);
}
DateFormat::ISO => {
assert_eq!(
"2022-03-14 17:56".to_owned(),
description.today_from_timestamp(1647251762)
);
assert_eq!(
"2022-03-14 17:56".to_owned(),
description.str_from_cell_data("1647251762".to_owned())
);
}
DateFormat::Local => {
assert_eq!(
"2022/03/14 17:56".to_owned(),
description.today_from_timestamp(1647251762)
);
assert_eq!(
"2022/03/14 17:56".to_owned(),
description.str_from_cell_data("1647251762".to_owned())
);
}
}
}
}
#[test]
fn date_description_time_format_test() {
let mut description = DateDescription::default();
for time_format in TimeFormat::iter() {
description.time_format = time_format;
match time_format {
TimeFormat::TwentyFourHour => {
assert_eq!(
"Mar 14,2022 17:56".to_owned(),
description.today_from_timestamp(1647251762)
);
assert_eq!(
"Mar 14,2022 17:56".to_owned(),
description.str_from_cell_data("1647251762".to_owned())
);
}
TimeFormat::TwelveHour => {
assert_eq!(
"Mar 14,2022 05:56:02 PM".to_owned(),
description.today_from_timestamp(1647251762)
);
assert_eq!(
"Mar 14,2022 05:56:02 PM".to_owned(),
description.str_from_cell_data("1647251762".to_owned())
);
}
}
}
}
#[test]
fn date_description_invalid_data_test() {
let description = DateDescription::default();
description.str_to_cell_data("he").unwrap();
}
}

View File

@ -1,25 +1,62 @@
use crate::impl_from_and_to_type_option;
use crate::services::row::StringifyCellData;
use crate::services::util::*;
use chrono::format::strftime::StrftimeItems;
use chrono::NaiveDateTime;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::FlowyError;
use flowy_grid_data_model::entities::{Field, FieldType};
use lazy_static::lazy_static;
use rust_decimal::prelude::Zero;
use rust_decimal::Decimal;
use rusty_money::{
iso::{Currency, CNY, EUR, USD},
Money,
};
use rusty_money::iso::{Currency, CNY, EUR, USD};
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use strum::IntoEnumIterator;
use strum_macros::EnumIter;
lazy_static! {
static ref STRIP_SYMBOL: Vec<String> = make_strip_symbol();
}
#[derive(Clone, Copy, Debug, EnumIter, Serialize, Deserialize, ProtoBuf_Enum)]
pub enum NumberFormat {
Number = 0,
USD = 1,
CNY = 2,
EUR = 3,
}
impl std::default::Default for NumberFormat {
fn default() -> Self {
NumberFormat::Number
}
}
impl NumberFormat {
pub fn symbol(&self) -> String {
match self {
NumberFormat::Number => "".to_string(),
NumberFormat::USD => USD.symbol.to_string(),
NumberFormat::CNY => CNY.symbol.to_string(),
NumberFormat::EUR => EUR.symbol.to_string(),
}
}
#[allow(dead_code)]
pub fn code(&self) -> String {
match self {
NumberFormat::Number => "".to_string(),
NumberFormat::USD => USD.iso_alpha_code.to_string(),
NumberFormat::CNY => CNY.iso_alpha_code.to_string(),
NumberFormat::EUR => EUR.iso_alpha_code.to_string(),
}
}
}
// Number
#[derive(Clone, Debug, Serialize, Deserialize, ProtoBuf)]
pub struct NumberDescription {
#[pb(index = 1)]
pub money: MoneySymbol,
pub format: NumberFormat,
#[pb(index = 2)]
pub scale: u32,
@ -37,10 +74,10 @@ 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();
let format = NumberFormat::default();
let symbol = format.symbol();
NumberDescription {
money,
format,
scale: 0,
symbol,
sign_positive: true,
@ -50,101 +87,156 @@ impl std::default::Default for NumberDescription {
}
impl NumberDescription {
pub fn set_money_symbol(&mut self, money_symbol: MoneySymbol) {
self.money = money_symbol;
self.symbol = money_symbol.symbol_str();
pub fn set_format(&mut self, format: NumberFormat) {
self.format = format;
self.symbol = format.symbol();
}
fn money_from_str(&self, s: &str) -> Option<String> {
match Decimal::from_str(s) {
Ok(mut decimal) => {
match decimal.set_scale(self.scale) {
Ok(_) => {}
Err(e) => {
tracing::error!("Set decimal scale failed: {:?}", e);
}
}
decimal.set_sign_positive(self.sign_positive);
Some(self.money.with_decimal(decimal).to_string())
}
fn decimal_from_str(&self, s: &str) -> Decimal {
let mut decimal = Decimal::from_str(s).unwrap_or(Decimal::zero());
match decimal.set_scale(self.scale) {
Ok(_) => {}
Err(e) => {
tracing::error!("Parser money from {} failed: {:?}", s, e);
None
tracing::error!("Set decimal scale failed: {:?}", e);
}
}
decimal.set_sign_positive(self.sign_positive);
decimal
}
fn money_from_str(&self, s: &str, currency: &'static Currency) -> String {
let decimal = self.decimal_from_str(s);
let money = rusty_money::Money::from_decimal(decimal, currency);
money.to_string()
}
fn strip_symbol(&self, s: &str) -> String {
let mut s = String::from(s);
if !s.chars().all(char::is_numeric) {
s.retain(|c| !STRIP_SYMBOL.contains(&c.to_string()));
}
s
}
}
impl StringifyCellData for NumberDescription {
fn str_from_cell_data(&self, data: String) -> String {
match self.money_from_str(&data) {
Some(money_str) => money_str,
None => String::default(),
match self.format {
NumberFormat::Number => data,
NumberFormat::USD => self.money_from_str(&data, USD),
NumberFormat::CNY => self.money_from_str(&data, CNY),
NumberFormat::EUR => self.money_from_str(&data, EUR),
}
}
fn str_to_cell_data(&self, s: &str) -> Result<String, FlowyError> {
let strip_symbol_money = strip_money_symbol(s);
let decimal = Decimal::from_str(&strip_symbol_money).map_err(|err| FlowyError::internal().context(err))?;
Ok(decimal.to_string())
Ok(self.strip_symbol(s))
}
}
#[derive(Clone, Copy, Debug, EnumIter, Serialize, Deserialize, ProtoBuf_Enum)]
pub enum MoneySymbol {
CNY = 0,
EUR = 1,
USD = 2,
}
impl std::default::Default for MoneySymbol {
fn default() -> Self {
MoneySymbol::USD
fn make_strip_symbol() -> Vec<String> {
let mut symbols = vec![",".to_owned(), ".".to_owned()];
for format in NumberFormat::iter() {
symbols.push(format.symbol());
}
symbols
}
impl MoneySymbol {
// Currency list https://docs.rs/rusty-money/0.4.0/rusty_money/iso/index.html
pub fn from_symbol_str(s: &str) -> MoneySymbol {
match s {
"CNY" => MoneySymbol::CNY,
"EUR" => MoneySymbol::EUR,
"USD" => MoneySymbol::USD,
_ => MoneySymbol::CNY,
#[cfg(test)]
mod tests {
use crate::services::cell::{NumberDescription, NumberFormat};
use crate::services::row::StringifyCellData;
use strum::IntoEnumIterator;
#[test]
fn number_description_test() {
let mut description = NumberDescription::default();
assert_eq!(description.str_to_cell_data("¥18,443").unwrap(), "18443".to_owned());
assert_eq!(description.str_to_cell_data("$18,443").unwrap(), "18443".to_owned());
assert_eq!(description.str_to_cell_data("€18.443").unwrap(), "18443".to_owned());
for format in NumberFormat::iter() {
description.format = format;
match format {
NumberFormat::Number => {
assert_eq!(description.str_from_cell_data("18443".to_owned()), "18443".to_owned());
}
NumberFormat::USD => {
assert_eq!(description.str_from_cell_data("18443".to_owned()), "$18,443".to_owned());
}
NumberFormat::CNY => {
assert_eq!(description.str_from_cell_data("18443".to_owned()), "¥18,443".to_owned());
}
NumberFormat::EUR => {
assert_eq!(description.str_from_cell_data("18443".to_owned()), "€18.443".to_owned());
}
}
}
}
pub fn from_money(money: &rusty_money::Money<Currency>) -> MoneySymbol {
MoneySymbol::from_symbol_str(&money.currency().symbol.to_string())
}
#[test]
fn number_description_scale_test() {
let mut description = NumberDescription::default();
description.scale = 1;
pub fn currency(&self) -> &'static Currency {
match self {
MoneySymbol::CNY => CNY,
MoneySymbol::EUR => EUR,
MoneySymbol::USD => USD,
for format in NumberFormat::iter() {
description.format = format;
match format {
NumberFormat::Number => {
assert_eq!(description.str_from_cell_data("18443".to_owned()), "18443".to_owned());
}
NumberFormat::USD => {
assert_eq!(
description.str_from_cell_data("18443".to_owned()),
"$1,844.3".to_owned()
);
}
NumberFormat::CNY => {
assert_eq!(
description.str_from_cell_data("18443".to_owned()),
"¥1,844.3".to_owned()
);
}
NumberFormat::EUR => {
assert_eq!(
description.str_from_cell_data("18443".to_owned()),
"€1.844,3".to_owned()
);
}
}
}
}
// string_to_money("¥18,443").unwrap();
// string_to_money("$18,443").unwrap();
// string_to_money("€18,443").unwrap();
pub fn code(&self) -> String {
self.currency().iso_alpha_code.to_string()
}
#[test]
fn number_description_sign_test() {
let mut description = NumberDescription::default();
description.sign_positive = false;
pub fn symbol_str(&self) -> String {
self.currency().symbol.to_string()
}
pub fn zero(&self) -> Money<Currency> {
let mut decimal = Decimal::new(0, 0);
decimal.set_sign_positive(true);
self.with_decimal(decimal)
}
pub fn with_decimal(&self, decimal: Decimal) -> Money<Currency> {
let money = rusty_money::Money::from_decimal(decimal, self.currency());
money
for format in NumberFormat::iter() {
description.format = format;
match format {
NumberFormat::Number => {
assert_eq!(description.str_from_cell_data("18443".to_owned()), "18443".to_owned());
}
NumberFormat::USD => {
assert_eq!(
description.str_from_cell_data("18443".to_owned()),
"-$18,443".to_owned()
);
}
NumberFormat::CNY => {
assert_eq!(
description.str_from_cell_data("18443".to_owned()),
"-¥18,443".to_owned()
);
}
NumberFormat::EUR => {
assert_eq!(
description.str_from_cell_data("18443".to_owned()),
"-€18.443".to_owned()
);
}
}
}
}
}

View File

@ -1,7 +1,7 @@
use crate::impl_from_and_to_type_option;
use crate::services::row::StringifyCellData;
use crate::services::util::*;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_derive::ProtoBuf;
use flowy_error::FlowyError;
use flowy_grid_data_model::entities::{Field, FieldType};
use serde::{Deserialize, Serialize};
@ -23,7 +23,7 @@ impl StringifyCellData for SingleSelectDescription {
}
fn str_to_cell_data(&self, s: &str) -> Result<String, FlowyError> {
Ok(s.to_owned())
Ok(select_option_id_from_data(s.to_owned(), true))
}
}
@ -43,10 +43,22 @@ impl StringifyCellData for MultiSelectDescription {
}
fn str_to_cell_data(&self, s: &str) -> Result<String, FlowyError> {
Ok(s.to_owned())
Ok(select_option_id_from_data(s.to_owned(), false))
}
}
fn select_option_id_from_data(data: String, is_single_select: bool) -> String {
if !is_single_select {
return data;
}
let select_option_ids = data.split(',').collect::<Vec<&str>>();
if select_option_ids.is_empty() {
return "".to_owned();
}
select_option_ids.split_first().unwrap().0.to_string()
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)]
pub struct SelectOption {
#[pb(index = 1)]
@ -68,3 +80,18 @@ impl SelectOption {
}
}
}
#[cfg(test)]
mod tests {
use crate::services::cell::{MultiSelectDescription, SingleSelectDescription};
use crate::services::row::StringifyCellData;
#[test]
fn selection_description_test() {
let description = SingleSelectDescription::default();
assert_eq!(description.str_to_cell_data("1,2,3").unwrap(), "1".to_owned());
let description = MultiSelectDescription::default();
assert_eq!(description.str_to_cell_data("1,2,3").unwrap(), "1,2,3".to_owned());
}
}

View File

@ -1,7 +1,7 @@
use crate::impl_from_and_to_type_option;
use crate::services::row::StringifyCellData;
use crate::services::util::*;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_derive::ProtoBuf;
use flowy_error::FlowyError;
use flowy_grid_data_model::entities::{Field, FieldType};
use serde::{Deserialize, Serialize};

View File

@ -1,5 +1,5 @@
use crate::manager::GridUser;
use crate::services::grid_meta_editor::GridBlockMetaEditorManager;
use crate::services::block_meta_editor::GridBlockMetaEditorManager;
use crate::services::kv_persistence::{GridKVPersistence, KVTransaction};
use bytes::Bytes;
use flowy_collaboration::client_grid::{GridChange, GridMetaPad};

View File

@ -1,8 +1,8 @@
mod util;
pub mod block_meta_editor;
pub mod cell;
pub mod field;
pub mod grid_editor;
pub mod grid_meta_editor;
pub mod kv_persistence;
pub mod row;

View File

@ -1,5 +1,5 @@
use crate::services::row::stringify_deserialize;
use flowy_grid_data_model::entities::{Cell, CellMeta, Field, RepeatedRowOrder, Row, RowMeta, RowOrder};
use flowy_grid_data_model::entities::{Cell, CellMeta, Field, Row, RowMeta, RowOrder};
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use std::collections::HashMap;

View File

@ -1,103 +1,35 @@
use crate::services::cell::MoneySymbol;
use flowy_error::FlowyError;
use flowy_grid_data_model::entities::{AnyData, Field, FieldType};
use lazy_static::lazy_static;
use rust_decimal::Decimal;
use rusty_money::{iso::Currency, Money};
use std::collections::HashMap;
use std::str::FromStr;
use strum::IntoEnumIterator;
lazy_static! {
static ref CURRENCIES_BY_SYMBOL: HashMap<String, &'static Currency> = generate_currency_by_symbol();
}
#[allow(dead_code)]
fn generate_currency_by_symbol() -> HashMap<String, &'static Currency> {
let mut map: HashMap<String, &'static Currency> = HashMap::new();
for money in MoneySymbol::iter() {
map.insert(money.symbol_str(), money.currency());
}
map
}
#[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 = MoneySymbol::from_symbol_str("CNY").currency();
if process_money_str.is_empty() {
return None;
}
return if process_money_str.chars().all(char::is_numeric) {
match Money::from_str(&process_money_str, default_currency) {
Ok(money) => Some(money),
Err(_) => None,
}
} else {
let symbol = process_money_str.chars().next().unwrap().to_string();
let mut currency = default_currency;
for key in CURRENCIES_BY_SYMBOL.keys() {
if symbol.eq(key) {
currency = CURRENCIES_BY_SYMBOL.get(key).unwrap();
crop_letters(&mut process_money_str, 1);
}
}
match Money::from_str(&process_money_str, currency) {
Ok(money) => Some(money),
Err(_) => None,
}
};
}
#[allow(dead_code)]
pub fn money_from_str(s: &str) -> Option<String> {
match Decimal::from_str(s) {
Ok(mut decimal) => {
match decimal.set_scale(0) {
Ok(_) => {}
Err(e) => {
tracing::error!("Set scale failed. {:?}", e);
}
}
decimal.set_sign_positive(true);
Some(MoneySymbol::USD.with_decimal(decimal).to_string())
}
Err(e) => {
tracing::debug!("Format {} to money failed, {:?}", s, e);
None
}
}
}
pub fn strip_money_symbol(money_str: &str) -> String {
let mut process_money_str = String::from(money_str);
if !process_money_str.chars().all(char::is_numeric) {
let symbol = process_money_str.chars().next().unwrap().to_string();
for key in CURRENCIES_BY_SYMBOL.keys() {
if symbol.eq(key) {
crop_letters(&mut process_money_str, 1);
}
}
}
process_money_str
}
fn crop_letters(s: &mut String, pos: usize) {
match s.char_indices().nth(pos) {
Some((pos, _)) => {
s.drain(..pos);
}
None => {
s.clear();
}
}
}
//
// #[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 = MoneySymbol::from_symbol_str("CNY").currency();
//
// if process_money_str.is_empty() {
// return None;
// }
//
// return if process_money_str.chars().all(char::is_numeric) {
// match Money::from_str(&process_money_str, default_currency) {
// Ok(money) => Some(money),
// Err(_) => None,
// }
// } else {
// let symbol = process_money_str.chars().next().unwrap().to_string();
// let mut currency = default_currency;
//
// for key in CURRENCIES_BY_SYMBOL.keys() {
// if symbol.eq(key) {
// currency = CURRENCIES_BY_SYMBOL.get(key).unwrap();
// crop_letters(&mut process_money_str, 1);
// }
// }
//
// match Money::from_str(&process_money_str, currency) {
// Ok(money) => Some(money),
// Err(_) => None,
// }
// };
// }
pub fn uuid() -> String {
uuid::Uuid::new_v4().to_string()

View File

@ -1,10 +1,8 @@
use crate::grid::script::EditorScript::*;
use crate::grid::script::*;
use flowy_grid::services::field::{SelectOption, SingleSelectDescription};
use flowy_grid::services::row::CreateRowContextBuilder;
use flowy_grid_data_model::entities::{
FieldChangeset, FieldType, GridBlock, GridBlockChangeset, Row, RowMetaChangeset,
};
use flowy_grid::services::cell::*;
use flowy_grid::services::row::{CreateRowContextBuilder, StringifyCellData};
use flowy_grid_data_model::entities::{FieldChangeset, FieldType, GridBlock, GridBlockChangeset, RowMetaChangeset};
#[tokio::test]
async fn default_grid_test() {
@ -249,19 +247,40 @@ async fn grid_update_cell() {
builder = builder.add_cell(&field.id, "hello world".to_owned());
}
FieldType::Number => {
builder = builder.add_cell(&field.id, "123".to_owned());
let description = NumberDescription::from(field);
let data = description.str_to_cell_data("¥18,443").unwrap();
builder = builder.add_cell(&field.id, data);
}
FieldType::DateTime => {
builder = builder.add_cell(&field.id, "March 8, 2022".to_owned());
let description = DateDescription::from(field);
let data = description.str_to_cell_data("1647251762").unwrap();
builder = builder.add_cell(&field.id, data);
}
FieldType::SingleSelect => {
let description = SingleSelectDescription::from(field);
let options = description.options.first().unwrap();
let data = description.str_to_cell_data(&options.id).unwrap();
builder = builder.add_cell(&field.id, data);
}
FieldType::MultiSelect => {
let description = MultiSelectDescription::from(field);
let options = description
.options
.iter()
.map(|option| option.id.clone())
.collect::<Vec<_>>()
.join(",");
let data = description.str_to_cell_data(&options).unwrap();
builder = builder.add_cell(&field.id, data);
}
FieldType::SingleSelect => {}
FieldType::MultiSelect => {}
FieldType::Checkbox => {
builder = builder.add_cell(&field.id, "1".to_owned());
let description = CheckboxDescription::from(field);
let data = description.str_to_cell_data("false").unwrap();
builder = builder.add_cell(&field.id, data);
}
}
}
let context = builder.build();
let scripts = vec![AssertRowCount(3), CreateRow { context }];
let scripts = vec![AssertRowCount(3), CreateRow { context }, AssertGridMetaPad];
test.run_scripts(scripts).await;
}

View File

@ -3,7 +3,7 @@ use flowy_grid::services::field::*;
use flowy_grid::services::grid_editor::{ClientGridEditor, GridPadBuilder};
use flowy_grid::services::row::CreateRowContext;
use flowy_grid_data_model::entities::{
CellMetaChangeset, Field, FieldChangeset, FieldType, GridBlock, GridBlockChangeset, Row, RowMeta, RowMetaChangeset,
CellMetaChangeset, Field, FieldChangeset, FieldType, GridBlock, GridBlockChangeset, RowMeta, RowMetaChangeset,
};
use flowy_sync::REVISION_WRITE_INTERVAL_IN_MILLIS;
use flowy_test::helper::ViewTest;
@ -104,7 +104,7 @@ impl GridEditorTest {
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;
let _cache = rev_manager.revision_cache().await;
match script {
EditorScript::CreateField { field } => {