Browse Source

Refactor/type option (#1578)

Nathan.fooo 2 years ago
parent
commit
85e489babb
43 changed files with 966 additions and 616 deletions
  1. 4 3
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  2. 2 0
      frontend/rust-lib/flowy-grid/src/lib.rs
  3. 74 186
      frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs
  4. 52 34
      frontend/rust-lib/flowy-grid/src/services/cell/type_cell_data.rs
  5. 9 6
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_filter.rs
  6. 3 2
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_tests.rs
  7. 40 29
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option.rs
  8. 15 3
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs
  9. 12 7
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_filter.rs
  10. 4 5
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_tests.rs
  11. 44 33
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option.rs
  12. 13 10
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option_entities.rs
  13. 2 1
      frontend/rust-lib/flowy-grid/src/services/field/type_options/mod.rs
  14. 7 3
      frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_filter.rs
  15. 3 2
      frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_tests.rs
  16. 50 30
      frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs
  17. 5 3
      frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option_entities.rs
  18. 28 16
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_type_option.rs
  19. 29 17
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs
  20. 12 8
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_filter.rs
  21. 31 67
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_type_option.rs
  22. 28 14
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/single_select_type_option.rs
  23. 11 14
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/type_option_transform.rs
  24. 10 6
      frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_filter.rs
  25. 5 8
      frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_tests.rs
  26. 111 31
      frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_type_option.rs
  27. 119 0
      frontend/rust-lib/flowy-grid/src/services/field/type_options/type_option.rs
  28. 10 8
      frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_filter.rs
  29. 6 7
      frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_tests.rs
  30. 41 28
      frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option.rs
  31. 21 8
      frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option_entities.rs
  32. 0 10
      frontend/rust-lib/flowy-grid/src/services/field/type_options/util/cell_data_util.rs
  33. 0 3
      frontend/rust-lib/flowy-grid/src/services/field/type_options/util/mod.rs
  34. 3 3
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  35. 2 2
      frontend/rust-lib/flowy-grid/src/services/group/action.rs
  36. 3 3
      frontend/rust-lib/flowy-grid/src/services/group/controller.rs
  37. 75 4
      frontend/rust-lib/flowy-grid/src/services/sort/controller.rs
  38. 1 1
      frontend/rust-lib/flowy-grid/src/services/view_editor/editor.rs
  39. 1 0
      frontend/rust-lib/flowy-grid/tests/grid/mod.rs
  40. 2 0
      frontend/rust-lib/flowy-grid/tests/grid/sort_test/mod.rs
  41. 53 0
      frontend/rust-lib/flowy-grid/tests/grid/sort_test/script.rs
  42. 0 0
      frontend/rust-lib/flowy-grid/tests/grid/sort_test/text_sort_test.rs
  43. 25 1
      shared-lib/grid-rev-model/src/sort_rev.rs

+ 4 - 3
frontend/rust-lib/flowy-grid/src/event_handler.rs

@@ -1,11 +1,11 @@
 use crate::entities::*;
 use crate::manager::GridManager;
-use crate::services::cell::TypeCellData;
+use crate::services::cell::{FromCellString, TypeCellData};
 use crate::services::field::{
     default_type_option_builder_from_type, select_type_option_from_field_rev, type_option_builder_from_json_str,
     DateCellChangeset, DateChangesetPB, SelectOptionCellChangeset, SelectOptionCellChangesetPB,
     SelectOptionCellChangesetParams, SelectOptionCellDataPB, SelectOptionChangeset, SelectOptionChangesetPB,
-    SelectOptionPB,
+    SelectOptionIds, SelectOptionPB,
 };
 use crate::services::row::make_row_from_row_rev;
 use flowy_error::{ErrorCode, FlowyError, FlowyResult};
@@ -418,7 +418,8 @@ pub(crate) async fn get_select_option_handler(
                 },
                 Some(cell_rev) => cell_rev.try_into()?,
             };
-            let selected_options = type_option.get_selected_options(type_cell_data.into());
+            let ids = SelectOptionIds::from_cell_str(&type_cell_data.data)?;
+            let selected_options = type_option.get_selected_options(ids);
             data_result(selected_options)
         }
     }

+ 2 - 0
frontend/rust-lib/flowy-grid/src/lib.rs

@@ -1,3 +1,5 @@
+extern crate core;
+
 #[macro_use]
 mod macros;
 

+ 74 - 186
frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs

@@ -1,113 +1,66 @@
 use crate::entities::FieldType;
-use crate::services::cell::{CellBytes, TypeCellData};
+use crate::services::cell::{CellProtobufBlob, TypeCellData};
 use crate::services::field::*;
+use flowy_error::{ErrorCode, FlowyError, FlowyResult};
+use grid_rev_model::{CellRevision, FieldRevision};
 use std::cmp::Ordering;
 use std::fmt::Debug;
 
-use flowy_error::{ErrorCode, FlowyError, FlowyResult};
-use grid_rev_model::{CellRevision, FieldRevision, FieldTypeRevision};
-
 /// This trait is used when doing filter/search on the grid.
-pub trait CellFilterable<T> {
+pub trait CellFilterable: TypeOptionConfiguration {
     /// Return true if type_cell_data match the filter condition.
-    fn apply_filter(&self, type_cell_data: TypeCellData, filter: &T) -> FlowyResult<bool>;
+    fn apply_filter(
+        &self,
+        type_cell_data: TypeCellData,
+        filter: &<Self as TypeOptionConfiguration>::CellFilterConfiguration,
+    ) -> FlowyResult<bool>;
 }
 
 pub trait CellComparable {
-    fn apply_cmp(&self, type_cell_data: &TypeCellData, other_type_cell_data: &TypeCellData) -> FlowyResult<Ordering>;
+    type CellData;
+    fn apply_cmp(&self, cell_data: &Self::CellData, other_cell_data: &Self::CellData) -> Ordering;
 }
 
-/// Serialize the cell data in Protobuf/String format.
-///
-/// Each cell data is a opaque data, it needs to deserialized to a concrete data struct.
-/// Essentially when the field type is SingleSelect/Multi-Select, the cell data contains a
-/// list of option ids. So it need to be decoded including convert the option's id to
-/// option's name
-///
-pub trait CellDataSerialize<CD> {
-    /// Serialize the cell data into `CellBytes` that will be posted to the `Dart` side. Using the
-    /// corresponding protobuf struct implemented in `Dart` to deserialize the data.
-    ///
-    /// Using `utf8` to encode the cell data if the cell data use `String` as its data container.
-    /// Using `protobuf` to encode the cell data if the cell data use `Protobuf struct` as its data container.
+/// Decode the opaque cell data into readable format content
+pub trait CellDataDecoder: TypeOption {
     ///
-    /// When switching the field type of the `FieldRevision` to another field type. The `field_type`
-    /// of the `FieldRevision` is not equal to the `decoded_field_type`. The cell data is need to do
-    /// some custom transformation.
+    /// Tries to decode the opaque cell data to `decoded_field_type`. Sometimes, the `field_type`
+    /// of the `FieldRevision` is not equal to the `decoded_field_type`(This happened When switching
+    /// the field type of the `FieldRevision` to another field type). So the cell data is need to do
+    /// some transformation.
     ///
     /// For example, the current field type of the `FieldRevision` is a checkbox. When switching the field
-    /// type from the checkbox to single select, the `TypeOptionBuilder`'s transform method gets called.
-    /// It will create two new options,`Yes` and `No`, if they don't exist. But the cell data didn't change,
-    /// because we can't iterate all the rows to transform the cell data that can be parsed by the current
-    /// field type. One approach is to transform the cell data when it get read. For the moment,
-    /// the cell data is a string, `Yes` or `No`. It needs to compare with the option's name, if match
-    /// return the id of the option. Otherwise, return a default value of `CellBytes`.
-    ///
-    /// # Arguments
-    ///
-    /// * `cell_data`: the generic annotation `CD` represents as the deserialize data type of the cell.
-    /// * `decoded_field_type`: the field type of the cell_data when doing serialization
-    ///
-    /// returns: Result<CellBytes, FlowyError>
-    ///
-    fn serialize_cell_data_to_bytes(
+    /// type from the checkbox to single select, it will create two new options,`Yes` and `No`, if they don't exist.
+    /// But the data of the cell doesn't change. We can't iterate all the rows to transform the cell
+    /// data that can be parsed by the current field type. One approach is to transform the cell data
+    /// when it get read. For the moment, the cell data is a string, `Yes` or `No`. It needs to compare
+    /// with the option's name, if match return the id of the option.
+    fn try_decode_cell_data(
         &self,
-        cell_data: IntoCellData<CD>,
+        cell_data: String,
         decoded_field_type: &FieldType,
         field_rev: &FieldRevision,
-    ) -> FlowyResult<CellBytes>;
+    ) -> FlowyResult<<Self as TypeOption>::CellData>;
 
-    /// Serialize the cell data into `String` that is readable
-    ///
-    /// The cell data is not readable which means it can't display the cell data directly to user.
-    /// For example,
-    /// 1. the cell data is timestamp if its field type is FieldType::Date that is not readable.
-    /// So it needs to be parsed as the date string with custom format setting.
-    ///
-    /// 2. the cell data is a commas separated id if its field type if FieldType::MultiSelect that is not readable.
-    /// So it needs to be parsed as a commas separated option name.
-    ///
-    fn serialize_cell_data_to_str(
+    /// Same as `decode_cell_data` does but Decode the cell data to readable `String`
+    fn decode_cell_data_to_str(
         &self,
-        cell_data: IntoCellData<CD>,
+        cell_data: String,
         decoded_field_type: &FieldType,
         field_rev: &FieldRevision,
     ) -> FlowyResult<String>;
 }
 
-pub trait CellDataOperation<CD, CS> {
-    /// The generic annotation `CD` represents as the deserialize data type of the cell data.
-    /// The Serialize/Deserialize struct of the cell is base on the field type of the cell.
-    ///
-    /// For example:
-    /// FieldType::URL => URLCellData
-    /// FieldType::Date=> DateCellData
-    ///
-    /// Each cell data is a opaque data, it needs to deserialized to a concrete data struct.
-    /// Essentially when the field type is SingleSelect/Multi-Select, the cell data contains a
-    /// list of option ids. So it need to be decoded including convert the option's id to
-    /// option's name
-    ///
-    /// `cell_data`: the opaque data of the cell.
-    /// `decoded_field_type`: the field type of the cell data when doing serialization
-    /// `field_rev`: the field of the cell data
-    ///
-    /// Returns the error if the cell data can't be parsed into `CD`.
-    ///
-    fn decode_cell_data(
-        &self,
-        cell_data: IntoCellData<CD>,
-        decoded_field_type: &FieldType,
-        field_rev: &FieldRevision,
-    ) -> FlowyResult<CellBytes>;
-
-    /// The changeset is able to parse into the concrete data struct if CS implements  
-    /// the `FromCellChangeset` trait.
-    ///
-    /// For example:
-    /// SelectOptionCellChangeset,DateCellChangeset. etc.
+pub trait CellDataChangeset: TypeOption {
+    /// The changeset is able to parse into the concrete data struct if `TypeOption::CellChangeset`
+    /// implements the `FromCellChangeset` trait.
+    /// For example,the SelectOptionCellChangeset,DateCellChangeset. etc.
     ///  
-    fn apply_changeset(&self, changeset: AnyCellChangeset<CS>, cell_rev: Option<CellRevision>) -> FlowyResult<String>;
+    fn apply_changeset(
+        &self,
+        changeset: AnyCellChangeset<<Self as TypeOption>::CellChangeset>,
+        cell_rev: Option<CellRevision>,
+    ) -> FlowyResult<String>;
 }
 
 /// changeset: It will be deserialized into specific data base on the FieldType.
@@ -143,16 +96,16 @@ pub fn apply_cell_data_changeset<C: ToString, T: AsRef<FieldRevision>>(
 pub fn decode_type_cell_data<T: TryInto<TypeCellData, Error = FlowyError> + Debug>(
     data: T,
     field_rev: &FieldRevision,
-) -> (FieldType, CellBytes) {
+) -> (FieldType, CellProtobufBlob) {
     let to_field_type = field_rev.ty.into();
     match data.try_into() {
         Ok(type_cell_data) => {
             let TypeCellData { data, field_type } = type_cell_data;
-            match try_decode_cell_data(data.into(), &field_type, &to_field_type, field_rev) {
+            match try_decode_cell_data(data, &field_type, &to_field_type, field_rev) {
                 Ok(cell_bytes) => (field_type, cell_bytes),
                 Err(e) => {
                     tracing::error!("Decode cell data failed, {:?}", e);
-                    (field_type, CellBytes::default())
+                    (field_type, CellProtobufBlob::default())
                 }
             }
         }
@@ -161,109 +114,43 @@ pub fn decode_type_cell_data<T: TryInto<TypeCellData, Error = FlowyError> + Debu
             // display the existing cell data. For example, the UI of the text cell will be blank if
             // the type of the data of cell is Number.
 
-            (to_field_type, CellBytes::default())
-        }
-    }
-}
-
-pub fn decode_cell_data_to_string<C: Into<IntoCellData<String>>>(
-    cell_data: C,
-    from_field_type: &FieldType,
-    to_field_type: &FieldType,
-    field_rev: &FieldRevision,
-) -> FlowyResult<String> {
-    let cell_data = cell_data.into().try_into_inner()?;
-    let get_cell_display_str = || {
-        let field_type: FieldTypeRevision = to_field_type.into();
-        let result = match to_field_type {
-            FieldType::RichText => field_rev
-                .get_type_option::<RichTextTypeOptionPB>(field_type)?
-                .serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
-            FieldType::Number => field_rev
-                .get_type_option::<NumberTypeOptionPB>(field_type)?
-                .serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
-            FieldType::DateTime => field_rev
-                .get_type_option::<DateTypeOptionPB>(field_type)?
-                .serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
-            FieldType::SingleSelect => field_rev
-                .get_type_option::<SingleSelectTypeOptionPB>(field_type)?
-                .serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
-            FieldType::MultiSelect => field_rev
-                .get_type_option::<MultiSelectTypeOptionPB>(field_type)?
-                .serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
-            FieldType::Checklist => field_rev
-                .get_type_option::<ChecklistTypeOptionPB>(field_type)?
-                .serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
-            FieldType::Checkbox => field_rev
-                .get_type_option::<CheckboxTypeOptionPB>(field_type)?
-                .serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
-            FieldType::URL => field_rev
-                .get_type_option::<URLTypeOptionPB>(field_type)?
-                .serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
-        };
-        Some(result)
-    };
-
-    match get_cell_display_str() {
-        Some(Ok(s)) => Ok(s),
-        Some(Err(err)) => {
-            tracing::error!("{:?}", err);
-            Ok("".to_owned())
+            (to_field_type, CellProtobufBlob::default())
         }
-        None => Ok("".to_owned()),
     }
 }
 
-/// Use the `to_field_type`'s TypeOption to parse the cell data into `from_field_type` type's data.
+/// Decode the opaque cell data from one field type to another using the corresponding type option builder
+///
+/// The cell data might become an empty string depends on these two fields' `TypeOptionBuilder`
+/// support transform or not.
+///
+/// # Arguments
+///
+/// * `cell_data`: the opaque cell data
+/// * `from_field_type`: the original field type of the passed-in cell data. Check the `TypeCellData`
+/// that is used to save the origin field type of the cell data.
+/// * `to_field_type`: decode the passed-in cell data to this field type. It will use the to_field_type's
+/// TypeOption to decode this cell data.
+/// * `field_rev`: used to get the corresponding TypeOption for the specified field type.
 ///
-/// Each `FieldType` has its corresponding `TypeOption` that implements the `CellDisplayable`
-/// and `CellDataOperation` traits.
+/// returns: CellBytes
 ///
 pub fn try_decode_cell_data(
-    cell_data: IntoCellData<String>,
+    cell_data: String,
     from_field_type: &FieldType,
     to_field_type: &FieldType,
     field_rev: &FieldRevision,
-) -> FlowyResult<CellBytes> {
-    let cell_data = cell_data.try_into_inner()?;
-    let get_cell_data = || {
-        let field_type: FieldTypeRevision = to_field_type.into();
-        let data = match to_field_type {
-            FieldType::RichText => field_rev
-                .get_type_option::<RichTextTypeOptionPB>(field_type)?
-                .decode_cell_data(cell_data.into(), from_field_type, field_rev),
-            FieldType::Number => field_rev
-                .get_type_option::<NumberTypeOptionPB>(field_type)?
-                .decode_cell_data(cell_data.into(), from_field_type, field_rev),
-            FieldType::DateTime => field_rev
-                .get_type_option::<DateTypeOptionPB>(field_type)?
-                .decode_cell_data(cell_data.into(), from_field_type, field_rev),
-            FieldType::SingleSelect => field_rev
-                .get_type_option::<SingleSelectTypeOptionPB>(field_type)?
-                .decode_cell_data(cell_data.into(), from_field_type, field_rev),
-            FieldType::MultiSelect => field_rev
-                .get_type_option::<MultiSelectTypeOptionPB>(field_type)?
-                .decode_cell_data(cell_data.into(), from_field_type, field_rev),
-            FieldType::Checklist => field_rev
-                .get_type_option::<ChecklistTypeOptionPB>(field_type)?
-                .decode_cell_data(cell_data.into(), from_field_type, field_rev),
-            FieldType::Checkbox => field_rev
-                .get_type_option::<CheckboxTypeOptionPB>(field_type)?
-                .decode_cell_data(cell_data.into(), from_field_type, field_rev),
-            FieldType::URL => field_rev
-                .get_type_option::<URLTypeOptionPB>(field_type)?
-                .decode_cell_data(cell_data.into(), from_field_type, field_rev),
-        };
-        Some(data)
-    };
+) -> FlowyResult<CellProtobufBlob> {
+    match FieldRevisionExt::new(field_rev).get_type_option_handler(to_field_type) {
+        None => Ok(CellProtobufBlob::default()),
+        Some(handler) => handler.handle_cell_data(cell_data, from_field_type, field_rev),
+    }
+}
 
-    match get_cell_data() {
-        Some(Ok(data)) => Ok(data),
-        Some(Err(err)) => {
-            tracing::error!("{:?}", err);
-            Ok(CellBytes::default())
-        }
-        None => Ok(CellBytes::default()),
+pub fn stringify_cell_data(cell_data: String, field_type: &FieldType, field_rev: &FieldRevision) -> String {
+    match FieldRevisionExt::new(field_rev).get_type_option_handler(field_type) {
+        None => "".to_string(),
+        Some(handler) => handler.stringify_cell_data(cell_data, field_type, field_rev),
     }
 }
 
@@ -322,7 +209,8 @@ pub trait FromCellString {
         Self: Sized;
 }
 
-/// IntoCellData is a helper struct. String will be parser into Option<T> only if the T impl the FromCellString trait.
+/// IntoCellData is a helper struct used to deserialize string into a specific data type that implements
+/// the `FromCellString` trait.
 ///
 pub struct IntoCellData<T>(pub Option<T>);
 impl<T> IntoCellData<T> {
@@ -349,18 +237,18 @@ where
     }
 }
 
-impl std::convert::From<usize> for IntoCellData<String> {
-    fn from(n: usize) -> Self {
-        IntoCellData(Some(n.to_string()))
-    }
-}
-
 impl<T> std::convert::From<T> for IntoCellData<T> {
     fn from(val: T) -> Self {
         IntoCellData(Some(val))
     }
 }
 
+impl std::convert::From<usize> for IntoCellData<String> {
+    fn from(n: usize) -> Self {
+        IntoCellData(Some(n.to_string()))
+    }
+}
+
 impl std::convert::From<IntoCellData<String>> for String {
     fn from(p: IntoCellData<String>) -> Self {
         p.try_into_inner().unwrap_or_else(|_| String::new())

+ 52 - 34
frontend/rust-lib/flowy-grid/src/services/cell/type_cell_data.rs

@@ -4,11 +4,16 @@ use bytes::Bytes;
 use flowy_error::{internal_error, FlowyError, FlowyResult};
 use grid_rev_model::CellRevision;
 use serde::{Deserialize, Serialize};
-use std::str::FromStr;
 
 /// TypeCellData is a generic CellData, you can parse the type_cell_data according to the field_type.
-/// When the type of field is changed, it's different from the field_type of TypeCellData.
-/// So it will return an empty data. You could check the CellDataOperation trait for more information.
+/// The `data` is encoded by JSON format. You can use `IntoCellData` to decode the opaque data to
+/// concrete cell type.
+/// TypeCellData -> IntoCellData<T> -> T
+///
+/// The `TypeCellData` is the same as the cell data that was saved to disk except it carries the
+/// field_type. The field_type indicates the cell data original `FieldType`. The field_type will
+/// be changed if the current Field's type switch from one to another.  
+///
 #[derive(Debug, Serialize, Deserialize)]
 pub struct TypeCellData {
     pub data: String,
@@ -22,25 +27,40 @@ impl TypeCellData {
             field_type: field_type.clone(),
         }
     }
-}
 
-impl std::str::FromStr for TypeCellData {
-    type Err = FlowyError;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        let type_option_cell_data: TypeCellData = serde_json::from_str(s).map_err(|err| {
-            let msg = format!("Deserialize {} to any cell data failed. Serde error: {}", s, err);
+    pub fn from_json_str(s: &str) -> FlowyResult<Self> {
+        let type_cell_data: TypeCellData = serde_json::from_str(s).map_err(|err| {
+            let msg = format!("Deserialize {} to any cell data failed.{}", s, err);
             FlowyError::internal().context(msg)
         })?;
-        Ok(type_option_cell_data)
+        Ok(type_cell_data)
+    }
+
+    pub fn into_inner(self) -> String {
+        self.data
     }
 }
 
-impl std::convert::TryInto<TypeCellData> for String {
+impl std::convert::TryFrom<String> for TypeCellData {
     type Error = FlowyError;
 
-    fn try_into(self) -> Result<TypeCellData, Self::Error> {
-        TypeCellData::from_str(&self)
+    fn try_from(value: String) -> Result<Self, Self::Error> {
+        TypeCellData::from_json_str(&value)
+    }
+}
+
+impl<T> std::convert::From<TypeCellData> for IntoCellData<T>
+where
+    T: FromCellString,
+{
+    fn from(any_call_data: TypeCellData) -> Self {
+        IntoCellData::from(any_call_data.data)
+    }
+}
+
+impl ToString for TypeCellData {
+    fn to_string(&self) -> String {
+        self.data.clone()
     }
 }
 
@@ -48,7 +68,7 @@ impl std::convert::TryFrom<&CellRevision> for TypeCellData {
     type Error = FlowyError;
 
     fn try_from(value: &CellRevision) -> Result<Self, Self::Error> {
-        Self::from_str(&value.data)
+        Self::from_json_str(&value.data)
     }
 }
 
@@ -60,15 +80,6 @@ impl std::convert::TryFrom<CellRevision> for TypeCellData {
     }
 }
 
-impl<T> std::convert::From<TypeCellData> for IntoCellData<T>
-where
-    T: FromCellString,
-{
-    fn from(any_call_data: TypeCellData) -> Self {
-        IntoCellData::from(any_call_data.data)
-    }
-}
-
 impl TypeCellData {
     pub fn new(content: String, field_type: FieldType) -> Self {
         TypeCellData {
@@ -104,6 +115,7 @@ impl TypeCellData {
     pub fn is_multi_select(&self) -> bool {
         self.field_type == FieldType::MultiSelect
     }
+
     pub fn is_checklist(&self) -> bool {
         self.field_type == FieldType::Checklist
     }
@@ -121,28 +133,34 @@ impl TypeCellData {
 ///
 /// For example:
 ///
-/// * Use DateCellData to parse the data when the FieldType is Date.
-/// * Use URLCellData to parse the data when the FieldType is URL.
+/// * Use DateCellDataPB to parse the data when the FieldType is Date.
+/// * Use URLCellDataPB to parse the data when the FieldType is URL.
 /// * Use String to parse the data when the FieldType is RichText, Number, or Checkbox.
 /// * Check out the implementation of CellDataOperation trait for more information.
 #[derive(Default, Debug)]
-pub struct CellBytes(pub Bytes);
+pub struct CellProtobufBlob(pub Bytes);
 
-pub trait CellDataIsEmpty {
+pub trait DecodedCellData {
+    type Object;
     fn is_empty(&self) -> bool;
 }
 
-pub trait CellBytesParser {
-    type Object: CellDataIsEmpty;
+pub trait CellProtobufBlobParser {
+    type Object: DecodedCellData;
     fn parser(bytes: &Bytes) -> FlowyResult<Self::Object>;
 }
 
+pub trait CellStringParser {
+    type Object;
+    fn parser_cell_str(&self, s: &str) -> Option<Self::Object>;
+}
+
 pub trait CellBytesCustomParser {
     type Object;
     fn parse(&self, bytes: &Bytes) -> FlowyResult<Self::Object>;
 }
 
-impl CellBytes {
+impl CellProtobufBlob {
     pub fn new<T: AsRef<[u8]>>(data: T) -> Self {
         let bytes = Bytes::from(data.as_ref().to_vec());
         Self(bytes)
@@ -158,7 +176,7 @@ impl CellBytes {
 
     pub fn parser<P>(&self) -> FlowyResult<P::Object>
     where
-        P: CellBytesParser,
+        P: CellProtobufBlobParser,
     {
         P::parser(&self.0)
     }
@@ -178,7 +196,7 @@ impl CellBytes {
     // }
 }
 
-impl ToString for CellBytes {
+impl ToString for CellProtobufBlob {
     fn to_string(&self) -> String {
         match String::from_utf8(self.0.to_vec()) {
             Ok(s) => s,
@@ -190,7 +208,7 @@ impl ToString for CellBytes {
     }
 }
 
-impl std::ops::Deref for CellBytes {
+impl std::ops::Deref for CellProtobufBlob {
     type Target = Bytes;
 
     fn deref(&self) -> &Self::Target {

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

@@ -1,6 +1,6 @@
 use crate::entities::{CheckboxFilterConditionPB, CheckboxFilterPB};
-use crate::services::cell::{CellFilterable, IntoCellData, TypeCellData};
-use crate::services::field::{CheckboxCellData, CheckboxTypeOptionPB};
+use crate::services::cell::{CellFilterable, TypeCellData};
+use crate::services::field::{CheckboxCellData, CheckboxTypeOptionPB, TypeOptionCellData, TypeOptionConfiguration};
 use flowy_error::FlowyResult;
 
 impl CheckboxFilterPB {
@@ -13,13 +13,16 @@ impl CheckboxFilterPB {
     }
 }
 
-impl CellFilterable<CheckboxFilterPB> for CheckboxTypeOptionPB {
-    fn apply_filter(&self, type_cell_data: TypeCellData, filter: &CheckboxFilterPB) -> FlowyResult<bool> {
+impl CellFilterable for CheckboxTypeOptionPB {
+    fn apply_filter(
+        &self,
+        type_cell_data: TypeCellData,
+        filter: &<Self as TypeOptionConfiguration>::CellFilterConfiguration,
+    ) -> FlowyResult<bool> {
         if !type_cell_data.is_checkbox() {
             return Ok(true);
         }
-        let cell_data: IntoCellData<CheckboxCellData> = type_cell_data.into();
-        let checkbox_cell_data = cell_data.try_into_inner()?;
+        let checkbox_cell_data = self.decode_type_option_cell_data(type_cell_data.data)?;
         Ok(filter.is_visible(&checkbox_cell_data))
     }
 }

+ 3 - 2
frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_tests.rs

@@ -1,9 +1,10 @@
 #[cfg(test)]
 mod tests {
     use crate::entities::FieldType;
-    use crate::services::cell::CellDataOperation;
+    use crate::services::cell::CellDataDecoder;
     use crate::services::field::type_options::checkbox_type_option::*;
     use crate::services::field::FieldBuilder;
+
     use grid_rev_model::FieldRevision;
 
     #[test]
@@ -35,7 +36,7 @@ mod tests {
     ) {
         assert_eq!(
             type_option
-                .decode_cell_data(input_str.to_owned().into(), field_type, field_rev)
+                .try_decode_cell_data(input_str.to_owned(), field_type, field_rev)
                 .unwrap()
                 .to_string(),
             expected_str.to_owned()

+ 40 - 29
frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option.rs

@@ -1,7 +1,9 @@
-use crate::entities::FieldType;
+use crate::entities::{CheckboxFilterPB, FieldType};
 use crate::impl_type_option;
-use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
-use crate::services::field::{BoxTypeOptionBuilder, CheckboxCellData, TypeOptionBuilder};
+use crate::services::cell::{AnyCellChangeset, CellDataChangeset, CellDataDecoder, FromCellString};
+use crate::services::field::{
+    BoxTypeOptionBuilder, CheckboxCellData, TypeOption, TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration,
+};
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
 use flowy_error::{FlowyError, FlowyResult};
@@ -42,47 +44,56 @@ pub struct CheckboxTypeOptionPB {
 }
 impl_type_option!(CheckboxTypeOptionPB, FieldType::Checkbox);
 
-impl CellDataSerialize<CheckboxCellData> for CheckboxTypeOptionPB {
-    fn serialize_cell_data_to_bytes(
+impl TypeOption for CheckboxTypeOptionPB {
+    type CellData = CheckboxCellData;
+    type CellChangeset = CheckboxCellChangeset;
+    type CellPBType = CheckboxCellData;
+}
+
+impl TypeOptionConfiguration for CheckboxTypeOptionPB {
+    type CellFilterConfiguration = CheckboxFilterPB;
+}
+
+impl TypeOptionCellData for CheckboxTypeOptionPB {
+    fn convert_into_pb_type(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellPBType {
+        cell_data
+    }
+
+    fn decode_type_option_cell_data(&self, cell_data: String) -> FlowyResult<<Self as TypeOption>::CellData> {
+        CheckboxCellData::from_cell_str(&cell_data)
+    }
+}
+
+impl CellDataDecoder for CheckboxTypeOptionPB {
+    fn try_decode_cell_data(
         &self,
-        cell_data: IntoCellData<CheckboxCellData>,
-        _decoded_field_type: &FieldType,
+        cell_data: String,
+        decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
-    ) -> FlowyResult<CellBytes> {
-        let cell_data = cell_data.try_into_inner()?;
-        Ok(CellBytes::new(cell_data))
+    ) -> FlowyResult<<Self as TypeOption>::CellData> {
+        if !decoded_field_type.is_checkbox() {
+            return Ok(Default::default());
+        }
+
+        self.decode_type_option_cell_data(cell_data)
     }
 
-    fn serialize_cell_data_to_str(
+    fn decode_cell_data_to_str(
         &self,
-        cell_data: IntoCellData<CheckboxCellData>,
+        cell_data: String,
         _decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<String> {
-        let cell_data = cell_data.try_into_inner()?;
-        Ok(cell_data.to_string())
+        Ok(cell_data)
     }
 }
 
 pub type CheckboxCellChangeset = String;
 
-impl CellDataOperation<CheckboxCellData, CheckboxCellChangeset> for CheckboxTypeOptionPB {
-    fn decode_cell_data(
-        &self,
-        cell_data: IntoCellData<CheckboxCellData>,
-        decoded_field_type: &FieldType,
-        field_rev: &FieldRevision,
-    ) -> FlowyResult<CellBytes> {
-        if !decoded_field_type.is_checkbox() {
-            return Ok(CellBytes::default());
-        }
-
-        self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
-    }
-
+impl CellDataChangeset for CheckboxTypeOptionPB {
     fn apply_changeset(
         &self,
-        changeset: AnyCellChangeset<String>,
+        changeset: AnyCellChangeset<CheckboxCellChangeset>,
         _cell_rev: Option<CellRevision>,
     ) -> Result<String, FlowyError> {
         let changeset = changeset.try_into_inner()?;

+ 15 - 3
frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs

@@ -1,11 +1,13 @@
-use crate::services::cell::{CellBytesParser, CellDataIsEmpty, FromCellString};
+use crate::services::cell::{CellProtobufBlobParser, DecodedCellData, FromCellString};
 use bytes::Bytes;
 use flowy_error::{FlowyError, FlowyResult};
+use protobuf::ProtobufError;
 use std::str::FromStr;
 
 pub const CHECK: &str = "Yes";
 pub const UNCHECK: &str = "No";
 
+#[derive(Default, Debug)]
 pub struct CheckboxCellData(String);
 
 impl CheckboxCellData {
@@ -47,6 +49,14 @@ impl FromStr for CheckboxCellData {
     }
 }
 
+impl std::convert::TryFrom<CheckboxCellData> for Bytes {
+    type Error = ProtobufError;
+
+    fn try_from(value: CheckboxCellData) -> Result<Self, Self::Error> {
+        Ok(Bytes::from(value.0))
+    }
+}
+
 impl FromCellString for CheckboxCellData {
     fn from_cell_str(s: &str) -> FlowyResult<Self>
     where
@@ -62,14 +72,16 @@ impl ToString for CheckboxCellData {
     }
 }
 
-impl CellDataIsEmpty for CheckboxCellData {
+impl DecodedCellData for CheckboxCellData {
+    type Object = CheckboxCellData;
+
     fn is_empty(&self) -> bool {
         self.0.is_empty()
     }
 }
 
 pub struct CheckboxCellDataParser();
-impl CellBytesParser for CheckboxCellDataParser {
+impl CellProtobufBlobParser for CheckboxCellDataParser {
     type Object = CheckboxCellData;
     fn parser(bytes: &Bytes) -> FlowyResult<Self::Object> {
         match String::from_utf8(bytes.to_vec()) {

+ 12 - 7
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_filter.rs

@@ -1,7 +1,8 @@
 use crate::entities::{DateFilterConditionPB, DateFilterPB};
-use crate::services::cell::{CellFilterable, IntoCellData, TypeCellData};
-use crate::services::field::{DateTimestamp, DateTypeOptionPB};
+use crate::services::cell::{CellFilterable, TypeCellData};
+use crate::services::field::{DateTypeOptionPB, TypeOptionCellData, TypeOptionConfiguration};
 use chrono::NaiveDateTime;
+
 use flowy_error::FlowyResult;
 
 impl DateFilterPB {
@@ -59,14 +60,18 @@ impl DateFilterPB {
     }
 }
 
-impl CellFilterable<DateFilterPB> for DateTypeOptionPB {
-    fn apply_filter(&self, type_cell_data: TypeCellData, filter: &DateFilterPB) -> FlowyResult<bool> {
+impl CellFilterable for DateTypeOptionPB {
+    fn apply_filter(
+        &self,
+        type_cell_data: TypeCellData,
+        filter: &<Self as TypeOptionConfiguration>::CellFilterConfiguration,
+    ) -> FlowyResult<bool> {
         if !type_cell_data.is_date() {
             return Ok(true);
         }
-        let cell_data: IntoCellData<DateTimestamp> = type_cell_data.into();
-        let timestamp = cell_data.try_into_inner()?;
-        Ok(filter.is_visible(timestamp))
+
+        let date_cell_data = self.decode_type_option_cell_data(type_cell_data.data)?;
+        Ok(filter.is_visible(date_cell_data))
     }
 }
 

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

@@ -1,7 +1,8 @@
 #[cfg(test)]
 mod tests {
     use crate::entities::FieldType;
-    use crate::services::cell::CellDataOperation;
+    use crate::services::cell::{CellDataChangeset, CellDataDecoder};
+
     use crate::services::field::*;
     // use crate::services::field::{DateCellChangeset, DateCellData, DateFormat, DateTypeOptionPB, TimeFormat};
     use chrono::format::strftime::StrftimeItems;
@@ -162,11 +163,9 @@ mod tests {
 
     fn decode_cell_data(encoded_data: String, type_option: &DateTypeOptionPB, field_rev: &FieldRevision) -> String {
         let decoded_data = type_option
-            .decode_cell_data(encoded_data.into(), &FieldType::DateTime, field_rev)
-            .unwrap()
-            .parser::<DateCellDataParser>()
+            .try_decode_cell_data(encoded_data, &FieldType::DateTime, field_rev)
             .unwrap();
-
+        let decoded_data = type_option.convert_into_pb_type(decoded_data);
         if type_option.include_time {
             format!("{} {}", decoded_data.date, decoded_data.time)
                 .trim_end()

+ 44 - 33
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option.rs

@@ -1,8 +1,9 @@
-use crate::entities::FieldType;
+use crate::entities::{DateFilterPB, FieldType};
 use crate::impl_type_option;
-use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
+use crate::services::cell::{AnyCellChangeset, CellDataChangeset, CellDataDecoder, FromCellString};
 use crate::services::field::{
-    BoxTypeOptionBuilder, DateCellChangeset, DateCellDataPB, DateFormat, DateTimestamp, TimeFormat, TypeOptionBuilder,
+    BoxTypeOptionBuilder, DateCellChangeset, DateCellData, DateCellDataPB, DateFormat, TimeFormat, TypeOption,
+    TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration,
 };
 use bytes::Bytes;
 use chrono::format::strftime::StrftimeItems;
@@ -26,6 +27,26 @@ pub struct DateTypeOptionPB {
 }
 impl_type_option!(DateTypeOptionPB, FieldType::DateTime);
 
+impl TypeOption for DateTypeOptionPB {
+    type CellData = DateCellData;
+    type CellChangeset = DateCellChangeset;
+    type CellPBType = DateCellDataPB;
+}
+
+impl TypeOptionConfiguration for DateTypeOptionPB {
+    type CellFilterConfiguration = DateFilterPB;
+}
+
+impl TypeOptionCellData for DateTypeOptionPB {
+    fn convert_into_pb_type(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellPBType {
+        self.today_desc_from_timestamp(cell_data)
+    }
+
+    fn decode_type_option_cell_data(&self, cell_data: String) -> FlowyResult<<Self as TypeOption>::CellData> {
+        DateCellData::from_cell_str(&cell_data)
+    }
+}
+
 impl DateTypeOptionPB {
     #[allow(dead_code)]
     pub fn new() -> Self {
@@ -107,47 +128,37 @@ impl DateTypeOptionPB {
     }
 }
 
-impl CellDataSerialize<DateTimestamp> for DateTypeOptionPB {
-    fn serialize_cell_data_to_bytes(
+impl CellDataDecoder for DateTypeOptionPB {
+    fn try_decode_cell_data(
         &self,
-        cell_data: IntoCellData<DateTimestamp>,
-        _decoded_field_type: &FieldType,
+        cell_data: String,
+        decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
-    ) -> FlowyResult<CellBytes> {
-        let timestamp = cell_data.try_into_inner()?;
-        let cell_data_pb = self.today_desc_from_timestamp(timestamp);
-        CellBytes::from(cell_data_pb)
+    ) -> FlowyResult<<Self as TypeOption>::CellData> {
+        // Return default data if the type_option_cell_data is not FieldType::DateTime.
+        // It happens when switching from one field to another.
+        // For example:
+        // FieldType::RichText -> FieldType::DateTime, it will display empty content on the screen.
+        if !decoded_field_type.is_date() {
+            return Ok(Default::default());
+        }
+
+        self.decode_type_option_cell_data(cell_data)
     }
 
-    fn serialize_cell_data_to_str(
+    fn decode_cell_data_to_str(
         &self,
-        cell_data: IntoCellData<DateTimestamp>,
+        cell_data: String,
         _decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<String> {
-        let timestamp = cell_data.try_into_inner()?;
-        let date_cell_data = self.today_desc_from_timestamp(timestamp);
-        Ok(date_cell_data.date)
+        let cell_data = self.decode_type_option_cell_data(cell_data)?;
+        let cell_data_pb = self.today_desc_from_timestamp(cell_data);
+        Ok(cell_data_pb.date)
     }
 }
 
-impl CellDataOperation<DateTimestamp, DateCellChangeset> for DateTypeOptionPB {
-    fn decode_cell_data(
-        &self,
-        cell_data: IntoCellData<DateTimestamp>,
-        decoded_field_type: &FieldType,
-        field_rev: &FieldRevision,
-    ) -> FlowyResult<CellBytes> {
-        // Return default data if the type_option_cell_data is not FieldType::DateTime.
-        // It happens when switching from one field to another.
-        // For example:
-        // FieldType::RichText -> FieldType::DateTime, it will display empty content on the screen.
-        if !decoded_field_type.is_date() {
-            return Ok(CellBytes::default());
-        }
-        self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
-    }
-
+impl CellDataChangeset for DateTypeOptionPB {
     fn apply_changeset(
         &self,
         changeset: AnyCellChangeset<DateCellChangeset>,

+ 13 - 10
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option_entities.rs

@@ -1,5 +1,5 @@
 use crate::entities::CellPathPB;
-use crate::services::cell::{CellBytesParser, CellDataIsEmpty, FromCellChangeset, FromCellString};
+use crate::services::cell::{CellProtobufBlobParser, DecodedCellData, FromCellChangeset, FromCellString};
 use bytes::Bytes;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::{internal_error, FlowyResult};
@@ -68,27 +68,28 @@ impl ToString for DateCellChangeset {
     }
 }
 
-pub struct DateTimestamp(Option<i64>);
+#[derive(Default)]
+pub struct DateCellData(pub Option<i64>);
 
-impl std::convert::From<DateTimestamp> for i64 {
-    fn from(timestamp: DateTimestamp) -> Self {
+impl std::convert::From<DateCellData> for i64 {
+    fn from(timestamp: DateCellData) -> Self {
         timestamp.0.unwrap_or(0)
     }
 }
 
-impl std::convert::From<DateTimestamp> for Option<i64> {
-    fn from(timestamp: DateTimestamp) -> Self {
+impl std::convert::From<DateCellData> for Option<i64> {
+    fn from(timestamp: DateCellData) -> Self {
         timestamp.0
     }
 }
 
-impl FromCellString for DateTimestamp {
+impl FromCellString for DateCellData {
     fn from_cell_str(s: &str) -> FlowyResult<Self>
     where
         Self: Sized,
     {
         let num = s.parse::<i64>().ok();
-        Ok(DateTimestamp(num))
+        Ok(DateCellData(num))
     }
 }
 
@@ -174,14 +175,16 @@ impl std::default::Default for TimeFormat {
     }
 }
 
-impl CellDataIsEmpty for DateCellDataPB {
+impl DecodedCellData for DateCellDataPB {
+    type Object = DateCellDataPB;
+
     fn is_empty(&self) -> bool {
         self.date.is_empty()
     }
 }
 
 pub struct DateCellDataParser();
-impl CellBytesParser for DateCellDataParser {
+impl CellProtobufBlobParser for DateCellDataParser {
     type Object = DateCellDataPB;
 
     fn parser(bytes: &Bytes) -> FlowyResult<Self::Object> {

+ 2 - 1
frontend/rust-lib/flowy-grid/src/services/field/type_options/mod.rs

@@ -3,12 +3,13 @@ pub mod date_type_option;
 pub mod number_type_option;
 pub mod selection_type_option;
 pub mod text_type_option;
+mod type_option;
 pub mod url_type_option;
-mod util;
 
 pub use checkbox_type_option::*;
 pub use date_type_option::*;
 pub use number_type_option::*;
 pub use selection_type_option::*;
 pub use text_type_option::*;
+pub use type_option::*;
 pub use url_type_option::*;

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

@@ -1,6 +1,6 @@
 use crate::entities::{NumberFilterConditionPB, NumberFilterPB};
 use crate::services::cell::{CellFilterable, TypeCellData};
-use crate::services::field::{NumberCellData, NumberTypeOptionPB};
+use crate::services::field::{NumberCellData, NumberTypeOptionPB, TypeOptionConfiguration};
 use flowy_error::FlowyResult;
 use rust_decimal::prelude::Zero;
 use rust_decimal::Decimal;
@@ -37,8 +37,12 @@ impl NumberFilterPB {
     }
 }
 
-impl CellFilterable<NumberFilterPB> for NumberTypeOptionPB {
-    fn apply_filter(&self, type_cell_data: TypeCellData, filter: &NumberFilterPB) -> FlowyResult<bool> {
+impl CellFilterable for NumberTypeOptionPB {
+    fn apply_filter(
+        &self,
+        type_cell_data: TypeCellData,
+        filter: &<Self as TypeOptionConfiguration>::CellFilterConfiguration,
+    ) -> FlowyResult<bool> {
         if !type_cell_data.is_number() {
             return Ok(true);
         }

+ 3 - 2
frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_tests.rs

@@ -1,8 +1,9 @@
 #[cfg(test)]
 mod tests {
     use crate::entities::FieldType;
-    use crate::services::cell::CellDataOperation;
+    use crate::services::cell::CellDataDecoder;
     use crate::services::field::FieldBuilder;
+
     use crate::services::field::{strip_currency_symbol, NumberFormat, NumberTypeOptionPB};
     use grid_rev_model::FieldRevision;
     use strum::IntoEnumIterator;
@@ -438,7 +439,7 @@ mod tests {
     ) {
         assert_eq!(
             type_option
-                .decode_cell_data(input_str.to_owned().into(), field_type, field_rev)
+                .try_decode_cell_data(input_str.to_owned(), field_type, field_rev)
                 .unwrap()
                 .to_string(),
             expected_str.to_owned()

+ 50 - 30
frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs

@@ -1,14 +1,19 @@
-use crate::entities::FieldType;
+use crate::entities::{FieldType, NumberFilterPB};
 use crate::impl_type_option;
-use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
+use crate::services::cell::{AnyCellChangeset, CellComparable, CellDataChangeset, CellDataDecoder};
 use crate::services::field::type_options::number_type_option::format::*;
-use crate::services::field::{BoxTypeOptionBuilder, NumberCellData, TypeOptionBuilder};
+use crate::services::field::{
+    BoxTypeOptionBuilder, NumberCellData, StrCellData, TypeOption, TypeOptionBuilder, TypeOptionCellData,
+    TypeOptionConfiguration,
+};
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
 use flowy_error::{FlowyError, FlowyResult};
 use grid_rev_model::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer};
 use rust_decimal::Decimal;
 use serde::{Deserialize, Serialize};
+use std::cmp::Ordering;
+use std::default::Default;
 use std::str::FromStr;
 
 #[derive(Default)]
@@ -71,6 +76,26 @@ pub struct NumberTypeOptionPB {
 }
 impl_type_option!(NumberTypeOptionPB, FieldType::Number);
 
+impl TypeOption for NumberTypeOptionPB {
+    type CellData = StrCellData;
+    type CellChangeset = NumberCellChangeset;
+    type CellPBType = StrCellData;
+}
+
+impl TypeOptionConfiguration for NumberTypeOptionPB {
+    type CellFilterConfiguration = NumberFilterPB;
+}
+
+impl TypeOptionCellData for NumberTypeOptionPB {
+    fn convert_into_pb_type(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellPBType {
+        cell_data
+    }
+
+    fn decode_type_option_cell_data(&self, cell_data: String) -> FlowyResult<<Self as TypeOption>::CellData> {
+        Ok(cell_data.into())
+    }
+}
+
 impl NumberTypeOptionPB {
     pub fn new() -> Self {
         Self::default()
@@ -103,50 +128,38 @@ pub(crate) fn strip_currency_symbol<T: ToString>(s: T) -> String {
     s
 }
 
-impl CellDataSerialize<String> for NumberTypeOptionPB {
-    fn serialize_cell_data_to_bytes(
+impl CellDataDecoder for NumberTypeOptionPB {
+    fn try_decode_cell_data(
         &self,
-        cell_data: IntoCellData<String>,
-        _decoded_field_type: &FieldType,
+        cell_data: String,
+        decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
-    ) -> FlowyResult<CellBytes> {
-        let cell_data: String = cell_data.try_into_inner()?;
-        match self.format_cell_data(&cell_data) {
-            Ok(num) => Ok(CellBytes::new(num.to_string())),
-            Err(_) => Ok(CellBytes::default()),
+    ) -> FlowyResult<<Self as TypeOption>::CellData> {
+        if decoded_field_type.is_date() {
+            return Ok(Default::default());
         }
+
+        let str_cell_data = self.decode_type_option_cell_data(cell_data)?;
+        let s = self.format_cell_data(&str_cell_data)?.to_string();
+        Ok(s.into())
     }
 
-    fn serialize_cell_data_to_str(
+    fn decode_cell_data_to_str(
         &self,
-        cell_data: IntoCellData<String>,
+        cell_data: String,
         _decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<String> {
-        let cell_data: String = cell_data.try_into_inner()?;
         Ok(cell_data)
     }
 }
 
 pub type NumberCellChangeset = String;
 
-impl CellDataOperation<String, NumberCellChangeset> for NumberTypeOptionPB {
-    fn decode_cell_data(
-        &self,
-        cell_data: IntoCellData<String>,
-        decoded_field_type: &FieldType,
-        field_rev: &FieldRevision,
-    ) -> FlowyResult<CellBytes> {
-        if decoded_field_type.is_date() {
-            return Ok(CellBytes::default());
-        }
-
-        self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
-    }
-
+impl CellDataChangeset for NumberTypeOptionPB {
     fn apply_changeset(
         &self,
-        changeset: AnyCellChangeset<String>,
+        changeset: AnyCellChangeset<NumberCellChangeset>,
         _cell_rev: Option<CellRevision>,
     ) -> Result<String, FlowyError> {
         let changeset = changeset.try_into_inner()?;
@@ -155,6 +168,13 @@ impl CellDataOperation<String, NumberCellChangeset> for NumberTypeOptionPB {
         Ok(data)
     }
 }
+impl CellComparable for NumberTypeOptionPB {
+    type CellData = NumberCellData;
+
+    fn apply_cmp(&self, _cell_data: &Self::CellData, _other_cell_data: &Self::CellData) -> Ordering {
+        Ordering::Equal
+    }
+}
 
 impl std::default::Default for NumberTypeOptionPB {
     fn default() -> Self {

+ 5 - 3
frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option_entities.rs

@@ -1,4 +1,4 @@
-use crate::services::cell::{CellBytesCustomParser, CellBytesParser, CellDataIsEmpty};
+use crate::services::cell::{CellBytesCustomParser, CellProtobufBlobParser, DecodedCellData};
 use crate::services::field::number_currency::Currency;
 use crate::services::field::{strip_currency_symbol, NumberFormat, STRIP_SYMBOL};
 use bytes::Bytes;
@@ -94,14 +94,16 @@ impl ToString for NumberCellData {
     }
 }
 
-impl CellDataIsEmpty for NumberCellData {
+impl DecodedCellData for NumberCellData {
+    type Object = NumberCellData;
+
     fn is_empty(&self) -> bool {
         self.decimal.is_none()
     }
 }
 
 pub struct NumberCellDataParser();
-impl CellBytesParser for NumberCellDataParser {
+impl CellProtobufBlobParser for NumberCellDataParser {
     type Object = NumberCellData;
     fn parser(bytes: &Bytes) -> FlowyResult<Self::Object> {
         match String::from_utf8(bytes.to_vec()) {

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

@@ -1,11 +1,10 @@
-use crate::entities::FieldType;
+use crate::entities::{ChecklistFilterPB, FieldType};
 use crate::impl_type_option;
-use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
+use crate::services::cell::{AnyCellChangeset, CellDataChangeset, FromCellString, TypeCellData};
 use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer;
-use crate::services::field::type_options::util::get_cell_data;
 use crate::services::field::{
-    BoxTypeOptionBuilder, SelectOptionCellChangeset, SelectOptionIds, SelectOptionPB, SelectTypeOptionSharedAction,
-    TypeOptionBuilder,
+    BoxTypeOptionBuilder, SelectOptionCellChangeset, SelectOptionCellDataPB, SelectOptionIds, SelectOptionPB,
+    SelectTypeOptionSharedAction, TypeOption, TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration,
 };
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
@@ -24,6 +23,26 @@ pub struct ChecklistTypeOptionPB {
 }
 impl_type_option!(ChecklistTypeOptionPB, FieldType::Checklist);
 
+impl TypeOption for ChecklistTypeOptionPB {
+    type CellData = SelectOptionIds;
+    type CellChangeset = SelectOptionCellChangeset;
+    type CellPBType = SelectOptionCellDataPB;
+}
+
+impl TypeOptionConfiguration for ChecklistTypeOptionPB {
+    type CellFilterConfiguration = ChecklistFilterPB;
+}
+
+impl TypeOptionCellData for ChecklistTypeOptionPB {
+    fn convert_into_pb_type(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellPBType {
+        self.get_selected_options(cell_data)
+    }
+
+    fn decode_type_option_cell_data(&self, cell_data: String) -> FlowyResult<<Self as TypeOption>::CellData> {
+        SelectOptionIds::from_cell_str(&cell_data)
+    }
+}
+
 impl SelectTypeOptionSharedAction for ChecklistTypeOptionPB {
     fn number_of_max_options(&self) -> Option<usize> {
         None
@@ -38,16 +57,7 @@ impl SelectTypeOptionSharedAction for ChecklistTypeOptionPB {
     }
 }
 
-impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for ChecklistTypeOptionPB {
-    fn decode_cell_data(
-        &self,
-        cell_data: IntoCellData<SelectOptionIds>,
-        decoded_field_type: &FieldType,
-        field_rev: &FieldRevision,
-    ) -> FlowyResult<CellBytes> {
-        self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
-    }
-
+impl CellDataChangeset for ChecklistTypeOptionPB {
     fn apply_changeset(
         &self,
         changeset: AnyCellChangeset<SelectOptionCellChangeset>,
@@ -64,7 +74,9 @@ impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for Checklist
         match cell_rev {
             None => Ok(SelectOptionIds::from(insert_option_ids).to_string()),
             Some(cell_rev) => {
-                let cell_data = get_cell_data(&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();
                 for insert_option_id in insert_option_ids {
                     if !select_ids.contains(&insert_option_id) {

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

@@ -1,11 +1,10 @@
-use crate::entities::FieldType;
+use crate::entities::{FieldType, SelectOptionFilterPB};
 use crate::impl_type_option;
-use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
+use crate::services::cell::{AnyCellChangeset, CellDataChangeset, FromCellString, TypeCellData};
 use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer;
-use crate::services::field::type_options::util::get_cell_data;
 use crate::services::field::{
-    BoxTypeOptionBuilder, SelectOptionCellChangeset, SelectOptionIds, SelectOptionPB, SelectTypeOptionSharedAction,
-    TypeOptionBuilder,
+    BoxTypeOptionBuilder, SelectOptionCellChangeset, SelectOptionCellDataPB, SelectOptionIds, SelectOptionPB,
+    SelectTypeOptionSharedAction, TypeOption, TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration,
 };
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
@@ -24,6 +23,26 @@ pub struct MultiSelectTypeOptionPB {
 }
 impl_type_option!(MultiSelectTypeOptionPB, FieldType::MultiSelect);
 
+impl TypeOption for MultiSelectTypeOptionPB {
+    type CellData = SelectOptionIds;
+    type CellChangeset = SelectOptionCellChangeset;
+    type CellPBType = SelectOptionCellDataPB;
+}
+
+impl TypeOptionConfiguration for MultiSelectTypeOptionPB {
+    type CellFilterConfiguration = SelectOptionFilterPB;
+}
+
+impl TypeOptionCellData for MultiSelectTypeOptionPB {
+    fn convert_into_pb_type(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellPBType {
+        self.get_selected_options(cell_data)
+    }
+
+    fn decode_type_option_cell_data(&self, cell_data: String) -> FlowyResult<<Self as TypeOption>::CellData> {
+        SelectOptionIds::from_cell_str(&cell_data)
+    }
+}
+
 impl SelectTypeOptionSharedAction for MultiSelectTypeOptionPB {
     fn number_of_max_options(&self) -> Option<usize> {
         None
@@ -38,16 +57,7 @@ impl SelectTypeOptionSharedAction for MultiSelectTypeOptionPB {
     }
 }
 
-impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for MultiSelectTypeOptionPB {
-    fn decode_cell_data(
-        &self,
-        cell_data: IntoCellData<SelectOptionIds>,
-        decoded_field_type: &FieldType,
-        field_rev: &FieldRevision,
-    ) -> FlowyResult<CellBytes> {
-        self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
-    }
-
+impl CellDataChangeset for MultiSelectTypeOptionPB {
     fn apply_changeset(
         &self,
         changeset: AnyCellChangeset<SelectOptionCellChangeset>,
@@ -67,7 +77,9 @@ impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for MultiSele
                 new_cell_data = SelectOptionIds::from(insert_option_ids).to_string();
             }
             Some(cell_rev) => {
-                let cell_data = get_cell_data(&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();
                 for insert_option_id in insert_option_ids {
                     if !select_ids.contains(&insert_option_id) {
@@ -115,7 +127,7 @@ impl TypeOptionBuilder for MultiSelectTypeOptionBuilder {
 #[cfg(test)]
 mod tests {
     use crate::entities::FieldType;
-    use crate::services::cell::CellDataOperation;
+    use crate::services::cell::CellDataChangeset;
     use crate::services::field::type_options::selection_type_option::*;
     use crate::services::field::{CheckboxTypeOptionBuilder, FieldBuilder, TypeOptionBuilder};
     use crate::services::field::{MultiSelectTypeOptionBuilder, MultiSelectTypeOptionPB};

+ 12 - 8
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_filter.rs

@@ -2,7 +2,9 @@
 
 use crate::entities::{ChecklistFilterPB, FieldType, SelectOptionConditionPB, SelectOptionFilterPB};
 use crate::services::cell::{CellFilterable, TypeCellData};
-use crate::services::field::{ChecklistTypeOptionPB, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB};
+use crate::services::field::{
+    ChecklistTypeOptionPB, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB, TypeOptionCellData,
+};
 use crate::services::field::{SelectTypeOptionSharedAction, SelectedSelectOptions};
 use flowy_error::FlowyResult;
 
@@ -78,33 +80,35 @@ impl SelectOptionFilterPB {
     }
 }
 
-impl CellFilterable<SelectOptionFilterPB> for MultiSelectTypeOptionPB {
+impl CellFilterable for MultiSelectTypeOptionPB {
     fn apply_filter(&self, type_cell_data: TypeCellData, filter: &SelectOptionFilterPB) -> FlowyResult<bool> {
         if !type_cell_data.is_multi_select() {
             return Ok(true);
         }
-
-        let selected_options = SelectedSelectOptions::from(self.get_selected_options(type_cell_data.into()));
+        let ids = self.decode_type_option_cell_data(type_cell_data.data)?;
+        let selected_options = SelectedSelectOptions::from(self.get_selected_options(ids));
         Ok(filter.is_visible(&selected_options, FieldType::MultiSelect))
     }
 }
 
-impl CellFilterable<SelectOptionFilterPB> for SingleSelectTypeOptionPB {
+impl CellFilterable for SingleSelectTypeOptionPB {
     fn apply_filter(&self, type_cell_data: TypeCellData, filter: &SelectOptionFilterPB) -> FlowyResult<bool> {
         if !type_cell_data.is_single_select() {
             return Ok(true);
         }
-        let selected_options = SelectedSelectOptions::from(self.get_selected_options(type_cell_data.into()));
+        let ids = self.decode_type_option_cell_data(type_cell_data.data)?;
+        let selected_options = SelectedSelectOptions::from(self.get_selected_options(ids));
         Ok(filter.is_visible(&selected_options, FieldType::SingleSelect))
     }
 }
 
-impl CellFilterable<ChecklistFilterPB> for ChecklistTypeOptionPB {
+impl CellFilterable for ChecklistTypeOptionPB {
     fn apply_filter(&self, type_cell_data: TypeCellData, filter: &ChecklistFilterPB) -> FlowyResult<bool> {
         if !type_cell_data.is_checklist() {
             return Ok(true);
         }
-        let selected_options = SelectedSelectOptions::from(self.get_selected_options(type_cell_data.into()));
+        let ids = self.decode_type_option_cell_data(type_cell_data.data)?;
+        let selected_options = SelectedSelectOptions::from(self.get_selected_options(ids));
         Ok(filter.is_visible(&self.options, &selected_options))
     }
 }

+ 31 - 67
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_type_option.rs

@@ -1,10 +1,13 @@
 use crate::entities::parser::NotEmptyStr;
 use crate::entities::{CellChangesetPB, CellPathPB, CellPathParams, FieldType};
 use crate::services::cell::{
-    CellBytes, CellBytesParser, CellDataIsEmpty, CellDataSerialize, FromCellChangeset, FromCellString, IntoCellData,
+    CellDataDecoder, CellProtobufBlobParser, DecodedCellData, FromCellChangeset, FromCellString,
 };
+
 use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer;
-use crate::services::field::{ChecklistTypeOptionPB, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB};
+use crate::services::field::{
+    ChecklistTypeOptionPB, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB, TypeOption, TypeOptionCellData,
+};
 use bytes::Bytes;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::{internal_error, ErrorCode, FlowyResult};
@@ -69,17 +72,10 @@ impl std::default::Default for SelectOptionColorPB {
     }
 }
 
-pub fn make_selected_options(
-    cell_data: IntoCellData<SelectOptionIds>,
-    options: &[SelectOptionPB],
-) -> Vec<SelectOptionPB> {
-    if let Ok(ids) = cell_data.try_into_inner() {
-        ids.iter()
-            .flat_map(|option_id| options.iter().find(|option| &option.id == option_id).cloned())
-            .collect()
-    } else {
-        vec![]
-    }
+pub fn make_selected_options(ids: SelectOptionIds, options: &[SelectOptionPB]) -> Vec<SelectOptionPB> {
+    ids.iter()
+        .flat_map(|option_id| options.iter().find(|option| &option.id == option_id).cloned())
+        .collect()
 }
 /// Defines the shared actions used by SingleSelect or Multi-Select.
 pub trait SelectTypeOptionSharedAction: TypeOptionDataSerializer + Send + Sync {
@@ -113,8 +109,8 @@ pub trait SelectTypeOptionSharedAction: TypeOptionDataSerializer + Send + Sync {
     }
 
     /// Return a list of options that are selected by user
-    fn get_selected_options(&self, cell_data: IntoCellData<SelectOptionIds>) -> SelectOptionCellDataPB {
-        let mut select_options = make_selected_options(cell_data, self.options());
+    fn get_selected_options(&self, ids: SelectOptionIds) -> SelectOptionCellDataPB {
+        let mut select_options = make_selected_options(ids, self.options());
         match self.number_of_max_options() {
             None => {}
             Some(number_of_max_options) => {
@@ -127,75 +123,39 @@ pub trait SelectTypeOptionSharedAction: TypeOptionDataSerializer + Send + Sync {
         }
     }
 
-    fn transform_cell_data(
-        &self,
-        cell_data: IntoCellData<SelectOptionIds>,
-        decoded_field_type: &FieldType,
-        _field_rev: &FieldRevision,
-    ) -> FlowyResult<CellBytes> {
-        match decoded_field_type {
-            FieldType::SingleSelect | FieldType::MultiSelect => {
-                // Do nothing
-            }
-            FieldType::Checkbox => {
-                // transform the cell data to the option id
-                let mut transformed_ids = Vec::new();
-                let options = self.options();
-                cell_data.0.iter().for_each(|ids| {
-                    ids.0.iter().for_each(|name| {
-                        let id = options
-                            .iter()
-                            .find(|option| option.name == name.clone())
-                            .unwrap()
-                            .id
-                            .clone();
-                        transformed_ids.push(id);
-                    })
-                });
-
-                return CellBytes::from(
-                    self.get_selected_options(IntoCellData(Some(SelectOptionIds(transformed_ids)))),
-                );
-            }
-            _ => {
-                return Ok(CellBytes::default());
-            }
-        }
-
-        CellBytes::from(self.get_selected_options(cell_data))
-    }
-
     fn options(&self) -> &Vec<SelectOptionPB>;
 
     fn mut_options(&mut self) -> &mut Vec<SelectOptionPB>;
 }
 
-impl<T> CellDataSerialize<SelectOptionIds> for T
+impl<T> CellDataDecoder for T
 where
-    T: SelectTypeOptionSharedAction,
+    T: SelectTypeOptionSharedAction + TypeOption<CellData = SelectOptionIds> + TypeOptionCellData,
 {
-    fn serialize_cell_data_to_bytes(
+    fn try_decode_cell_data(
         &self,
-        cell_data: IntoCellData<SelectOptionIds>,
+        cell_data: String,
         decoded_field_type: &FieldType,
         field_rev: &FieldRevision,
-    ) -> FlowyResult<CellBytes> {
-        SelectOptionTypeOptionTransformer::transform_type_option_cell_data(
+    ) -> FlowyResult<<Self as TypeOption>::CellData> {
+        let cell_data = self.decode_type_option_cell_data(cell_data)?;
+        Ok(SelectOptionTypeOptionTransformer::transform_type_option_cell_data(
             self,
             cell_data,
             decoded_field_type,
             field_rev,
-        )
+        ))
     }
 
-    fn serialize_cell_data_to_str(
+    fn decode_cell_data_to_str(
         &self,
-        cell_data: IntoCellData<SelectOptionIds>,
+        cell_data: String,
         _decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<String> {
+        let ids = self.decode_type_option_cell_data(cell_data)?;
         Ok(self
-            .get_selected_options(cell_data)
+            .get_selected_options(ids)
             .select_options
             .into_iter()
             .map(|option| option.name)
@@ -333,14 +293,16 @@ impl std::ops::DerefMut for SelectOptionIds {
     }
 }
 
-impl CellDataIsEmpty for SelectOptionIds {
+impl DecodedCellData for SelectOptionIds {
+    type Object = SelectOptionIds;
+
     fn is_empty(&self) -> bool {
         self.0.is_empty()
     }
 }
 
 pub struct SelectOptionIdsParser();
-impl CellBytesParser for SelectOptionIdsParser {
+impl CellProtobufBlobParser for SelectOptionIdsParser {
     type Object = SelectOptionIds;
     fn parser(bytes: &Bytes) -> FlowyResult<Self::Object> {
         match String::from_utf8(bytes.to_vec()) {
@@ -350,14 +312,16 @@ impl CellBytesParser for SelectOptionIdsParser {
     }
 }
 
-impl CellDataIsEmpty for SelectOptionCellDataPB {
+impl DecodedCellData for SelectOptionCellDataPB {
+    type Object = SelectOptionCellDataPB;
+
     fn is_empty(&self) -> bool {
         self.select_options.is_empty()
     }
 }
 
 pub struct SelectOptionCellDataParser();
-impl CellBytesParser for SelectOptionCellDataParser {
+impl CellProtobufBlobParser for SelectOptionCellDataParser {
     type Object = SelectOptionCellDataPB;
 
     fn parser(bytes: &Bytes) -> FlowyResult<Self::Object> {

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

@@ -1,8 +1,11 @@
-use crate::entities::FieldType;
+use crate::entities::{FieldType, SelectOptionFilterPB};
 use crate::impl_type_option;
-use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
+use crate::services::cell::{AnyCellChangeset, CellDataChangeset, FromCellString};
 use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer;
-use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
+use crate::services::field::{
+    BoxTypeOptionBuilder, SelectOptionCellDataPB, TypeOption, TypeOptionBuilder, TypeOptionCellData,
+    TypeOptionConfiguration,
+};
 use crate::services::field::{
     SelectOptionCellChangeset, SelectOptionIds, SelectOptionPB, SelectTypeOptionSharedAction,
 };
@@ -23,6 +26,26 @@ pub struct SingleSelectTypeOptionPB {
 }
 impl_type_option!(SingleSelectTypeOptionPB, FieldType::SingleSelect);
 
+impl TypeOption for SingleSelectTypeOptionPB {
+    type CellData = SelectOptionIds;
+    type CellChangeset = SelectOptionCellChangeset;
+    type CellPBType = SelectOptionCellDataPB;
+}
+
+impl TypeOptionConfiguration for SingleSelectTypeOptionPB {
+    type CellFilterConfiguration = SelectOptionFilterPB;
+}
+
+impl TypeOptionCellData for SingleSelectTypeOptionPB {
+    fn convert_into_pb_type(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellPBType {
+        self.get_selected_options(cell_data)
+    }
+
+    fn decode_type_option_cell_data(&self, cell_data: String) -> FlowyResult<<Self as TypeOption>::CellData> {
+        SelectOptionIds::from_cell_str(&cell_data)
+    }
+}
+
 impl SelectTypeOptionSharedAction for SingleSelectTypeOptionPB {
     fn number_of_max_options(&self) -> Option<usize> {
         Some(1)
@@ -37,16 +60,7 @@ impl SelectTypeOptionSharedAction for SingleSelectTypeOptionPB {
     }
 }
 
-impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for SingleSelectTypeOptionPB {
-    fn decode_cell_data(
-        &self,
-        cell_data: IntoCellData<SelectOptionIds>,
-        decoded_field_type: &FieldType,
-        field_rev: &FieldRevision,
-    ) -> FlowyResult<CellBytes> {
-        self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
-    }
-
+impl CellDataChangeset for SingleSelectTypeOptionPB {
     fn apply_changeset(
         &self,
         changeset: AnyCellChangeset<SelectOptionCellChangeset>,
@@ -102,7 +116,7 @@ impl TypeOptionBuilder for SingleSelectTypeOptionBuilder {
 #[cfg(test)]
 mod tests {
     use crate::entities::FieldType;
-    use crate::services::cell::CellDataOperation;
+    use crate::services::cell::CellDataChangeset;
     use crate::services::field::type_options::*;
     use crate::services::field::{FieldBuilder, TypeOptionBuilder};
 

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

@@ -1,10 +1,10 @@
 use crate::entities::FieldType;
-use crate::services::cell::{CellBytes, IntoCellData};
+
 use crate::services::field::{
     MultiSelectTypeOptionPB, SelectOptionColorPB, SelectOptionIds, SelectOptionPB, SelectTypeOptionSharedAction,
-    SingleSelectTypeOptionPB, CHECK, UNCHECK,
+    SingleSelectTypeOptionPB, TypeOption, CHECK, UNCHECK,
 };
-use flowy_error::FlowyResult;
+
 use grid_rev_model::FieldRevision;
 use serde_json;
 
@@ -57,31 +57,28 @@ impl SelectOptionTypeOptionTransformer {
 
     pub fn transform_type_option_cell_data<T>(
         shared: &T,
-        cell_data: IntoCellData<SelectOptionIds>,
+        cell_data: <T as TypeOption>::CellData,
         decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
-    ) -> FlowyResult<CellBytes>
+    ) -> <T as TypeOption>::CellData
     where
-        T: SelectTypeOptionSharedAction,
+        T: SelectTypeOptionSharedAction + TypeOption<CellData = SelectOptionIds>,
     {
         match decoded_field_type {
-            FieldType::SingleSelect | FieldType::MultiSelect | FieldType::Checklist => {
-                //
-                CellBytes::from(shared.get_selected_options(cell_data))
-            }
+            FieldType::SingleSelect | FieldType::MultiSelect | FieldType::Checklist => cell_data,
             FieldType::Checkbox => {
                 // transform the cell data to the option id
                 let mut transformed_ids = Vec::new();
                 let options = shared.options();
-                cell_data.try_into_inner()?.iter().for_each(|name| {
+                cell_data.iter().for_each(|name| {
                     if let Some(option) = options.iter().find(|option| &option.name == name) {
                         transformed_ids.push(option.id.clone());
                     }
                 });
-                let transformed_cell_data = IntoCellData::from(SelectOptionIds::from(transformed_ids));
-                CellBytes::from(shared.get_selected_options(transformed_cell_data))
+
+                SelectOptionIds::from(transformed_ids)
             }
-            _ => Ok(CellBytes::default()),
+            _ => SelectOptionIds::from(vec![]),
         }
     }
 }

+ 10 - 6
frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_filter.rs

@@ -1,6 +1,6 @@
 use crate::entities::{TextFilterConditionPB, TextFilterPB};
-use crate::services::cell::{CellFilterable, IntoCellData, TypeCellData};
-use crate::services::field::{RichTextTypeOptionPB, TextCellData};
+use crate::services::cell::{CellFilterable, TypeCellData};
+use crate::services::field::{RichTextTypeOptionPB, TypeOptionCellData, TypeOptionConfiguration};
 use flowy_error::FlowyResult;
 
 impl TextFilterPB {
@@ -20,17 +20,21 @@ impl TextFilterPB {
     }
 }
 
-impl CellFilterable<TextFilterPB> for RichTextTypeOptionPB {
-    fn apply_filter(&self, type_cell_data: TypeCellData, filter: &TextFilterPB) -> FlowyResult<bool> {
+impl CellFilterable for RichTextTypeOptionPB {
+    fn apply_filter(
+        &self,
+        type_cell_data: TypeCellData,
+        filter: &<Self as TypeOptionConfiguration>::CellFilterConfiguration,
+    ) -> FlowyResult<bool> {
         if !type_cell_data.is_text() {
             return Ok(false);
         }
 
-        let cell_data: IntoCellData<TextCellData> = type_cell_data.into();
-        let text_cell_data = cell_data.try_into_inner()?;
+        let text_cell_data = self.decode_type_option_cell_data(type_cell_data.data)?;
         Ok(filter.is_visible(text_cell_data))
     }
 }
+
 #[cfg(test)]
 mod tests {
     #![allow(clippy::all)]

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

@@ -1,8 +1,9 @@
 #[cfg(test)]
 mod tests {
     use crate::entities::FieldType;
-    use crate::services::cell::CellDataOperation;
+    use crate::services::cell::CellDataDecoder;
     use crate::services::field::FieldBuilder;
+
     use crate::services::field::*;
 
     // Test parser the cell data which field's type is FieldType::Date to cell data
@@ -15,11 +16,9 @@ mod tests {
 
         assert_eq!(
             type_option
-                .decode_cell_data(1647251762.into(), &field_type, &field_rev)
-                .unwrap()
-                .parser::<TextCellDataParser>()
+                .try_decode_cell_data(1647251762.to_string(), &field_type, &field_rev)
                 .unwrap()
-                .as_ref(),
+                .as_str(),
             "Mar 14,2022"
         );
     }
@@ -38,9 +37,7 @@ mod tests {
 
         assert_eq!(
             type_option
-                .decode_cell_data(option_id.into(), &field_type, &field_rev)
-                .unwrap()
-                .parser::<TextCellDataParser>()
+                .try_decode_cell_data(option_id, &field_type, &field_rev)
                 .unwrap()
                 .to_string(),
             done_option.name,

+ 111 - 31
frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_type_option.rs

@@ -1,15 +1,19 @@
-use crate::entities::FieldType;
+use crate::entities::{FieldType, TextFilterPB};
 use crate::impl_type_option;
 use crate::services::cell::{
-    decode_cell_data_to_string, AnyCellChangeset, CellBytes, CellBytesParser, CellDataIsEmpty, CellDataOperation,
-    CellDataSerialize, FromCellString, IntoCellData,
+    stringify_cell_data, AnyCellChangeset, CellComparable, CellDataChangeset, CellDataDecoder, CellProtobufBlobParser,
+    DecodedCellData, FromCellString,
+};
+use crate::services::field::{
+    BoxTypeOptionBuilder, TypeOption, TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration,
 };
-use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
 use flowy_error::{FlowyError, FlowyResult};
 use grid_rev_model::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer};
+use protobuf::ProtobufError;
 use serde::{Deserialize, Serialize};
+use std::cmp::Ordering;
 
 #[derive(Default)]
 pub struct RichTextTypeOptionBuilder(RichTextTypeOptionPB);
@@ -39,48 +43,57 @@ pub struct RichTextTypeOptionPB {
 }
 impl_type_option!(RichTextTypeOptionPB, FieldType::RichText);
 
-impl CellDataSerialize<RichTextCellData> for RichTextTypeOptionPB {
-    fn serialize_cell_data_to_bytes(
-        &self,
-        cell_data: IntoCellData<RichTextCellData>,
-        _decoded_field_type: &FieldType,
-        _field_rev: &FieldRevision,
-    ) -> FlowyResult<CellBytes> {
-        let cell_str: RichTextCellData = cell_data.try_into_inner()?;
-        Ok(CellBytes::new(cell_str))
+impl TypeOption for RichTextTypeOptionPB {
+    type CellData = StrCellData;
+    type CellChangeset = String;
+    type CellPBType = StrCellData;
+}
+
+impl TypeOptionConfiguration for RichTextTypeOptionPB {
+    type CellFilterConfiguration = TextFilterPB;
+}
+
+impl TypeOptionCellData for RichTextTypeOptionPB {
+    fn convert_into_pb_type(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellPBType {
+        cell_data
     }
 
-    fn serialize_cell_data_to_str(
-        &self,
-        cell_data: IntoCellData<RichTextCellData>,
-        _decoded_field_type: &FieldType,
-        _field_rev: &FieldRevision,
-    ) -> FlowyResult<String> {
-        let cell_str: RichTextCellData = cell_data.try_into_inner()?;
-        Ok(cell_str)
+    fn decode_type_option_cell_data(&self, cell_data: String) -> FlowyResult<<Self as TypeOption>::CellData> {
+        StrCellData::from_cell_str(&cell_data)
     }
 }
 
-impl CellDataOperation<RichTextCellData, String> for RichTextTypeOptionPB {
-    fn decode_cell_data(
+impl CellDataDecoder for RichTextTypeOptionPB {
+    fn try_decode_cell_data(
         &self,
-        cell_data: IntoCellData<RichTextCellData>,
+        cell_data: String,
         decoded_field_type: &FieldType,
         field_rev: &FieldRevision,
-    ) -> FlowyResult<CellBytes> {
+    ) -> FlowyResult<<Self as TypeOption>::CellData> {
         if decoded_field_type.is_date()
             || decoded_field_type.is_single_select()
             || decoded_field_type.is_multi_select()
             || decoded_field_type.is_number()
             || decoded_field_type.is_url()
         {
-            let s = decode_cell_data_to_string(cell_data, decoded_field_type, decoded_field_type, field_rev);
-            Ok(CellBytes::new(s.unwrap_or_else(|_| "".to_owned())))
+            Ok(stringify_cell_data(cell_data, decoded_field_type, field_rev).into())
         } else {
-            self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
+            StrCellData::from_cell_str(&cell_data)
         }
     }
 
+    fn decode_cell_data_to_str(
+        &self,
+        cell_data: String,
+        _decoded_field_type: &FieldType,
+        _field_rev: &FieldRevision,
+    ) -> FlowyResult<String> {
+        let cell_str = StrCellData::from_cell_str(&cell_data)?;
+        Ok(cell_str.into())
+    }
+}
+
+impl CellDataChangeset for RichTextTypeOptionPB {
     fn apply_changeset(
         &self,
         changeset: AnyCellChangeset<String>,
@@ -95,6 +108,14 @@ impl CellDataOperation<RichTextCellData, String> for RichTextTypeOptionPB {
     }
 }
 
+impl CellComparable for RichTextTypeOptionPB {
+    type CellData = String;
+
+    fn apply_cmp(&self, cell_data: &Self::CellData, other_cell_data: &Self::CellData) -> Ordering {
+        cell_data.cmp(other_cell_data)
+    }
+}
+
 pub struct TextCellData(pub String);
 impl AsRef<str> for TextCellData {
     fn as_ref(&self) -> &str {
@@ -125,14 +146,16 @@ impl ToString for TextCellData {
     }
 }
 
-impl CellDataIsEmpty for TextCellData {
+impl DecodedCellData for TextCellData {
+    type Object = TextCellData;
+
     fn is_empty(&self) -> bool {
         self.0.is_empty()
     }
 }
 
 pub struct TextCellDataParser();
-impl CellBytesParser for TextCellDataParser {
+impl CellProtobufBlobParser for TextCellDataParser {
     type Object = TextCellData;
     fn parser(bytes: &Bytes) -> FlowyResult<Self::Object> {
         match String::from_utf8(bytes.to_vec()) {
@@ -142,4 +165,61 @@ impl CellBytesParser for TextCellDataParser {
     }
 }
 
-pub type RichTextCellData = String;
+#[derive(Default, Debug)]
+pub struct StrCellData(pub String);
+impl std::ops::Deref for StrCellData {
+    type Target = String;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl std::ops::DerefMut for StrCellData {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+
+impl FromCellString for StrCellData {
+    fn from_cell_str(s: &str) -> FlowyResult<Self> {
+        Ok(Self(s.to_owned()))
+    }
+}
+
+impl std::convert::From<String> for StrCellData {
+    fn from(s: String) -> Self {
+        Self(s)
+    }
+}
+
+impl std::convert::From<StrCellData> for String {
+    fn from(value: StrCellData) -> Self {
+        value.0
+    }
+}
+
+impl std::convert::From<&str> for StrCellData {
+    fn from(s: &str) -> Self {
+        Self(s.to_owned())
+    }
+}
+
+impl std::convert::TryFrom<StrCellData> for Bytes {
+    type Error = ProtobufError;
+
+    fn try_from(value: StrCellData) -> Result<Self, Self::Error> {
+        Ok(Bytes::from(value.0))
+    }
+}
+
+impl AsRef<[u8]> for StrCellData {
+    fn as_ref(&self) -> &[u8] {
+        self.0.as_ref()
+    }
+}
+impl AsRef<str> for StrCellData {
+    fn as_ref(&self) -> &str {
+        self.0.as_str()
+    }
+}

+ 119 - 0
frontend/rust-lib/flowy-grid/src/services/field/type_options/type_option.rs

@@ -0,0 +1,119 @@
+use crate::entities::FieldType;
+use crate::services::cell::{CellDataChangeset, CellDataDecoder, CellProtobufBlob, FromCellString};
+use crate::services::field::{
+    CheckboxTypeOptionPB, ChecklistTypeOptionPB, DateTypeOptionPB, MultiSelectTypeOptionPB, NumberTypeOptionPB,
+    RichTextTypeOptionPB, SingleSelectTypeOptionPB, URLTypeOptionPB,
+};
+use bytes::Bytes;
+use flowy_error::FlowyResult;
+use grid_rev_model::FieldRevision;
+use protobuf::ProtobufError;
+use std::fmt::Debug;
+
+pub trait TypeOption {
+    type CellData: FromCellString + Default;
+    type CellChangeset;
+    type CellPBType: TryInto<Bytes, Error = ProtobufError> + Debug;
+}
+
+pub trait TypeOptionConfiguration {
+    type CellFilterConfiguration;
+}
+
+pub trait TypeOptionCellDataHandler {
+    fn handle_cell_data(
+        &self,
+        cell_data: String,
+        decoded_field_type: &FieldType,
+        field_rev: &FieldRevision,
+    ) -> FlowyResult<CellProtobufBlob>;
+
+    fn stringify_cell_data(&self, cell_data: String, field_type: &FieldType, field_rev: &FieldRevision) -> String;
+}
+
+pub trait TypeOptionCellData: TypeOption {
+    ///
+    /// Convert the decoded cell data into corresponding `Protobuf struct`.
+    /// For example:
+    ///    FieldType::URL => URLCellDataPB
+    ///    FieldType::Date=> DateCellDataPB
+    fn convert_into_pb_type(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellPBType;
+
+    /// Decodes the opaque cell data to corresponding data struct.
+    // For example, the cell data is timestamp if its field type is `FieldType::Date`. This cell
+    // data can not directly show to user. So it needs to be encode as the date string with custom
+    // format setting. Encode `1647251762` to `"Mar 14,2022`
+    fn decode_type_option_cell_data(&self, cell_data: String) -> FlowyResult<<Self as TypeOption>::CellData>;
+}
+
+impl<T> TypeOptionCellDataHandler for T
+where
+    T: TypeOption + CellDataDecoder + CellDataChangeset + TypeOptionCellData,
+{
+    fn handle_cell_data(
+        &self,
+        cell_data: String,
+        field_type: &FieldType,
+        field_rev: &FieldRevision,
+    ) -> FlowyResult<CellProtobufBlob> {
+        let cell_data = self.try_decode_cell_data(cell_data, field_type, field_rev)?;
+        CellProtobufBlob::from(self.convert_into_pb_type(cell_data))
+    }
+
+    fn stringify_cell_data(
+        &self,
+        cell_data: String,
+        decoded_field_type: &FieldType,
+        field_rev: &FieldRevision,
+    ) -> String {
+        self.decode_cell_data_to_str(cell_data, decoded_field_type, field_rev)
+            .unwrap_or_default()
+    }
+}
+
+pub struct FieldRevisionExt<'a> {
+    field_rev: &'a FieldRevision,
+}
+
+impl<'a> FieldRevisionExt<'a> {
+    pub fn new(field_rev: &'a FieldRevision) -> Self {
+        Self { field_rev }
+    }
+
+    pub fn get_type_option_handler(&self, field_type: &FieldType) -> Option<Box<dyn TypeOptionCellDataHandler>> {
+        match field_type {
+            FieldType::RichText => self
+                .field_rev
+                .get_type_option::<RichTextTypeOptionPB>(field_type.into())
+                .map(|type_option| Box::new(type_option) as Box<dyn TypeOptionCellDataHandler>),
+            FieldType::Number => self
+                .field_rev
+                .get_type_option::<NumberTypeOptionPB>(field_type.into())
+                .map(|type_option| Box::new(type_option) as Box<dyn TypeOptionCellDataHandler>),
+            FieldType::DateTime => self
+                .field_rev
+                .get_type_option::<DateTypeOptionPB>(field_type.into())
+                .map(|type_option| Box::new(type_option) as Box<dyn TypeOptionCellDataHandler>),
+            FieldType::SingleSelect => self
+                .field_rev
+                .get_type_option::<SingleSelectTypeOptionPB>(field_type.into())
+                .map(|type_option| Box::new(type_option) as Box<dyn TypeOptionCellDataHandler>),
+            FieldType::MultiSelect => self
+                .field_rev
+                .get_type_option::<MultiSelectTypeOptionPB>(field_type.into())
+                .map(|type_option| Box::new(type_option) as Box<dyn TypeOptionCellDataHandler>),
+            FieldType::Checkbox => self
+                .field_rev
+                .get_type_option::<CheckboxTypeOptionPB>(field_type.into())
+                .map(|type_option| Box::new(type_option) as Box<dyn TypeOptionCellDataHandler>),
+            FieldType::URL => self
+                .field_rev
+                .get_type_option::<URLTypeOptionPB>(field_type.into())
+                .map(|type_option| Box::new(type_option) as Box<dyn TypeOptionCellDataHandler>),
+            FieldType::Checklist => self
+                .field_rev
+                .get_type_option::<ChecklistTypeOptionPB>(field_type.into())
+                .map(|type_option| Box::new(type_option) as Box<dyn TypeOptionCellDataHandler>),
+        }
+    }
+}

+ 10 - 8
frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_filter.rs

@@ -1,16 +1,18 @@
-use crate::entities::TextFilterPB;
-use crate::services::cell::{CellFilterable, IntoCellData, TypeCellData};
-use crate::services::field::{TextCellData, URLTypeOptionPB};
+use crate::services::cell::{CellFilterable, TypeCellData};
+use crate::services::field::{TypeOptionCellData, TypeOptionConfiguration, URLTypeOptionPB};
 use flowy_error::FlowyResult;
 
-impl CellFilterable<TextFilterPB> for URLTypeOptionPB {
-    fn apply_filter(&self, type_cell_data: TypeCellData, filter: &TextFilterPB) -> FlowyResult<bool> {
+impl CellFilterable for URLTypeOptionPB {
+    fn apply_filter(
+        &self,
+        type_cell_data: TypeCellData,
+        filter: &<Self as TypeOptionConfiguration>::CellFilterConfiguration,
+    ) -> FlowyResult<bool> {
         if !type_cell_data.is_url() {
             return Ok(true);
         }
 
-        let cell_data: IntoCellData<TextCellData> = type_cell_data.into();
-        let text_cell_data = cell_data.try_into_inner()?;
-        Ok(filter.is_visible(&text_cell_data))
+        let url_cell_data = self.decode_type_option_cell_data(type_cell_data.data)?;
+        Ok(filter.is_visible(&url_cell_data))
     }
 }

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

@@ -1,8 +1,9 @@
 #[cfg(test)]
 mod tests {
     use crate::entities::FieldType;
-    use crate::services::cell::{CellDataOperation, IntoCellData};
-    use crate::services::field::{FieldBuilder, URLCellDataParser};
+    use crate::services::cell::{CellDataChangeset, CellDataDecoder};
+
+    use crate::services::field::FieldBuilder;
     use crate::services::field::{URLCellData, URLTypeOptionPB};
     use grid_rev_model::FieldRevision;
 
@@ -175,16 +176,14 @@ mod tests {
         assert_eq!(expected_url.to_owned(), decode_cell_data.url);
     }
 
-    fn decode_cell_data<T: Into<IntoCellData<URLCellData>>>(
-        encoded_data: T,
+    fn decode_cell_data(
+        encoded_data: String,
         type_option: &URLTypeOptionPB,
         field_rev: &FieldRevision,
         field_type: &FieldType,
     ) -> URLCellData {
         type_option
-            .decode_cell_data(encoded_data.into(), field_type, field_rev)
-            .unwrap()
-            .parser::<URLCellDataParser>()
+            .try_decode_cell_data(encoded_data, field_type, field_rev)
             .unwrap()
     }
 }

+ 41 - 28
frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option.rs

@@ -1,7 +1,10 @@
-use crate::entities::FieldType;
+use crate::entities::{FieldType, TextFilterPB};
 use crate::impl_type_option;
-use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
-use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder, URLCellData, URLCellDataPB};
+use crate::services::cell::{AnyCellChangeset, CellDataChangeset, CellDataDecoder, FromCellString};
+use crate::services::field::{
+    BoxTypeOptionBuilder, TypeOption, TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration, URLCellData,
+    URLCellDataPB,
+};
 use bytes::Bytes;
 use fancy_regex::Regex;
 use flowy_derive::ProtoBuf;
@@ -36,47 +39,57 @@ pub struct URLTypeOptionPB {
 }
 impl_type_option!(URLTypeOptionPB, FieldType::URL);
 
-impl CellDataSerialize<URLCellData> for URLTypeOptionPB {
-    fn serialize_cell_data_to_bytes(
+impl TypeOption for URLTypeOptionPB {
+    type CellData = URLCellData;
+    type CellChangeset = URLCellChangeset;
+    type CellPBType = URLCellDataPB;
+}
+
+impl TypeOptionConfiguration for URLTypeOptionPB {
+    type CellFilterConfiguration = TextFilterPB;
+}
+
+impl TypeOptionCellData for URLTypeOptionPB {
+    fn convert_into_pb_type(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellPBType {
+        cell_data.into()
+    }
+
+    fn decode_type_option_cell_data(&self, cell_data: String) -> FlowyResult<<Self as TypeOption>::CellData> {
+        URLCellData::from_cell_str(&cell_data)
+    }
+}
+
+impl CellDataDecoder for URLTypeOptionPB {
+    fn try_decode_cell_data(
         &self,
-        cell_data: IntoCellData<URLCellData>,
-        _decoded_field_type: &FieldType,
+        cell_data: String,
+        decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
-    ) -> FlowyResult<CellBytes> {
-        let cell_data_pb: URLCellDataPB = cell_data.try_into_inner()?.into();
-        CellBytes::from(cell_data_pb)
+    ) -> FlowyResult<<Self as TypeOption>::CellData> {
+        if !decoded_field_type.is_url() {
+            return Ok(Default::default());
+        }
+
+        self.decode_type_option_cell_data(cell_data)
     }
 
-    fn serialize_cell_data_to_str(
+    fn decode_cell_data_to_str(
         &self,
-        cell_data: IntoCellData<URLCellData>,
+        cell_data: String,
         _decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<String> {
-        let cell_data: URLCellData = cell_data.try_into_inner()?;
+        let cell_data = self.decode_type_option_cell_data(cell_data)?;
         Ok(cell_data.content)
     }
 }
 
 pub type URLCellChangeset = String;
 
-impl CellDataOperation<URLCellData, URLCellChangeset> for URLTypeOptionPB {
-    fn decode_cell_data(
-        &self,
-        cell_data: IntoCellData<URLCellData>,
-        decoded_field_type: &FieldType,
-        _field_rev: &FieldRevision,
-    ) -> FlowyResult<CellBytes> {
-        if !decoded_field_type.is_url() {
-            return Ok(CellBytes::default());
-        }
-        let cell_data = cell_data.try_into_inner()?.to_json()?;
-        Ok(CellBytes::new(cell_data))
-    }
-
+impl CellDataChangeset for URLTypeOptionPB {
     fn apply_changeset(
         &self,
-        changeset: AnyCellChangeset<String>,
+        changeset: AnyCellChangeset<URLCellChangeset>,
         _cell_rev: Option<CellRevision>,
     ) -> Result<String, FlowyError> {
         let content = changeset.try_into_inner()?;

+ 21 - 8
frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option_entities.rs

@@ -1,4 +1,4 @@
-use crate::services::cell::{CellBytesParser, CellDataIsEmpty, FromCellString};
+use crate::services::cell::{CellProtobufBlobParser, DecodedCellData, FromCellString};
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
 use flowy_error::{internal_error, FlowyResult};
@@ -22,6 +22,14 @@ impl From<URLCellData> for URLCellDataPB {
     }
 }
 
+impl DecodedCellData for URLCellDataPB {
+    type Object = URLCellDataPB;
+
+    fn is_empty(&self) -> bool {
+        self.content.is_empty()
+    }
+}
+
 #[derive(Clone, Default, Serialize, Deserialize)]
 pub struct URLCellData {
     pub url: String,
@@ -41,21 +49,26 @@ impl URLCellData {
     }
 }
 
-impl CellDataIsEmpty for URLCellData {
+impl AsRef<str> for URLCellData {
+    fn as_ref(&self) -> &str {
+        &self.url
+    }
+}
+
+impl DecodedCellData for URLCellData {
+    type Object = URLCellData;
+
     fn is_empty(&self) -> bool {
         self.content.is_empty()
     }
 }
 
 pub struct URLCellDataParser();
-impl CellBytesParser for URLCellDataParser {
-    type Object = URLCellData;
+impl CellProtobufBlobParser for URLCellDataParser {
+    type Object = URLCellDataPB;
 
     fn parser(bytes: &Bytes) -> FlowyResult<Self::Object> {
-        match String::from_utf8(bytes.to_vec()) {
-            Ok(s) => URLCellData::from_cell_str(&s),
-            Err(_) => Ok(URLCellData::default()),
-        }
+        URLCellDataPB::try_from(bytes.as_ref()).map_err(internal_error)
     }
 }
 

+ 0 - 10
frontend/rust-lib/flowy-grid/src/services/field/type_options/util/cell_data_util.rs

@@ -1,10 +0,0 @@
-use crate::services::cell::TypeCellData;
-use grid_rev_model::CellRevision;
-use std::str::FromStr;
-
-pub fn get_cell_data(cell_rev: &CellRevision) -> String {
-    match TypeCellData::from_str(&cell_rev.data) {
-        Ok(type_option) => type_option.data,
-        Err(_) => String::new(),
-    }
-}

+ 0 - 3
frontend/rust-lib/flowy-grid/src/services/field/type_options/util/mod.rs

@@ -1,3 +0,0 @@
-mod cell_data_util;
-
-pub use cell_data_util::*;

+ 3 - 3
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

@@ -3,7 +3,7 @@ use crate::entities::CellPathParams;
 use crate::entities::*;
 use crate::manager::GridUser;
 use crate::services::block_manager::GridBlockManager;
-use crate::services::cell::{apply_cell_data_changeset, decode_type_cell_data, CellBytes};
+use crate::services::cell::{apply_cell_data_changeset, decode_type_cell_data, CellProtobufBlob};
 use crate::services::field::{
     default_type_option_builder_from_type, type_option_builder_from_bytes, type_option_builder_from_json_str,
     FieldBuilder,
@@ -435,12 +435,12 @@ impl GridRevisionEditor {
         Some(CellPB::new(&params.field_id, field_type, cell_bytes.to_vec()))
     }
 
-    pub async fn get_cell_bytes(&self, params: &CellPathParams) -> Option<CellBytes> {
+    pub async fn get_cell_bytes(&self, params: &CellPathParams) -> Option<CellProtobufBlob> {
         let (_, cell_data) = self.decode_cell_data_from(params).await?;
         Some(cell_data)
     }
 
-    async fn decode_cell_data_from(&self, params: &CellPathParams) -> Option<(FieldType, CellBytes)> {
+    async fn decode_cell_data_from(&self, params: &CellPathParams) -> Option<(FieldType, CellProtobufBlob)> {
         let field_rev = self.get_field_rev(&params.field_id).await?;
         let (_, row_rev) = self.block_manager.get_row_rev(&params.row_id).await.ok()??;
         let cell_rev = row_rev.cells.get(&params.field_id)?.clone();

+ 2 - 2
frontend/rust-lib/flowy-grid/src/services/group/action.rs

@@ -1,5 +1,5 @@
 use crate::entities::{GroupRowsNotificationPB, GroupViewChangesetPB};
-use crate::services::cell::CellDataIsEmpty;
+use crate::services::cell::DecodedCellData;
 use crate::services::group::controller::MoveGroupRowContext;
 use crate::services::group::Group;
 use flowy_error::FlowyResult;
@@ -11,7 +11,7 @@ use std::sync::Arc;
 /// For example, the `CheckboxGroupController` implements this trait to provide custom behavior.
 ///
 pub trait GroupControllerCustomActions: Send + Sync {
-    type CellDataType: CellDataIsEmpty;
+    type CellDataType: DecodedCellData;
     /// Returns the a value of the cell, default value is None
     ///
     /// Determine which group the row is placed in based on the data of the cell. If the cell data

+ 3 - 3
frontend/rust-lib/flowy-grid/src/services/group/controller.rs

@@ -1,5 +1,5 @@
 use crate::entities::{GroupRowsNotificationPB, GroupViewChangesetPB, InsertedRowPB, RowPB};
-use crate::services::cell::{decode_type_cell_data, CellBytesParser, CellDataIsEmpty};
+use crate::services::cell::{decode_type_cell_data, CellProtobufBlobParser, DecodedCellData};
 use crate::services::group::action::{GroupControllerCustomActions, GroupControllerSharedActions};
 use crate::services::group::configuration::GroupContext;
 use crate::services::group::entities::Group;
@@ -55,7 +55,7 @@ pub struct MoveGroupRowContext<'a> {
 /// C: represents the group configuration that impl [GroupConfigurationSerde]
 /// T: the type-option data deserializer that impl [TypeOptionDataDeserializer]
 /// G: the group generator, [GroupGenerator]
-/// P: the parser that impl [CellBytesParser] for the CellBytes
+/// P: the parser that impl [CellProtobufBlobParser] for the CellBytes
 pub struct GenericGroupController<C, T, G, P> {
     pub field_id: String,
     pub type_option: Option<T>,
@@ -154,7 +154,7 @@ where
 
 impl<C, T, G, P> GroupControllerSharedActions for GenericGroupController<C, T, G, P>
 where
-    P: CellBytesParser,
+    P: CellProtobufBlobParser,
     C: GroupConfigurationContentSerde,
     T: TypeOptionDataDeserializer,
     G: GroupGenerator<Context = GroupContext<C>, TypeOptionType = T>,

+ 75 - 4
frontend/rust-lib/flowy-grid/src/services/sort/controller.rs

@@ -1,10 +1,17 @@
 #![allow(clippy::all)]
+
+use crate::entities::FieldType;
 #[allow(unused_attributes)]
-use crate::entities::{GridSortPB, SortChangesetNotificationPB};
+use crate::entities::SortChangesetNotificationPB;
+
 use crate::services::sort::{SortChangeset, SortType};
+
 use flowy_task::TaskDispatcher;
-use grid_rev_model::{FieldRevision, RowRevision, SortRevision};
+use grid_rev_model::{CellRevision, FieldRevision, RowRevision, SortCondition, SortRevision};
 use lib_infra::future::Fut;
+
+use std::cmp::Ordering;
+use std::collections::HashMap;
 use std::sync::Arc;
 use tokio::sync::RwLock;
 
@@ -23,7 +30,9 @@ pub struct SortController {
     delegate: Box<dyn SortDelegate>,
     task_scheduler: Arc<RwLock<TaskDispatcher>>,
     #[allow(dead_code)]
-    sorts: Vec<GridSortPB>,
+    sorts: Vec<SortRevision>,
+    #[allow(dead_code)]
+    row_orders: HashMap<String, usize>,
 }
 
 impl SortController {
@@ -37,6 +46,7 @@ impl SortController {
             delegate: Box::new(delegate),
             task_scheduler,
             sorts: vec![],
+            row_orders: HashMap::new(),
         }
     }
 
@@ -49,10 +59,71 @@ impl SortController {
     }
 
     pub fn sort_rows(&self, _rows: &mut Vec<Arc<RowRevision>>) {
-        //
+        // rows.par_sort_by(|left, right| cmp_row(left, right, &self.sorts));
     }
 
     pub async fn did_receive_changes(&mut self, _changeset: SortChangeset) -> Option<SortChangesetNotificationPB> {
         None
     }
 }
+
+#[allow(dead_code)]
+fn cmp_row(
+    left: &Arc<RowRevision>,
+    right: &Arc<RowRevision>,
+    sorts: &[SortRevision],
+    field_revs: &[Arc<FieldRevision>],
+) -> Ordering {
+    let mut order = Ordering::Equal;
+    for sort in sorts.iter() {
+        let cmp_order = match (left.cells.get(&sort.field_id), right.cells.get(&sort.field_id)) {
+            (Some(left_cell), Some(right_cell)) => {
+                let field_type: FieldType = sort.field_type.into();
+                match field_revs.iter().find(|field_rev| field_rev.id == sort.field_id) {
+                    None => Ordering::Equal,
+                    Some(field_rev) => cmp_cell(left_cell, right_cell, field_rev, field_type),
+                }
+            }
+            (Some(_), None) => Ordering::Greater,
+            (None, Some(_)) => Ordering::Less,
+            _ => Ordering::Equal,
+        };
+
+        if cmp_order.is_ne() {
+            // If the cmp_order is not Ordering::Equal, then break the loop.
+            order = match sort.condition {
+                SortCondition::Ascending => cmp_order,
+                SortCondition::Descending => cmp_order.reverse(),
+            };
+            break;
+        }
+    }
+    order
+}
+
+#[allow(dead_code)]
+fn cmp_cell(
+    _left: &CellRevision,
+    _right: &CellRevision,
+    _field_rev: &Arc<FieldRevision>,
+    field_type: FieldType,
+) -> Ordering {
+    let cal_order = || {
+        let order = match &field_type {
+            // FieldType::RichText => {
+            //     let left_cell = TypeCellData::try_from(left).ok()?.into();
+            //     let right_cell = TypeCellData::try_from(right).ok()?.into();
+            //     field_rev
+            //         .get_type_option::<RichTextTypeOptionPB>(field_rev.ty)?
+            //         .apply_cmp(&left_cell, &right_cell)
+            // }
+            // FieldType::Number => field_rev
+            //     .get_type_option::<NumberTypeOptionPB>(field_rev.ty)?
+            //     .apply_cmp(&left_cell, &right_cell),
+            _ => Ordering::Equal,
+        };
+        Option::<Ordering>::Some(order)
+    };
+
+    cal_order().unwrap_or(Ordering::Equal)
+}

+ 1 - 1
frontend/rust-lib/flowy-grid/src/services/view_editor/editor.rs

@@ -386,7 +386,7 @@ impl GridViewRevisionEditor {
             id: sort_id,
             field_id: params.field_id.clone(),
             field_type: params.field_type,
-            condition: params.condition,
+            condition: params.condition.into(),
         };
 
         let mut sort_controller = self.sort_controller.write().await;

+ 1 - 0
frontend/rust-lib/flowy-grid/tests/grid/mod.rs

@@ -5,3 +5,4 @@ mod filter_test;
 mod grid_editor;
 mod group_test;
 mod snapshot_test;
+mod sort_test;

+ 2 - 0
frontend/rust-lib/flowy-grid/tests/grid/sort_test/mod.rs

@@ -0,0 +1,2 @@
+// mod script;
+// mod text_sort_test;

+ 53 - 0
frontend/rust-lib/flowy-grid/tests/grid/sort_test/script.rs

@@ -0,0 +1,53 @@
+use crate::grid::grid_editor::GridEditorTest;
+use flowy_grid::entities::{AlterSortParams, DeleteSortParams};
+
+pub enum SortScript {
+    InsertSort { params: AlterSortParams },
+    DeleteSort { params: DeleteSortParams },
+    AssertTextOrder { orders: Vec<String> },
+}
+
+pub struct GridSortTest {
+    inner: GridEditorTest,
+}
+
+impl GridSortTest {
+    pub async fn new() -> Self {
+        let editor_test = GridEditorTest::new_table().await;
+        Self { inner: editor_test }
+    }
+    pub async fn run_scripts(&mut self, scripts: Vec<SortScript>) {
+        for script in scripts {
+            self.run_script(script).await;
+        }
+    }
+
+    pub async fn run_script(&mut self, script: SortScript) {
+        match script {
+            SortScript::InsertSort { params } => {
+                let _ = self.editor.create_or_update_sort(params).await.unwrap();
+            }
+            SortScript::DeleteSort { params } => {
+                //
+                self.editor.delete_sort(params).await.unwrap();
+            }
+            SortScript::AssertTextOrder { orders: _ } => {
+                //
+            }
+        }
+    }
+}
+
+impl std::ops::Deref for GridSortTest {
+    type Target = GridEditorTest;
+
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl std::ops::DerefMut for GridSortTest {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.inner
+    }
+}

+ 0 - 0
frontend/rust-lib/flowy-grid/tests/grid/sort_test/text_sort_test.rs


+ 25 - 1
shared-lib/grid-rev-model/src/sort_rev.rs

@@ -1,10 +1,34 @@
 use crate::FieldTypeRevision;
 use serde::{Deserialize, Serialize};
+use serde_repr::*;
 
 #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)]
 pub struct SortRevision {
     pub id: String,
     pub field_id: String,
     pub field_type: FieldTypeRevision,
-    pub condition: u8,
+    pub condition: SortCondition,
+}
+
+#[derive(Serialize_repr, Deserialize_repr, PartialEq, Eq, Hash, Clone, Debug)]
+#[repr(u8)]
+pub enum SortCondition {
+    Ascending = 0,
+    Descending = 1,
+}
+
+impl std::convert::From<u8> for SortCondition {
+    fn from(num: u8) -> Self {
+        match num {
+            0 => SortCondition::Ascending,
+            1 => SortCondition::Descending,
+            _ => SortCondition::Ascending,
+        }
+    }
+}
+
+impl std::default::Default for SortCondition {
+    fn default() -> Self {
+        Self::Ascending
+    }
 }