refactor: apply cell change set (#1589)

* refactor: update cell changeset trait

* refactor: update cell changeset documentation

Co-authored-by: nathan <nathan@appflowy.io>
This commit is contained in:
Nathan.fooo 2022-12-20 14:40:40 +08:00 committed by GitHub
parent 5d7008edd7
commit 4643851b3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 158 additions and 155 deletions

View File

@ -55,8 +55,8 @@ pub trait CellDataChangeset: TypeOption {
/// ///
fn apply_changeset( fn apply_changeset(
&self, &self,
changeset: AnyCellChangeset<<Self as TypeOption>::CellChangeset>, changeset: <Self as TypeOption>::CellChangeset,
cell_rev: Option<CellRevision>, type_cell_data: Option<TypeCellData>,
) -> FlowyResult<String>; ) -> FlowyResult<String>;
} }
@ -73,21 +73,18 @@ pub fn apply_cell_data_changeset<C: ToString, T: AsRef<FieldRevision>>(
) -> Result<String, FlowyError> { ) -> Result<String, FlowyError> {
let field_rev = field_rev.as_ref(); let field_rev = field_rev.as_ref();
let changeset = changeset.to_string(); let changeset = changeset.to_string();
let field_type = field_rev.ty.into(); let field_type: FieldType = field_rev.ty.into();
let s = match field_type {
FieldType::RichText => RichTextTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev),
FieldType::Number => NumberTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev),
FieldType::DateTime => DateTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev),
FieldType::SingleSelect => {
SingleSelectTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev)
}
FieldType::MultiSelect => MultiSelectTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev),
FieldType::Checklist => ChecklistTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev),
FieldType::Checkbox => CheckboxTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev),
FieldType::URL => URLTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev),
}?;
Ok(TypeCellData::new(s, field_type).to_json()) let type_cell_data = cell_rev.and_then(|cell_rev| match TypeCellData::try_from(cell_rev) {
Ok(type_cell_data) => Some(type_cell_data),
Err(_) => None,
});
let cell_data = match FieldRevisionExt::new(field_rev).get_type_option_cell_data_handler(&field_type) {
None => "".to_string(),
Some(handler) => handler.handle_cell_changeset(changeset, type_cell_data)?,
};
Ok(TypeCellData::new(cell_data, field_type).to_json())
} }
pub fn decode_type_cell_data<T: TryInto<TypeCellData, Error = FlowyError> + Debug>( pub fn decode_type_cell_data<T: TryInto<TypeCellData, Error = FlowyError> + Debug>(
@ -138,7 +135,7 @@ pub fn try_decode_cell_data(
to_field_type: &FieldType, to_field_type: &FieldType,
field_rev: &FieldRevision, field_rev: &FieldRevision,
) -> FlowyResult<CellProtobufBlob> { ) -> FlowyResult<CellProtobufBlob> {
match FieldRevisionExt::new(field_rev).get_type_option_handler(to_field_type) { match FieldRevisionExt::new(field_rev).get_type_option_cell_data_handler(to_field_type) {
None => Ok(CellProtobufBlob::default()), None => Ok(CellProtobufBlob::default()),
Some(handler) => handler.handle_cell_data(cell_data, from_field_type, field_rev), Some(handler) => handler.handle_cell_data(cell_data, from_field_type, field_rev),
} }
@ -154,7 +151,7 @@ pub fn stringify_cell_data(
to_field_type: &FieldType, to_field_type: &FieldType,
field_rev: &FieldRevision, field_rev: &FieldRevision,
) -> String { ) -> String {
match FieldRevisionExt::new(field_rev).get_type_option_handler(to_field_type) { match FieldRevisionExt::new(field_rev).get_type_option_cell_data_handler(to_field_type) {
None => "".to_string(), None => "".to_string(),
Some(handler) => handler.stringify_cell_data(cell_data, from_field_type, field_rev), Some(handler) => handler.stringify_cell_data(cell_data, from_field_type, field_rev),
} }
@ -269,6 +266,15 @@ pub trait FromCellChangeset {
Self: Sized; Self: Sized;
} }
impl FromCellChangeset for String {
fn from_changeset(changeset: String) -> FlowyResult<Self>
where
Self: Sized,
{
Ok(changeset)
}
}
pub struct AnyCellChangeset<T>(pub Option<T>); pub struct AnyCellChangeset<T>(pub Option<T>);
impl<T> AnyCellChangeset<T> { impl<T> AnyCellChangeset<T> {
@ -294,8 +300,8 @@ where
} }
} }
} }
impl std::convert::From<String> for AnyCellChangeset<String> { // impl std::convert::From<String> for AnyCellChangeset<String> {
fn from(s: String) -> Self { // fn from(s: String) -> Self {
AnyCellChangeset(Some(s)) // AnyCellChangeset(Some(s))
} // }
} // }

View File

@ -1,14 +1,14 @@
use crate::entities::{CheckboxFilterPB, FieldType}; use crate::entities::{CheckboxFilterPB, FieldType};
use crate::impl_type_option; use crate::impl_type_option;
use crate::services::cell::{AnyCellChangeset, CellDataChangeset, CellDataDecoder, FromCellString}; use crate::services::cell::{CellDataChangeset, CellDataDecoder, FromCellString, TypeCellData};
use crate::services::field::{ use crate::services::field::{
BoxTypeOptionBuilder, CheckboxCellData, TypeOption, TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration, BoxTypeOptionBuilder, CheckboxCellData, TypeOption, TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration,
TypeOptionTransform, TypeOptionTransform,
}; };
use bytes::Bytes; use bytes::Bytes;
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::FlowyResult;
use grid_rev_model::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer}; use grid_rev_model::{FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::str::FromStr; use std::str::FromStr;
@ -87,10 +87,9 @@ pub type CheckboxCellChangeset = String;
impl CellDataChangeset for CheckboxTypeOptionPB { impl CellDataChangeset for CheckboxTypeOptionPB {
fn apply_changeset( fn apply_changeset(
&self, &self,
changeset: AnyCellChangeset<CheckboxCellChangeset>, changeset: <Self as TypeOption>::CellChangeset,
_cell_rev: Option<CellRevision>, _type_cell_data: Option<TypeCellData>,
) -> Result<String, FlowyError> { ) -> FlowyResult<String> {
let changeset = changeset.try_into_inner()?;
let cell_data = CheckboxCellData::from_str(&changeset)?; let cell_data = CheckboxCellData::from_str(&changeset)?;
Ok(cell_data.to_string()) Ok(cell_data.to_string())
} }

View File

@ -147,13 +147,12 @@ mod tests {
expected_str: &str, expected_str: &str,
field_rev: &FieldRevision, field_rev: &FieldRevision,
) { ) {
let s = serde_json::to_string(&DateCellChangeset { let changeset = DateCellChangeset {
date: Some(timestamp.to_string()), date: Some(timestamp.to_string()),
time: include_time_str, time: include_time_str,
is_utc: false, is_utc: false,
}) };
.unwrap(); let encoded_data = type_option.apply_changeset(changeset, None).unwrap();
let encoded_data = type_option.apply_changeset(s.into(), None).unwrap();
assert_eq!( assert_eq!(
decode_cell_data(encoded_data, type_option, field_rev), decode_cell_data(encoded_data, type_option, field_rev),

View File

@ -1,6 +1,6 @@
use crate::entities::{DateFilterPB, FieldType}; use crate::entities::{DateFilterPB, FieldType};
use crate::impl_type_option; use crate::impl_type_option;
use crate::services::cell::{AnyCellChangeset, CellDataChangeset, CellDataDecoder, FromCellString}; use crate::services::cell::{CellDataChangeset, CellDataDecoder, FromCellString, TypeCellData};
use crate::services::field::{ use crate::services::field::{
BoxTypeOptionBuilder, DateCellChangeset, DateCellData, DateCellDataPB, DateFormat, TimeFormat, TypeOption, BoxTypeOptionBuilder, DateCellChangeset, DateCellData, DateCellDataPB, DateFormat, TimeFormat, TypeOption,
TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration, TypeOptionTransform, TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration, TypeOptionTransform,
@ -10,7 +10,7 @@ use chrono::format::strftime::StrftimeItems;
use chrono::{NaiveDateTime, Timelike}; use chrono::{NaiveDateTime, Timelike};
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use grid_rev_model::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer}; use grid_rev_model::{FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
// Date // Date
@ -156,10 +156,9 @@ impl CellDataDecoder for DateTypeOptionPB {
impl CellDataChangeset for DateTypeOptionPB { impl CellDataChangeset for DateTypeOptionPB {
fn apply_changeset( fn apply_changeset(
&self, &self,
changeset: AnyCellChangeset<DateCellChangeset>, changeset: <Self as TypeOption>::CellChangeset,
_cell_rev: Option<CellRevision>, _type_cell_data: Option<TypeCellData>,
) -> Result<String, FlowyError> { ) -> FlowyResult<String> {
let changeset = changeset.try_into_inner()?;
let cell_data = match changeset.date_timestamp() { let cell_data = match changeset.date_timestamp() {
None => 0, None => 0,
Some(date_timestamp) => match (self.include_time, changeset.time) { Some(date_timestamp) => match (self.include_time, changeset.time) {

View File

@ -1,6 +1,6 @@
use crate::entities::{FieldType, NumberFilterPB}; use crate::entities::{FieldType, NumberFilterPB};
use crate::impl_type_option; use crate::impl_type_option;
use crate::services::cell::{AnyCellChangeset, CellComparable, CellDataChangeset, CellDataDecoder}; use crate::services::cell::{CellComparable, CellDataChangeset, CellDataDecoder, TypeCellData};
use crate::services::field::type_options::number_type_option::format::*; use crate::services::field::type_options::number_type_option::format::*;
use crate::services::field::{ use crate::services::field::{
BoxTypeOptionBuilder, NumberCellData, StrCellData, TypeOption, TypeOptionBuilder, TypeOptionCellData, BoxTypeOptionBuilder, NumberCellData, StrCellData, TypeOption, TypeOptionBuilder, TypeOptionCellData,
@ -8,8 +8,8 @@ use crate::services::field::{
}; };
use bytes::Bytes; use bytes::Bytes;
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::FlowyResult;
use grid_rev_model::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer}; use grid_rev_model::{FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer};
use rust_decimal::Decimal; use rust_decimal::Decimal;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cmp::Ordering; use std::cmp::Ordering;
@ -156,10 +156,9 @@ pub type NumberCellChangeset = String;
impl CellDataChangeset for NumberTypeOptionPB { impl CellDataChangeset for NumberTypeOptionPB {
fn apply_changeset( fn apply_changeset(
&self, &self,
changeset: AnyCellChangeset<NumberCellChangeset>, changeset: <Self as TypeOption>::CellChangeset,
_cell_rev: Option<CellRevision>, _type_cell_data: Option<TypeCellData>,
) -> Result<String, FlowyError> { ) -> FlowyResult<String> {
let changeset = changeset.try_into_inner()?;
let data = changeset.trim().to_string(); let data = changeset.trim().to_string();
let _ = self.format_cell_data(&data)?; let _ = self.format_cell_data(&data)?;
Ok(data) Ok(data)

View File

@ -1,6 +1,6 @@
use crate::entities::{ChecklistFilterPB, FieldType}; use crate::entities::{ChecklistFilterPB, FieldType};
use crate::impl_type_option; use crate::impl_type_option;
use crate::services::cell::{AnyCellChangeset, CellDataChangeset, FromCellString, TypeCellData}; use crate::services::cell::{CellDataChangeset, FromCellString, TypeCellData};
use crate::services::field::{ use crate::services::field::{
BoxTypeOptionBuilder, SelectOptionCellChangeset, SelectOptionCellDataPB, SelectOptionIds, SelectOptionPB, BoxTypeOptionBuilder, SelectOptionCellChangeset, SelectOptionCellDataPB, SelectOptionIds, SelectOptionPB,
@ -8,8 +8,8 @@ use crate::services::field::{
}; };
use bytes::Bytes; use bytes::Bytes;
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::FlowyResult;
use grid_rev_model::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer}; use grid_rev_model::{FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
// Multiple select // Multiple select
@ -60,31 +60,26 @@ impl SelectTypeOptionSharedAction for ChecklistTypeOptionPB {
impl CellDataChangeset for ChecklistTypeOptionPB { impl CellDataChangeset for ChecklistTypeOptionPB {
fn apply_changeset( fn apply_changeset(
&self, &self,
changeset: AnyCellChangeset<SelectOptionCellChangeset>, changeset: <Self as TypeOption>::CellChangeset,
cell_rev: Option<CellRevision>, type_cell_data: Option<TypeCellData>,
) -> Result<String, FlowyError> { ) -> FlowyResult<String> {
let content_changeset = changeset.try_into_inner()?; let insert_option_ids = changeset
let insert_option_ids = content_changeset
.insert_option_ids .insert_option_ids
.into_iter() .into_iter()
.filter(|insert_option_id| self.options.iter().any(|option| &option.id == insert_option_id)) .filter(|insert_option_id| self.options.iter().any(|option| &option.id == insert_option_id))
.collect::<Vec<String>>(); .collect::<Vec<String>>();
match cell_rev { match type_cell_data {
None => Ok(SelectOptionIds::from(insert_option_ids).to_string()), None => Ok(SelectOptionIds::from(insert_option_ids).to_string()),
Some(cell_rev) => { Some(type_cell_data) => {
let cell_data = TypeCellData::try_from(cell_rev) let mut select_ids: SelectOptionIds = type_cell_data.data.into();
.map(|data| data.into_inner())
.unwrap_or_default();
let mut select_ids: SelectOptionIds = cell_data.into();
for insert_option_id in insert_option_ids { for insert_option_id in insert_option_ids {
if !select_ids.contains(&insert_option_id) { if !select_ids.contains(&insert_option_id) {
select_ids.push(insert_option_id); select_ids.push(insert_option_id);
} }
} }
for delete_option_id in content_changeset.delete_option_ids { for delete_option_id in changeset.delete_option_ids {
select_ids.retain(|id| id != &delete_option_id); select_ids.retain(|id| id != &delete_option_id);
} }

View File

@ -1,6 +1,6 @@
use crate::entities::{FieldType, SelectOptionFilterPB}; use crate::entities::{FieldType, SelectOptionFilterPB};
use crate::impl_type_option; use crate::impl_type_option;
use crate::services::cell::{AnyCellChangeset, CellDataChangeset, FromCellString, TypeCellData}; use crate::services::cell::{CellDataChangeset, FromCellString, TypeCellData};
use crate::services::field::{ use crate::services::field::{
BoxTypeOptionBuilder, SelectOptionCellChangeset, SelectOptionCellDataPB, SelectOptionIds, SelectOptionPB, BoxTypeOptionBuilder, SelectOptionCellChangeset, SelectOptionCellDataPB, SelectOptionIds, SelectOptionPB,
@ -8,8 +8,8 @@ use crate::services::field::{
}; };
use bytes::Bytes; use bytes::Bytes;
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::FlowyResult;
use grid_rev_model::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer}; use grid_rev_model::{FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
// Multiple select // Multiple select
@ -60,34 +60,29 @@ impl SelectTypeOptionSharedAction for MultiSelectTypeOptionPB {
impl CellDataChangeset for MultiSelectTypeOptionPB { impl CellDataChangeset for MultiSelectTypeOptionPB {
fn apply_changeset( fn apply_changeset(
&self, &self,
changeset: AnyCellChangeset<SelectOptionCellChangeset>, changeset: <Self as TypeOption>::CellChangeset,
cell_rev: Option<CellRevision>, type_cell_data: Option<TypeCellData>,
) -> Result<String, FlowyError> { ) -> FlowyResult<String> {
let content_changeset = changeset.try_into_inner()?; let insert_option_ids = changeset
let insert_option_ids = content_changeset
.insert_option_ids .insert_option_ids
.into_iter() .into_iter()
.filter(|insert_option_id| self.options.iter().any(|option| &option.id == insert_option_id)) .filter(|insert_option_id| self.options.iter().any(|option| &option.id == insert_option_id))
.collect::<Vec<String>>(); .collect::<Vec<String>>();
let new_cell_data: String; let new_cell_data: String;
match cell_rev { match type_cell_data {
None => { None => {
new_cell_data = SelectOptionIds::from(insert_option_ids).to_string(); new_cell_data = SelectOptionIds::from(insert_option_ids).to_string();
} }
Some(cell_rev) => { Some(type_cell_data) => {
let cell_data = TypeCellData::try_from(cell_rev) let mut select_ids: SelectOptionIds = type_cell_data.data.into();
.map(|data| data.into_inner())
.unwrap_or_default();
let mut select_ids: SelectOptionIds = cell_data.into();
for insert_option_id in insert_option_ids { for insert_option_id in insert_option_ids {
if !select_ids.contains(&insert_option_id) { if !select_ids.contains(&insert_option_id) {
select_ids.push(insert_option_id); select_ids.push(insert_option_id);
} }
} }
for delete_option_id in content_changeset.delete_option_ids { for delete_option_id in changeset.delete_option_ids {
select_ids.retain(|id| id != &delete_option_id); select_ids.retain(|id| id != &delete_option_id);
} }
@ -177,8 +172,8 @@ mod tests {
let field_rev = FieldBuilder::new(multi_select).name("Platform").build(); let field_rev = FieldBuilder::new(multi_select).name("Platform").build();
let type_option = MultiSelectTypeOptionPB::from(&field_rev); let type_option = MultiSelectTypeOptionPB::from(&field_rev);
let option_ids = vec![google.id, facebook.id]; let option_ids = vec![google.id, facebook.id];
let data = SelectOptionCellChangeset::from_insert_options(option_ids.clone()).to_str(); let changeset = SelectOptionCellChangeset::from_insert_options(option_ids.clone());
let select_option_ids: SelectOptionIds = type_option.apply_changeset(data.into(), None).unwrap().into(); let select_option_ids: SelectOptionIds = type_option.apply_changeset(changeset, None).unwrap().into();
assert_eq!(&*select_option_ids, &option_ids); assert_eq!(&*select_option_ids, &option_ids);
} }
@ -196,13 +191,13 @@ mod tests {
let option_ids = vec![google.id, facebook.id]; let option_ids = vec![google.id, facebook.id];
// insert // insert
let data = SelectOptionCellChangeset::from_insert_options(option_ids.clone()).to_str(); let changeset = SelectOptionCellChangeset::from_insert_options(option_ids.clone());
let select_option_ids: SelectOptionIds = type_option.apply_changeset(data.into(), None).unwrap().into(); let select_option_ids: SelectOptionIds = type_option.apply_changeset(changeset, None).unwrap().into();
assert_eq!(&*select_option_ids, &option_ids); assert_eq!(&*select_option_ids, &option_ids);
// delete // delete
let data = SelectOptionCellChangeset::from_delete_options(option_ids).to_str(); let changeset = SelectOptionCellChangeset::from_delete_options(option_ids);
let select_option_ids: SelectOptionIds = type_option.apply_changeset(data.into(), None).unwrap().into(); let select_option_ids: SelectOptionIds = type_option.apply_changeset(changeset, None).unwrap().into();
assert!(select_option_ids.is_empty()); assert!(select_option_ids.is_empty());
} }
@ -217,8 +212,8 @@ mod tests {
.build(); .build();
let type_option = MultiSelectTypeOptionPB::from(&field_rev); let type_option = MultiSelectTypeOptionPB::from(&field_rev);
let data = SelectOptionCellChangeset::from_insert_option_id(&google.id).to_str(); let changeset = SelectOptionCellChangeset::from_insert_option_id(&google.id);
let cell_option_ids = type_option.apply_changeset(data.into(), None).unwrap(); let cell_option_ids = type_option.apply_changeset(changeset, None).unwrap();
assert_eq!(cell_option_ids, google.id); assert_eq!(cell_option_ids, google.id);
} }
@ -232,8 +227,8 @@ mod tests {
.build(); .build();
let type_option = MultiSelectTypeOptionPB::from(&field_rev); let type_option = MultiSelectTypeOptionPB::from(&field_rev);
let data = SelectOptionCellChangeset::from_insert_option_id(&google.id).to_str(); let changeset = SelectOptionCellChangeset::from_insert_option_id(&google.id);
let cell_option_ids = type_option.apply_changeset(data.into(), None).unwrap(); let cell_option_ids = type_option.apply_changeset(changeset, None).unwrap();
assert!(cell_option_ids.is_empty()); assert!(cell_option_ids.is_empty());
} }
@ -250,22 +245,12 @@ mod tests {
let type_option = MultiSelectTypeOptionPB::from(&field_rev); let type_option = MultiSelectTypeOptionPB::from(&field_rev);
// empty option id string // empty option id string
let data = SelectOptionCellChangeset::from_insert_option_id("").to_str(); let changeset = SelectOptionCellChangeset::from_insert_option_id("");
let cell_option_ids = type_option.apply_changeset(data.into(), None).unwrap(); let cell_option_ids = type_option.apply_changeset(changeset, None).unwrap();
assert_eq!(cell_option_ids, ""); assert_eq!(cell_option_ids, "");
let data = SelectOptionCellChangeset::from_insert_option_id("123,456").to_str(); let changeset = SelectOptionCellChangeset::from_insert_option_id("123,456");
let cell_option_ids = type_option.apply_changeset(data.into(), None).unwrap(); let cell_option_ids = type_option.apply_changeset(changeset, None).unwrap();
assert_eq!(cell_option_ids, ""); assert_eq!(cell_option_ids, "");
} }
#[test]
fn multi_select_invalid_changeset_data_test() {
let multi_select = MultiSelectTypeOptionBuilder::default();
let field_rev = FieldBuilder::new(multi_select).name("Platform").build();
let type_option = MultiSelectTypeOptionPB::from(&field_rev);
// The type of the changeset should be SelectOptionCellChangeset
assert!(type_option.apply_changeset("123".to_owned().into(), None).is_err());
}
} }

View File

@ -1,6 +1,6 @@
use crate::entities::{FieldType, SelectOptionFilterPB}; use crate::entities::{FieldType, SelectOptionFilterPB};
use crate::impl_type_option; use crate::impl_type_option;
use crate::services::cell::{AnyCellChangeset, CellDataChangeset, FromCellString}; use crate::services::cell::{CellDataChangeset, FromCellString, TypeCellData};
use crate::services::field::{ use crate::services::field::{
BoxTypeOptionBuilder, SelectOptionCellDataPB, TypeOption, TypeOptionBuilder, TypeOptionCellData, BoxTypeOptionBuilder, SelectOptionCellDataPB, TypeOption, TypeOptionBuilder, TypeOptionCellData,
@ -11,8 +11,8 @@ use crate::services::field::{
}; };
use bytes::Bytes; use bytes::Bytes;
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::FlowyResult;
use grid_rev_model::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer}; use grid_rev_model::{FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
// Single select // Single select
@ -63,12 +63,10 @@ impl SelectTypeOptionSharedAction for SingleSelectTypeOptionPB {
impl CellDataChangeset for SingleSelectTypeOptionPB { impl CellDataChangeset for SingleSelectTypeOptionPB {
fn apply_changeset( fn apply_changeset(
&self, &self,
changeset: AnyCellChangeset<SelectOptionCellChangeset>, changeset: <Self as TypeOption>::CellChangeset,
_cell_rev: Option<CellRevision>, _type_cell_data: Option<TypeCellData>,
) -> Result<String, FlowyError> { ) -> FlowyResult<String> {
let content_changeset = changeset.try_into_inner()?; let mut insert_option_ids = changeset
let mut insert_option_ids = content_changeset
.insert_option_ids .insert_option_ids
.into_iter() .into_iter()
.filter(|insert_option_id| self.options.iter().any(|option| &option.id == insert_option_id)) .filter(|insert_option_id| self.options.iter().any(|option| &option.id == insert_option_id))
@ -162,8 +160,8 @@ mod tests {
let field_rev = FieldBuilder::new(single_select).name("Platform").build(); let field_rev = FieldBuilder::new(single_select).name("Platform").build();
let type_option = SingleSelectTypeOptionPB::from(&field_rev); let type_option = SingleSelectTypeOptionPB::from(&field_rev);
let option_ids = vec![google.id.clone(), facebook.id]; let option_ids = vec![google.id.clone(), facebook.id];
let data = SelectOptionCellChangeset::from_insert_options(option_ids).to_str(); let changeset = SelectOptionCellChangeset::from_insert_options(option_ids);
let select_option_ids: SelectOptionIds = type_option.apply_changeset(data.into(), None).unwrap().into(); let select_option_ids: SelectOptionIds = type_option.apply_changeset(changeset, None).unwrap().into();
assert_eq!(&*select_option_ids, &vec![google.id]); assert_eq!(&*select_option_ids, &vec![google.id]);
} }
@ -181,13 +179,13 @@ mod tests {
let option_ids = vec![google.id.clone(), facebook.id]; let option_ids = vec![google.id.clone(), facebook.id];
// insert // insert
let data = SelectOptionCellChangeset::from_insert_options(option_ids.clone()).to_str(); let changeset = SelectOptionCellChangeset::from_insert_options(option_ids.clone());
let select_option_ids: SelectOptionIds = type_option.apply_changeset(data.into(), None).unwrap().into(); let select_option_ids: SelectOptionIds = type_option.apply_changeset(changeset, None).unwrap().into();
assert_eq!(&*select_option_ids, &vec![google.id]); assert_eq!(&*select_option_ids, &vec![google.id]);
// delete // delete
let data = SelectOptionCellChangeset::from_delete_options(option_ids).to_str(); let changeset = SelectOptionCellChangeset::from_delete_options(option_ids);
let select_option_ids: SelectOptionIds = type_option.apply_changeset(data.into(), None).unwrap().into(); let select_option_ids: SelectOptionIds = type_option.apply_changeset(changeset, None).unwrap().into();
assert!(select_option_ids.is_empty()); assert!(select_option_ids.is_empty());
} }
@ -199,8 +197,8 @@ mod tests {
let type_option = SingleSelectTypeOptionPB::from(&field_rev); let type_option = SingleSelectTypeOptionPB::from(&field_rev);
let option_ids = vec![google.id]; let option_ids = vec![google.id];
let data = SelectOptionCellChangeset::from_insert_options(option_ids).to_str(); let changeset = SelectOptionCellChangeset::from_insert_options(option_ids);
let cell_option_ids = type_option.apply_changeset(data.into(), None).unwrap(); let cell_option_ids = type_option.apply_changeset(changeset, None).unwrap();
assert!(cell_option_ids.is_empty()); assert!(cell_option_ids.is_empty());
} }
@ -211,18 +209,8 @@ mod tests {
let field_rev = FieldBuilder::new(single_select).name("Platform").build(); let field_rev = FieldBuilder::new(single_select).name("Platform").build();
let type_option = SingleSelectTypeOptionPB::from(&field_rev); let type_option = SingleSelectTypeOptionPB::from(&field_rev);
let data = SelectOptionCellChangeset::from_insert_option_id("").to_str(); let changeset = SelectOptionCellChangeset::from_insert_option_id("");
let cell_option_ids = type_option.apply_changeset(data.into(), None).unwrap(); let cell_option_ids = type_option.apply_changeset(changeset, None).unwrap();
assert_eq!(cell_option_ids, ""); assert_eq!(cell_option_ids, "");
} }
#[test]
fn single_select_invalid_changeset_data_test() {
let single_select = SingleSelectTypeOptionBuilder::default();
let field_rev = FieldBuilder::new(single_select).name("Platform").build();
let type_option = SingleSelectTypeOptionPB::from(&field_rev);
// The type of the changeset should be SelectOptionCellChangeset
assert!(type_option.apply_changeset("123".to_owned().into(), None).is_err());
}
} }

View File

@ -1,8 +1,8 @@
use crate::entities::{FieldType, TextFilterPB}; use crate::entities::{FieldType, TextFilterPB};
use crate::impl_type_option; use crate::impl_type_option;
use crate::services::cell::{ use crate::services::cell::{
stringify_cell_data, AnyCellChangeset, CellComparable, CellDataChangeset, CellDataDecoder, CellProtobufBlobParser, stringify_cell_data, CellComparable, CellDataChangeset, CellDataDecoder, CellProtobufBlobParser, DecodedCellData,
DecodedCellData, FromCellString, FromCellString, TypeCellData,
}; };
use crate::services::field::{ use crate::services::field::{
BoxTypeOptionBuilder, TypeOption, TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration, BoxTypeOptionBuilder, TypeOption, TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration,
@ -11,7 +11,7 @@ use crate::services::field::{
use bytes::Bytes; use bytes::Bytes;
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
use grid_rev_model::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer}; use grid_rev_model::{FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer};
use protobuf::ProtobufError; use protobuf::ProtobufError;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cmp::Ordering; use std::cmp::Ordering;
@ -90,14 +90,13 @@ impl CellDataDecoder for RichTextTypeOptionPB {
impl CellDataChangeset for RichTextTypeOptionPB { impl CellDataChangeset for RichTextTypeOptionPB {
fn apply_changeset( fn apply_changeset(
&self, &self,
changeset: AnyCellChangeset<String>, changeset: <Self as TypeOption>::CellChangeset,
_cell_rev: Option<CellRevision>, _type_cell_data: Option<TypeCellData>,
) -> Result<String, FlowyError> { ) -> FlowyResult<String> {
let data = changeset.try_into_inner()?; if changeset.len() > 10000 {
if data.len() > 10000 {
Err(FlowyError::text_too_long().context("The len of the text should not be more than 10000")) Err(FlowyError::text_too_long().context("The len of the text should not be more than 10000"))
} else { } else {
Ok(data) Ok(changeset)
} }
} }
} }

View File

@ -1,5 +1,7 @@
use crate::entities::FieldType; use crate::entities::FieldType;
use crate::services::cell::{CellDataChangeset, CellDataDecoder, CellProtobufBlob, FromCellString}; use crate::services::cell::{
CellDataChangeset, CellDataDecoder, CellProtobufBlob, FromCellChangeset, FromCellString, TypeCellData,
};
use crate::services::field::{ use crate::services::field::{
CheckboxTypeOptionPB, ChecklistTypeOptionPB, DateTypeOptionPB, MultiSelectTypeOptionPB, NumberTypeOptionPB, CheckboxTypeOptionPB, ChecklistTypeOptionPB, DateTypeOptionPB, MultiSelectTypeOptionPB, NumberTypeOptionPB,
RichTextTypeOptionPB, SingleSelectTypeOptionPB, URLTypeOptionPB, RichTextTypeOptionPB, SingleSelectTypeOptionPB, URLTypeOptionPB,
@ -23,8 +25,11 @@ pub trait TypeOption {
/// ///
type CellData: FromCellString + Default; type CellData: FromCellString + Default;
/// Represents as the corresponding field type cell changeset.
/// The changeset must implements the `FromCellChangeset` trait. The `CellChangeset` is implemented
/// for `String`.
/// ///
type CellChangeset; type CellChangeset: FromCellChangeset;
/// For the moment, the protobuf type only be used in the FFI of `Dart`. If the decoded cell /// For the moment, the protobuf type only be used in the FFI of `Dart`. If the decoded cell
/// struct is just a `String`, then use the `StrCellData` as its `CellProtobufType`. /// struct is just a `String`, then use the `StrCellData` as its `CellProtobufType`.
@ -92,6 +97,7 @@ pub trait TypeOptionTransform: TypeOption {
} }
} }
/// A helper trait that used to erase the `Self` of `TypeOption` trait to make it become a Object-safe trait.
pub trait TypeOptionTransformHandler { pub trait TypeOptionTransformHandler {
fn transform(&mut self, old_type_option_field_type: FieldType, old_type_option_data: String); fn transform(&mut self, old_type_option_field_type: FieldType, old_type_option_data: String);
@ -113,6 +119,12 @@ where
} }
} }
/// A helper trait that used to erase the `Self` of `TypeOption` trait to make it become a Object-safe trait
/// Only object-safe traits can be made into trait objects.
/// > Object-safe traits are traits with methods that follow these two rules:
/// 1.the return type is not Self.
/// 2.there are no generic types parameters.
///
pub trait TypeOptionCellDataHandler { pub trait TypeOptionCellDataHandler {
fn handle_cell_data( fn handle_cell_data(
&self, &self,
@ -121,6 +133,12 @@ pub trait TypeOptionCellDataHandler {
field_rev: &FieldRevision, field_rev: &FieldRevision,
) -> FlowyResult<CellProtobufBlob>; ) -> FlowyResult<CellProtobufBlob>;
fn handle_cell_changeset(
&self,
cell_changeset: String,
old_type_cell_data: Option<TypeCellData>,
) -> FlowyResult<String>;
fn stringify_cell_data(&self, cell_data: String, field_type: &FieldType, field_rev: &FieldRevision) -> String; fn stringify_cell_data(&self, cell_data: String, field_type: &FieldType, field_rev: &FieldRevision) -> String;
} }
@ -145,6 +163,16 @@ where
CellProtobufBlob::from(self.convert_to_protobuf(cell_data)) CellProtobufBlob::from(self.convert_to_protobuf(cell_data))
} }
fn handle_cell_changeset(
&self,
cell_changeset: String,
old_type_cell_data: Option<TypeCellData>,
) -> FlowyResult<String> {
let changeset = <Self as TypeOption>::CellChangeset::from_changeset(cell_changeset)?;
let cell_data = self.apply_changeset(changeset, old_type_cell_data)?;
Ok(cell_data)
}
fn stringify_cell_data( fn stringify_cell_data(
&self, &self,
cell_data: String, cell_data: String,
@ -173,7 +201,10 @@ impl<'a> FieldRevisionExt<'a> {
Self { field_rev } Self { field_rev }
} }
pub fn get_type_option_handler(&self, field_type: &FieldType) -> Option<Box<dyn TypeOptionCellDataHandler>> { pub fn get_type_option_cell_data_handler(
&self,
field_type: &FieldType,
) -> Option<Box<dyn TypeOptionCellDataHandler>> {
match field_type { match field_type {
FieldType::RichText => self FieldType::RichText => self
.field_rev .field_rev

View File

@ -170,7 +170,7 @@ mod tests {
field_type: &FieldType, field_type: &FieldType,
field_rev: &FieldRevision, field_rev: &FieldRevision,
) { ) {
let encoded_data = type_option.apply_changeset(input_str.to_owned().into(), None).unwrap(); let encoded_data = type_option.apply_changeset(input_str.to_owned(), None).unwrap();
let decode_cell_data = decode_cell_data(encoded_data, type_option, field_rev, field_type); let decode_cell_data = decode_cell_data(encoded_data, type_option, field_rev, field_type);
assert_eq!(expected_str.to_owned(), decode_cell_data.content); assert_eq!(expected_str.to_owned(), decode_cell_data.content);
assert_eq!(expected_url.to_owned(), decode_cell_data.url); assert_eq!(expected_url.to_owned(), decode_cell_data.url);

View File

@ -1,6 +1,6 @@
use crate::entities::{FieldType, TextFilterPB}; use crate::entities::{FieldType, TextFilterPB};
use crate::impl_type_option; use crate::impl_type_option;
use crate::services::cell::{AnyCellChangeset, CellDataChangeset, CellDataDecoder, FromCellString}; use crate::services::cell::{CellDataChangeset, CellDataDecoder, FromCellString, TypeCellData};
use crate::services::field::{ use crate::services::field::{
BoxTypeOptionBuilder, TypeOption, TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration, BoxTypeOptionBuilder, TypeOption, TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration,
TypeOptionTransform, URLCellData, URLCellDataPB, TypeOptionTransform, URLCellData, URLCellDataPB,
@ -8,8 +8,8 @@ use crate::services::field::{
use bytes::Bytes; use bytes::Bytes;
use fancy_regex::Regex; use fancy_regex::Regex;
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::FlowyResult;
use grid_rev_model::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer}; use grid_rev_model::{FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -81,15 +81,18 @@ pub type URLCellChangeset = String;
impl CellDataChangeset for URLTypeOptionPB { impl CellDataChangeset for URLTypeOptionPB {
fn apply_changeset( fn apply_changeset(
&self, &self,
changeset: AnyCellChangeset<URLCellChangeset>, changeset: <Self as TypeOption>::CellChangeset,
_cell_rev: Option<CellRevision>, _type_cell_data: Option<TypeCellData>,
) -> Result<String, FlowyError> { ) -> FlowyResult<String> {
let content = changeset.try_into_inner()?;
let mut url = "".to_string(); let mut url = "".to_string();
if let Ok(Some(m)) = URL_REGEX.find(&content) { if let Ok(Some(m)) = URL_REGEX.find(&changeset) {
url = auto_append_scheme(m.as_str()); url = auto_append_scheme(m.as_str());
} }
URLCellData { url, content }.to_json() URLCellData {
url,
content: changeset,
}
.to_json()
} }
} }