chore: transform cell data

This commit is contained in:
appflowy 2022-04-02 21:17:20 +08:00
parent 2262dd0d7b
commit ca45673a9e
11 changed files with 376 additions and 139 deletions

View File

@ -148,9 +148,10 @@ impl GridBlockMetaEditorManager {
}
// Optimization: Using the shared memory(Arc, Cow,etc.) to reduce memory usage.
#[allow(dead_code)]
pub async fn get_cell_metas(
&self,
block_ids: Option<String>,
block_ids: Vec<String>,
field_id: &str,
row_ids: Option<Vec<String>>,
) -> FlowyResult<Vec<CellMeta>> {

View File

@ -1,11 +1,12 @@
use crate::impl_type_option;
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
use crate::services::row::CellDataSerde;
use crate::services::row::{CellDataSerde, TypeOptionCellData};
use bytes::Bytes;
use flowy_derive::ProtoBuf;
use flowy_error::FlowyError;
use flowy_grid_data_model::entities::{FieldMeta, FieldType, TypeOptionDataEntity, TypeOptionDataEntry};
use serde::{Deserialize, Serialize};
use std::str::FromStr;
#[derive(Default)]
pub struct CheckboxTypeOptionBuilder(CheckboxTypeOption);
@ -36,17 +37,30 @@ pub struct CheckboxTypeOption {
}
impl_type_option!(CheckboxTypeOption, FieldType::Checkbox);
const YES: &str = "Yes";
const NO: &str = "No";
impl CellDataSerde for CheckboxTypeOption {
fn deserialize_cell_data(&self, data: String) -> String {
data
fn deserialize_cell_data(&self, data: String, _field_meta: &FieldMeta) -> String {
if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) {
if !type_option_cell_data.is_text() || !type_option_cell_data.is_checkbox() {
return String::new();
}
let cell_data = type_option_cell_data.data;
if cell_data == YES || cell_data == NO {
return cell_data;
}
}
String::new()
}
fn serialize_cell_data(&self, data: &str) -> Result<String, FlowyError> {
let s = match string_to_bool(data) {
true => "No",
false => "Yes",
true => YES,
false => NO,
};
Ok(s.to_owned())
Ok(TypeOptionCellData::new(s, self.field_type()).json())
}
}
@ -66,11 +80,15 @@ fn string_to_bool(bool_str: &str) -> bool {
#[cfg(test)]
mod tests {
use crate::services::field::CheckboxTypeOption;
use crate::services::field::FieldBuilder;
use crate::services::row::CellDataSerde;
use flowy_grid_data_model::entities::FieldType;
#[test]
fn checkout_box_description_test() {
let type_option = CheckboxTypeOption::default();
let field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build();
assert_eq!(type_option.serialize_cell_data("true").unwrap(), "1".to_owned());
assert_eq!(type_option.serialize_cell_data("1").unwrap(), "1".to_owned());
assert_eq!(type_option.serialize_cell_data("yes").unwrap(), "1".to_owned());
@ -79,6 +97,9 @@ mod tests {
assert_eq!(type_option.serialize_cell_data("no").unwrap(), "0".to_owned());
assert_eq!(type_option.serialize_cell_data("123").unwrap(), "0".to_owned());
assert_eq!(type_option.deserialize_cell_data("1".to_owned()), "1".to_owned());
assert_eq!(
type_option.deserialize_cell_data("1".to_owned(), &field_meta),
"1".to_owned()
);
}
}

View File

@ -1,5 +1,5 @@
use crate::impl_type_option;
use crate::services::row::CellDataSerde;
use crate::services::row::{CellDataSerde, TypeOptionCellData};
use bytes::Bytes;
use chrono::format::strftime::StrftimeItems;
use chrono::NaiveDateTime;
@ -7,6 +7,7 @@ use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::FlowyError;
use flowy_grid_data_model::entities::{FieldMeta, FieldType, TypeOptionDataEntity, TypeOptionDataEntry};
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
use strum_macros::EnumIter;
@ -32,25 +33,34 @@ impl DateTypeOption {
fn today_from_native(&self, naive: chrono::NaiveDateTime) -> String {
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 = format!("{} {}", self.date_format.format_str(), self.time_format.format_str());
let output = format!("{}", local.format_with_items(StrftimeItems::new(&fmt_str)));
let output = format!("{}", local.format_with_items(StrftimeItems::new(&self.fmt_str())));
output
}
fn fmt_str(&self) -> String {
format!("{} {}", self.date_format.format_str(), self.time_format.format_str())
}
}
impl CellDataSerde for DateTypeOption {
fn deserialize_cell_data(&self, data: String) -> String {
match data.parse::<i64>() {
Ok(timestamp) => {
let native = NaiveDateTime::from_timestamp(timestamp, 0);
self.today_from_native(native)
fn deserialize_cell_data(&self, data: String, _field_meta: &FieldMeta) -> String {
if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) {
if !type_option_cell_data.is_date() {
return String::new();
}
Err(e) => {
tracing::debug!("DateDescription format {} fail. error: {:?}", data, e);
String::new()
let cell_data = type_option_cell_data.data;
if let Ok(timestamp) = cell_data.parse::<i64>() {
let native = NaiveDateTime::from_timestamp(timestamp, 0);
return self.today_from_native(native);
}
if NaiveDateTime::parse_from_str(&cell_data, &self.fmt_str()).is_ok() {
return cell_data;
}
}
String::new()
}
fn serialize_cell_data(&self, data: &str) -> Result<String, FlowyError> {
@ -58,7 +68,8 @@ impl CellDataSerde for DateTypeOption {
tracing::error!("Parse {} to i64 failed: {}", data, e);
return Err(FlowyError::internal().context(e));
};
Ok(data.to_owned())
Ok(TypeOptionCellData::new(data, self.field_type()).json())
}
}
@ -172,56 +183,59 @@ impl std::default::Default for TimeFormat {
#[cfg(test)]
mod tests {
use crate::services::field::FieldBuilder;
use crate::services::field::{DateFormat, DateTypeOption, TimeFormat};
use crate::services::row::CellDataSerde;
use crate::services::row::{CellDataSerde, TypeOptionCellData};
use flowy_grid_data_model::entities::FieldType;
use strum::IntoEnumIterator;
#[test]
fn date_description_date_format_test() {
let mut description = DateTypeOption::default();
let _timestamp = 1647251762;
fn date_description_invalid_input_test() {
let type_option = DateTypeOption::default();
let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build();
assert_eq!(
"".to_owned(),
type_option.deserialize_cell_data("1e".to_owned(), &field_meta)
);
}
#[test]
fn date_description_date_format_test() {
let mut type_option = DateTypeOption::default();
let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build();
for date_format in DateFormat::iter() {
description.date_format = date_format;
type_option.date_format = date_format;
match date_format {
DateFormat::Friendly => {
assert_eq!(
"Mar 14,2022 17:56".to_owned(),
description.today_from_timestamp(1647251762)
type_option.deserialize_cell_data(data("1647251762"), &field_meta)
);
assert_eq!(
"Mar 14,2022 17:56".to_owned(),
description.deserialize_cell_data("1647251762".to_owned())
type_option.deserialize_cell_data(data("Mar 14,2022 17:56"), &field_meta)
);
}
DateFormat::US => {
assert_eq!(
"2022/03/14 17:56".to_owned(),
description.today_from_timestamp(1647251762)
type_option.deserialize_cell_data(data("1647251762"), &field_meta)
);
assert_eq!(
"2022/03/14 17:56".to_owned(),
description.deserialize_cell_data("1647251762".to_owned())
type_option.deserialize_cell_data(data("2022/03/14 17:56"), &field_meta)
);
}
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.deserialize_cell_data("1647251762".to_owned())
type_option.deserialize_cell_data(data("1647251762"), &field_meta)
);
}
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.deserialize_cell_data("1647251762".to_owned())
type_option.deserialize_cell_data(data("1647251762"), &field_meta)
);
}
}
@ -230,28 +244,29 @@ mod tests {
#[test]
fn date_description_time_format_test() {
let mut description = DateTypeOption::default();
let mut type_option = DateTypeOption::default();
let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build();
for time_format in TimeFormat::iter() {
description.time_format = time_format;
type_option.time_format = time_format;
match time_format {
TimeFormat::TwentyFourHour => {
assert_eq!(
"Mar 14,2022 17:56".to_owned(),
description.today_from_timestamp(1647251762)
type_option.today_from_timestamp(1647251762)
);
assert_eq!(
"Mar 14,2022 17:56".to_owned(),
description.deserialize_cell_data("1647251762".to_owned())
type_option.deserialize_cell_data(data("1647251762"), &field_meta)
);
}
TimeFormat::TwelveHour => {
assert_eq!(
"Mar 14,2022 05:56:02 PM".to_owned(),
description.today_from_timestamp(1647251762)
type_option.today_from_timestamp(1647251762)
);
assert_eq!(
"Mar 14,2022 05:56:02 PM".to_owned(),
description.deserialize_cell_data("1647251762".to_owned())
type_option.deserialize_cell_data(data("1647251762"), &field_meta)
);
}
}
@ -264,4 +279,8 @@ mod tests {
let type_option = DateTypeOption::default();
type_option.serialize_cell_data("he").unwrap();
}
fn data(s: &str) -> String {
TypeOptionCellData::new(s, FieldType::DateTime).json()
}
}

View File

@ -1,10 +1,10 @@
use crate::impl_type_option;
use crate::services::row::CellDataSerde;
use crate::services::row::{CellDataSerde, TypeOptionCellData};
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::FlowyError;
use flowy_grid_data_model::entities::{FieldMeta, FieldType, TypeOptionDataEntity, TypeOptionDataEntry};
use lazy_static::lazy_static;
use rust_decimal::prelude::Zero;
use rust_decimal::Decimal;
use rusty_money::iso::{Currency, CNY, EUR, USD};
use serde::{Deserialize, Serialize};
@ -76,6 +76,42 @@ pub struct NumberTypeOption {
}
impl_type_option!(NumberTypeOption, FieldType::Number);
impl CellDataSerde for NumberTypeOption {
fn deserialize_cell_data(&self, data: String, _field_meta: &FieldMeta) -> String {
if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) {
if type_option_cell_data.is_date() {
return String::new();
}
let cell_data = type_option_cell_data.data;
match self.format {
NumberFormat::Number => {
if cell_data.parse::<i64>().is_ok() {
cell_data
} else {
String::new()
}
}
NumberFormat::USD => self.money_from_str(&cell_data, USD),
NumberFormat::CNY => self.money_from_str(&cell_data, CNY),
NumberFormat::EUR => self.money_from_str(&cell_data, EUR),
}
} else {
String::new()
}
}
fn serialize_cell_data(&self, data: &str) -> Result<String, FlowyError> {
let data = self.strip_symbol(data);
if !data.chars().all(char::is_numeric) {
return Err(FlowyError::invalid_data().context("Should only contain numbers"));
}
Ok(TypeOptionCellData::new(&data, self.field_type()).json())
}
}
impl std::default::Default for NumberTypeOption {
fn default() -> Self {
let format = NumberFormat::default();
@ -96,22 +132,21 @@ impl NumberTypeOption {
self.symbol = format.symbol();
}
fn decimal_from_str(&self, s: &str) -> Decimal {
let mut decimal = Decimal::from_str(s).unwrap_or_else(|_| Decimal::zero());
match decimal.set_scale(self.scale) {
Ok(_) => {}
Err(e) => {
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()
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);
let money = rusty_money::Money::from_decimal(decimal, currency);
money.to_string()
}
Err(_) => String::new(),
}
}
fn strip_symbol(&self, s: &str) -> String {
@ -158,26 +193,6 @@ impl NumberFormat {
}
}
impl CellDataSerde for NumberTypeOption {
fn deserialize_cell_data(&self, data: String) -> String {
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 serialize_cell_data(&self, data: &str) -> Result<String, FlowyError> {
let data = self.strip_symbol(data);
if !data.chars().all(char::is_numeric) {
return Err(FlowyError::invalid_data().context("Should only contain numbers"));
}
Ok(data)
}
}
fn make_strip_symbol() -> Vec<String> {
let mut symbols = vec![",".to_owned(), ".".to_owned()];
for format in NumberFormat::iter() {
@ -188,41 +203,60 @@ fn make_strip_symbol() -> Vec<String> {
#[cfg(test)]
mod tests {
use crate::services::field::FieldBuilder;
use crate::services::field::{NumberFormat, NumberTypeOption};
use crate::services::row::CellDataSerde;
use crate::services::row::{CellDataSerde, TypeOptionCellData};
use flowy_grid_data_model::entities::FieldType;
use strum::IntoEnumIterator;
#[test]
fn number_description_invalid_input_test() {
let type_option = NumberTypeOption::default();
let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build();
assert_eq!("".to_owned(), type_option.deserialize_cell_data(data(""), &field_meta));
assert_eq!(
"".to_owned(),
type_option.deserialize_cell_data(data("abc"), &field_meta)
);
}
#[test]
fn number_description_test() {
let mut type_option = NumberTypeOption::default();
assert_eq!(type_option.serialize_cell_data("¥18,443").unwrap(), "18443".to_owned());
assert_eq!(type_option.serialize_cell_data("$18,443").unwrap(), "18443".to_owned());
assert_eq!(type_option.serialize_cell_data("€18.443").unwrap(), "18443".to_owned());
let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build();
assert_eq!(type_option.strip_symbol("¥18,443"), "18443".to_owned());
assert_eq!(type_option.strip_symbol("$18,443"), "18443".to_owned());
assert_eq!(type_option.strip_symbol("€18.443"), "18443".to_owned());
for format in NumberFormat::iter() {
type_option.format = format;
match format {
NumberFormat::Number => {
assert_eq!(
type_option.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data(data("18443"), &field_meta),
"18443".to_owned()
);
}
NumberFormat::USD => {
assert_eq!(
type_option.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data(data("18443"), &field_meta),
"$18,443".to_owned()
);
assert_eq!(type_option.deserialize_cell_data(data(""), &field_meta), "".to_owned());
assert_eq!(
type_option.deserialize_cell_data(data("abc"), &field_meta),
"".to_owned()
);
}
NumberFormat::CNY => {
assert_eq!(
type_option.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data(data("18443"), &field_meta),
"¥18,443".to_owned()
);
}
NumberFormat::EUR => {
assert_eq!(
type_option.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data(data("18443"), &field_meta),
"€18.443".to_owned()
);
}
@ -230,37 +264,42 @@ mod tests {
}
}
fn data(s: &str) -> String {
TypeOptionCellData::new(s, FieldType::Number).json()
}
#[test]
fn number_description_scale_test() {
let mut type_option = NumberTypeOption {
scale: 1,
..Default::default()
};
let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build();
for format in NumberFormat::iter() {
type_option.format = format;
match format {
NumberFormat::Number => {
assert_eq!(
type_option.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data(data("18443"), &field_meta),
"18443".to_owned()
);
}
NumberFormat::USD => {
assert_eq!(
type_option.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data(data("18443"), &field_meta),
"$1,844.3".to_owned()
);
}
NumberFormat::CNY => {
assert_eq!(
type_option.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data(data("18443"), &field_meta),
"¥1,844.3".to_owned()
);
}
NumberFormat::EUR => {
assert_eq!(
type_option.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data(data("18443"), &field_meta),
"€1.844,3".to_owned()
);
}
@ -274,31 +313,32 @@ mod tests {
sign_positive: false,
..Default::default()
};
let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build();
for format in NumberFormat::iter() {
type_option.format = format;
match format {
NumberFormat::Number => {
assert_eq!(
type_option.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data(data("18443"), &field_meta),
"18443".to_owned()
);
}
NumberFormat::USD => {
assert_eq!(
type_option.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data(data("18443"), &field_meta),
"-$18,443".to_owned()
);
}
NumberFormat::CNY => {
assert_eq!(
type_option.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data(data("18443"), &field_meta),
"-¥18,443".to_owned()
);
}
NumberFormat::EUR => {
assert_eq!(
type_option.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data(data("18443"), &field_meta),
"-€18.443".to_owned()
);
}

View File

@ -1,6 +1,6 @@
use crate::impl_type_option;
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
use crate::services::row::CellDataSerde;
use crate::services::row::{CellDataSerde, TypeOptionCellData};
use crate::services::util::*;
use bytes::Bytes;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
@ -8,6 +8,7 @@ use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::entities::{FieldMeta, FieldType, TypeOptionDataEntity, TypeOptionDataEntry};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use uuid::Uuid;
pub const SELECTION_IDS_SEPARATOR: &str = ",";
@ -24,12 +25,25 @@ pub struct SingleSelectTypeOption {
impl_type_option!(SingleSelectTypeOption, FieldType::SingleSelect);
impl CellDataSerde for SingleSelectTypeOption {
fn deserialize_cell_data(&self, data: String) -> String {
data
fn deserialize_cell_data(&self, data: String, _field_meta: &FieldMeta) -> String {
if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) {
if !type_option_cell_data.is_single_select() || !type_option_cell_data.is_multi_select() {
return String::new();
}
let option_id = type_option_cell_data.data;
match self.options.iter().find(|option| option.id == option_id) {
None => String::new(),
Some(option) => option.name.clone(),
}
} else {
String::new()
}
}
fn serialize_cell_data(&self, data: &str) -> Result<String, FlowyError> {
single_select_option_id_from_data(data.to_owned())
let data = single_select_option_id_from_data(data.to_owned())?;
Ok(TypeOptionCellData::new(&data, self.field_type()).json())
}
}
@ -67,12 +81,32 @@ pub struct MultiSelectTypeOption {
impl_type_option!(MultiSelectTypeOption, FieldType::MultiSelect);
impl CellDataSerde for MultiSelectTypeOption {
fn deserialize_cell_data(&self, data: String) -> String {
data
fn deserialize_cell_data(&self, data: String, _field_meta: &FieldMeta) -> String {
if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) {
if !type_option_cell_data.is_single_select() || !type_option_cell_data.is_multi_select() {
return String::new();
}
match select_option_ids(type_option_cell_data.data) {
Ok(option_ids) => {
//
self.options
.iter()
.filter(|option| option_ids.contains(&option.id))
.map(|option| option.name.clone())
.collect::<Vec<String>>()
.join(SELECTION_IDS_SEPARATOR)
}
Err(_) => String::new(),
}
} else {
String::new()
}
}
fn serialize_cell_data(&self, data: &str) -> Result<String, FlowyError> {
multi_select_option_id_from_data(data.to_owned())
let data = multi_select_option_id_from_data(data.to_owned())?;
Ok(TypeOptionCellData::new(&data, self.field_type()).json())
}
}

View File

@ -1,11 +1,12 @@
use crate::impl_type_option;
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
use crate::services::row::CellDataSerde;
use crate::services::row::{deserialize_cell_data, CellDataSerde, TypeOptionCellData};
use bytes::Bytes;
use flowy_derive::ProtoBuf;
use flowy_error::FlowyError;
use flowy_grid_data_model::entities::{FieldMeta, FieldType, TypeOptionDataEntity, TypeOptionDataEntry};
use serde::{Deserialize, Serialize};
use std::str::FromStr;
#[derive(Default)]
pub struct RichTextTypeOptionBuilder(RichTextTypeOption);
@ -30,8 +31,20 @@ pub struct RichTextTypeOption {
impl_type_option!(RichTextTypeOption, FieldType::RichText);
impl CellDataSerde for RichTextTypeOption {
fn deserialize_cell_data(&self, data: String) -> String {
data
fn deserialize_cell_data(&self, data: String, field_meta: &FieldMeta) -> String {
if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) {
if type_option_cell_data.is_date()
|| type_option_cell_data.is_single_select()
|| type_option_cell_data.is_multi_select()
|| type_option_cell_data.is_number()
{
deserialize_cell_data(data, field_meta).unwrap_or_else(|_| "".to_owned())
} else {
type_option_cell_data.data
}
} else {
String::new()
}
}
fn serialize_cell_data(&self, data: &str) -> Result<String, FlowyError> {
@ -39,7 +52,68 @@ impl CellDataSerde for RichTextTypeOption {
if data.len() > 10000 {
Err(FlowyError::text_too_long().context("The len of the text should not be more than 10000"))
} else {
Ok(data)
Ok(TypeOptionCellData::new(&data, self.field_type()).json())
}
}
}
#[cfg(test)]
mod tests {
use crate::services::field::FieldBuilder;
use crate::services::field::*;
use crate::services::row::{CellDataSerde, TypeOptionCellData};
use flowy_grid_data_model::entities::FieldType;
#[test]
fn text_description_test() {
let type_option = RichTextTypeOption::default();
// date
let date_time_field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build();
let data = TypeOptionCellData::new("1647251762", FieldType::DateTime).json();
assert_eq!(
type_option.deserialize_cell_data(data, &date_time_field_meta),
"Mar 14,2022 17:56".to_owned()
);
// Single select
let done_option = SelectOption::new("Done");
let done_option_id = done_option.id.clone();
let single_select = SingleSelectTypeOptionBuilder::default().option(done_option);
let single_select_field_meta = FieldBuilder::new(single_select).build();
let data = TypeOptionCellData::new(&done_option_id, FieldType::SingleSelect).json();
assert_eq!(
type_option.deserialize_cell_data(data, &single_select_field_meta),
"Done".to_owned()
);
// Multiple select
let google_option = SelectOption::new("Google");
let google_option_id = google_option.id.clone();
let facebook_option = SelectOption::new("Facebook");
let face_option_id = facebook_option.id.clone();
let multi_select = MultiSelectTypeOptionBuilder::default()
.option(google_option)
.option(facebook_option)
.option(SelectOption::new("Twitter"));
let multi_select_field_meta = FieldBuilder::new(multi_select).build();
let data = TypeOptionCellData::new(
&vec![google_option_id, face_option_id].join(SELECTION_IDS_SEPARATOR),
FieldType::SingleSelect,
)
.json();
assert_eq!(
type_option.deserialize_cell_data(data, &multi_select_field_meta),
"Google,Facebook".to_owned()
);
//Number
let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD);
let number_field_meta = FieldBuilder::new(number).build();
let data = TypeOptionCellData::new("18443", FieldType::Number).json();
assert_eq!(
type_option.deserialize_cell_data(data, &number_field_meta),
"$18,443".to_owned()
);
}
}

View File

@ -1,5 +1 @@
// #[derive(Debug, Clone, Serialize, Deserialize, Default, ProtoBuf)]
// pub struct TypeOptionData {
// #[pb(index = 1)]
// pub map: HashMap<FieldType, String>,
// }

View File

@ -114,7 +114,16 @@ impl ClientGridEditor {
}
pub async fn switch_to_field_type(&self, field_id: &str, field_type: &FieldType) -> FlowyResult<()> {
// let cell_metas = self.block_meta_manager.get_cell_metas(None, field_id, None).await?;
// let block_ids = self
// .get_block_metas()
// .await?
// .into_iter()
// .map(|block_meta| block_meta.block_id)
// .collect();
// let cell_metas = self
// .block_meta_manager
// .get_cell_metas(block_ids, field_id, None)
// .await?;
let type_option_json_builder = |field_type: &FieldType| -> String {
return default_type_option_builder_from_type(field_type).entry().json_str();
@ -290,15 +299,16 @@ impl ClientGridEditor {
}
pub async fn grid_block_snapshots(&self, block_ids: Option<Vec<String>>) -> FlowyResult<Vec<GridBlockSnapshot>> {
let block_ids = block_ids.unwrap_or(
self.pad
let block_ids = match block_ids {
None => self.pad
.read()
.await
.get_block_metas()
.into_iter()
.map(|block_meta| block_meta.block_id)
.collect::<Vec<String>>(),
);
Some(block_ids) => block_ids,
};
let snapshots = self.block_meta_manager.make_block_snapshots(block_ids).await?;
Ok(snapshots)
}

View File

@ -1,12 +1,65 @@
use crate::services::field::*;
use flowy_error::FlowyError;
use flowy_grid_data_model::entities::{FieldMeta, FieldType};
use serde::{Deserialize, Serialize};
pub trait CellDataSerde {
fn deserialize_cell_data(&self, data: String) -> String;
fn deserialize_cell_data(&self, data: String, field_meta: &FieldMeta) -> String;
fn serialize_cell_data(&self, data: &str) -> Result<String, FlowyError>;
}
#[derive(Serialize, Deserialize)]
pub struct TypeOptionCellData {
pub data: String,
pub field_type: FieldType,
}
impl std::str::FromStr for TypeOptionCellData {
type Err = FlowyError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let type_option_cell_data: TypeOptionCellData = serde_json::from_str(s)?;
Ok(type_option_cell_data)
}
}
impl TypeOptionCellData {
pub fn new(data: &str, field_type: FieldType) -> Self {
TypeOptionCellData {
data: data.to_owned(),
field_type,
}
}
pub fn json(&self) -> String {
serde_json::to_string(self).unwrap_or_else(|_| "".to_owned())
}
pub fn is_number(&self) -> bool {
self.field_type == FieldType::Number
}
pub fn is_text(&self) -> bool {
self.field_type == FieldType::RichText
}
pub fn is_checkbox(&self) -> bool {
self.field_type == FieldType::Checkbox
}
pub fn is_date(&self) -> bool {
self.field_type == FieldType::DateTime
}
pub fn is_single_select(&self) -> bool {
self.field_type == FieldType::SingleSelect
}
pub fn is_multi_select(&self) -> bool {
self.field_type == FieldType::MultiSelect
}
}
#[allow(dead_code)]
pub fn serialize_cell_data(data: &str, field_meta: &FieldMeta) -> Result<String, FlowyError> {
match field_meta.field_type {
@ -21,12 +74,12 @@ pub fn serialize_cell_data(data: &str, field_meta: &FieldMeta) -> Result<String,
pub fn deserialize_cell_data(data: String, field_meta: &FieldMeta) -> Result<String, FlowyError> {
let s = match field_meta.field_type {
FieldType::RichText => RichTextTypeOption::from(field_meta).deserialize_cell_data(data),
FieldType::Number => NumberTypeOption::from(field_meta).deserialize_cell_data(data),
FieldType::DateTime => DateTypeOption::from(field_meta).deserialize_cell_data(data),
FieldType::SingleSelect => SingleSelectTypeOption::from(field_meta).deserialize_cell_data(data),
FieldType::MultiSelect => MultiSelectTypeOption::from(field_meta).deserialize_cell_data(data),
FieldType::Checkbox => CheckboxTypeOption::from(field_meta).deserialize_cell_data(data),
FieldType::RichText => RichTextTypeOption::from(field_meta).deserialize_cell_data(data, field_meta),
FieldType::Number => NumberTypeOption::from(field_meta).deserialize_cell_data(data, field_meta),
FieldType::DateTime => DateTypeOption::from(field_meta).deserialize_cell_data(data, field_meta),
FieldType::SingleSelect => SingleSelectTypeOption::from(field_meta).deserialize_cell_data(data, field_meta),
FieldType::MultiSelect => MultiSelectTypeOption::from(field_meta).deserialize_cell_data(data, field_meta),
FieldType::Checkbox => CheckboxTypeOption::from(field_meta).deserialize_cell_data(data, field_meta),
};
Ok(s)
}

View File

@ -246,6 +246,7 @@ impl TryInto<FieldChangesetParams> for FieldChangesetPayload {
#[derive(
Debug, Clone, PartialEq, Eq, ProtoBuf_Enum, EnumCountMacro, EnumString, EnumIter, Display, Serialize, Deserialize,
)]
#[repr(u8)]
pub enum FieldType {
RichText = 0,
Number = 1,
@ -278,18 +279,6 @@ impl FieldType {
let ty = self.clone();
format!("{}", ty as u8)
}
pub fn from_type_id(type_id: &str) -> Result<FieldType, String> {
match type_id {
"0" => Ok(FieldType::RichText),
"1" => Ok(FieldType::Number),
"2" => Ok(FieldType::DateTime),
"3" => Ok(FieldType::SingleSelect),
"4" => Ok(FieldType::MultiSelect),
"5" => Ok(FieldType::Checkbox),
_ => Err(format!("Invalid type_id: {}", type_id)),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, ProtoBuf)]

View File

@ -5,7 +5,7 @@ use flowy_grid_data_model::entities::{CellMeta, GridBlockMetaData, RowMeta, RowM
use lib_infra::uuid;
use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder};
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::collections::HashMap;
use std::sync::Arc;