Sfoglia il codice sorgente

refactor: apply cell change set (#1589)

* refactor: update cell changeset trait

* refactor: update cell changeset documentation

Co-authored-by: nathan <[email protected]>
Nathan.fooo 2 anni fa
parent
commit
4643851b3f

+ 29 - 23
frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs

@@ -55,8 +55,8 @@ pub trait CellDataChangeset: TypeOption {
     ///  
     fn apply_changeset(
         &self,
-        changeset: AnyCellChangeset<<Self as TypeOption>::CellChangeset>,
-        cell_rev: Option<CellRevision>,
+        changeset: <Self as TypeOption>::CellChangeset,
+        type_cell_data: Option<TypeCellData>,
     ) -> FlowyResult<String>;
 }
 
@@ -73,21 +73,18 @@ pub fn apply_cell_data_changeset<C: ToString, T: AsRef<FieldRevision>>(
 ) -> Result<String, FlowyError> {
     let field_rev = field_rev.as_ref();
     let changeset = changeset.to_string();
-    let field_type = 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),
-    }?;
+    let field_type: FieldType = field_rev.ty.into();
+
+    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,
+    });
 
-    Ok(TypeCellData::new(s, field_type).to_json())
+    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>(
@@ -138,7 +135,7 @@ pub fn try_decode_cell_data(
     to_field_type: &FieldType,
     field_rev: &FieldRevision,
 ) -> 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()),
         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,
     field_rev: &FieldRevision,
 ) -> 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(),
         Some(handler) => handler.stringify_cell_data(cell_data, from_field_type, field_rev),
     }
@@ -269,6 +266,15 @@ pub trait FromCellChangeset {
         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>);
 
 impl<T> AnyCellChangeset<T> {
@@ -294,8 +300,8 @@ where
         }
     }
 }
-impl std::convert::From<String> for AnyCellChangeset<String> {
-    fn from(s: String) -> Self {
-        AnyCellChangeset(Some(s))
-    }
-}
+// impl std::convert::From<String> for AnyCellChangeset<String> {
+//     fn from(s: String) -> Self {
+//         AnyCellChangeset(Some(s))
+//     }
+// }

+ 6 - 7
frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option.rs

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

+ 3 - 4
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_tests.rs

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

+ 5 - 6
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option.rs

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

+ 6 - 7
frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs

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

+ 11 - 16
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_type_option.rs

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

+ 25 - 40
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs

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

+ 17 - 29
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/single_select_type_option.rs

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

+ 8 - 9
frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_type_option.rs

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

+ 35 - 4
frontend/rust-lib/flowy-grid/src/services/field/type_options/type_option.rs

@@ -1,5 +1,7 @@
 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::{
     CheckboxTypeOptionPB, ChecklistTypeOptionPB, DateTypeOptionPB, MultiSelectTypeOptionPB, NumberTypeOptionPB,
     RichTextTypeOptionPB, SingleSelectTypeOptionPB, URLTypeOptionPB,
@@ -23,8 +25,11 @@ pub trait TypeOption {
     ///
     type CellData: FromCellString + Default;
 
-    ///
-    type CellChangeset;
+    /// Represents as the corresponding field type cell changeset.
+    /// The changeset must implements the `FromCellChangeset` trait. The `CellChangeset` is implemented
+    /// for `String`.
+    ///  
+    type CellChangeset: FromCellChangeset;
 
     ///  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`.
@@ -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 {
     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 {
     fn handle_cell_data(
         &self,
@@ -121,6 +133,12 @@ pub trait TypeOptionCellDataHandler {
         field_rev: &FieldRevision,
     ) -> 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;
 }
 
@@ -145,6 +163,16 @@ where
         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(
         &self,
         cell_data: String,
@@ -173,7 +201,10 @@ impl<'a> FieldRevisionExt<'a> {
         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 {
             FieldType::RichText => self
                 .field_rev

+ 1 - 1
frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_tests.rs

@@ -170,7 +170,7 @@ mod tests {
         field_type: &FieldType,
         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);
         assert_eq!(expected_str.to_owned(), decode_cell_data.content);
         assert_eq!(expected_url.to_owned(), decode_cell_data.url);

+ 12 - 9
frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option.rs

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