Pārlūkot izejas kodu

Merge pull request #599 from AppFlowy-IO/refactor/grid_decode_cell_data

Refactor/grid decode cell data
Nathan.fooo 2 gadi atpakaļ
vecāks
revīzija
bb6a670d26
26 mainītis faili ar 616 papildinājumiem un 567 dzēšanām
  1. 1 1
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart
  2. 2 2
      frontend/rust-lib/flowy-grid/src/entities/cell_entities.rs
  3. 2 0
      frontend/rust-lib/flowy-grid/src/entities/field_entities.rs
  4. 4 4
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  5. 151 0
      frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs
  6. 194 0
      frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs
  7. 5 0
      frontend/rust-lib/flowy-grid/src/services/cell/mod.rs
  8. 26 8
      frontend/rust-lib/flowy-grid/src/services/field/select_option.rs
  9. 17 20
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs
  10. 65 60
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs
  11. 25 28
      frontend/rust-lib/flowy-grid/src/services/field/type_options/multi_select_type_option.rs
  12. 16 18
      frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs
  13. 25 30
      frontend/rust-lib/flowy-grid/src/services/field/type_options/single_select_type_option.rs
  14. 30 25
      frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs
  15. 24 35
      frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs
  16. 1 1
      frontend/rust-lib/flowy-grid/src/services/field/type_options/util/cell_data_util.rs
  17. 2 3
      frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs
  18. 11 12
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  19. 1 0
      frontend/rust-lib/flowy-grid/src/services/mod.rs
  20. 0 305
      frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs
  21. 0 2
      frontend/rust-lib/flowy-grid/src/services/row/mod.rs
  22. 5 5
      frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs
  23. 4 4
      frontend/rust-lib/flowy-grid/tests/grid/cell_test.rs
  24. 1 1
      frontend/rust-lib/flowy-grid/tests/grid/field_util.rs
  25. 2 1
      frontend/rust-lib/flowy-grid/tests/grid/row_test.rs
  26. 2 2
      frontend/rust-lib/flowy-grid/tests/grid/row_util.rs

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart

@@ -38,7 +38,7 @@ class CellService {
       ..gridId = gridId
       ..fieldId = fieldId
       ..rowId = rowId
-      ..cellContentChangeset = data;
+      ..content = data;
     return GridEventUpdateCell(payload).send();
   }
 

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

@@ -134,7 +134,7 @@ pub struct CellChangeset {
     pub field_id: String,
 
     #[pb(index = 4, one_of)]
-    pub cell_content_changeset: Option<String>,
+    pub content: Option<String>,
 }
 
 impl std::convert::From<CellChangeset> for RowMetaChangeset {
@@ -142,7 +142,7 @@ impl std::convert::From<CellChangeset> for RowMetaChangeset {
         let mut cell_by_field_id = HashMap::with_capacity(1);
         let field_id = changeset.field_id;
         let cell_rev = CellRevision {
-            data: changeset.cell_content_changeset.unwrap_or_else(|| "".to_owned()),
+            data: changeset.content.unwrap_or_else(|| "".to_owned()),
         };
         cell_by_field_id.insert(field_id, cell_rev);
 

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

@@ -458,6 +458,8 @@ impl TryInto<FieldChangesetParams> for FieldChangesetPayload {
     Serialize_repr,
     Deserialize_repr,
 )]
+/// The order of the enum can't be changed. If you want to add a new type,
+/// it would be better to append it to the end of the list.
 #[repr(u8)]
 pub enum FieldType {
     RichText = 0,

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

@@ -1,10 +1,10 @@
 use crate::entities::*;
 use crate::manager::GridManager;
+use crate::services::cell::AnyCellData;
 use crate::services::field::select_option::*;
 use crate::services::field::{
     default_type_option_builder_from_type, type_option_builder_from_json_str, DateChangesetParams, DateChangesetPayload,
 };
-use crate::services::row::AnyCellData;
 use flowy_error::{ErrorCode, FlowyError, FlowyResult};
 use flowy_grid_data_model::revision::FieldRevision;
 use flowy_sync::entities::grid::{FieldChangesetParams, GridSettingChangesetParams};
@@ -323,7 +323,7 @@ pub(crate) async fn update_select_option_handler(
         let mut cell_content_changeset = None;
 
         if let Some(option) = changeset.insert_option {
-            cell_content_changeset = Some(SelectOptionCellContentChangeset::from_insert(&option.id).to_str());
+            cell_content_changeset = Some(SelectOptionCellChangeset::from_insert(&option.id).to_str());
             type_option.insert_option(option);
         }
 
@@ -332,7 +332,7 @@ pub(crate) async fn update_select_option_handler(
         }
 
         if let Some(option) = changeset.delete_option {
-            cell_content_changeset = Some(SelectOptionCellContentChangeset::from_delete(&option.id).to_str());
+            cell_content_changeset = Some(SelectOptionCellChangeset::from_delete(&option.id).to_str());
             type_option.delete_option(option);
         }
 
@@ -343,7 +343,7 @@ pub(crate) async fn update_select_option_handler(
             grid_id: changeset.cell_identifier.grid_id,
             row_id: changeset.cell_identifier.row_id,
             field_id: changeset.cell_identifier.field_id,
-            cell_content_changeset,
+            content: cell_content_changeset,
         };
         let _ = editor.update_cell(changeset).await?;
     }

+ 151 - 0
frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs

@@ -0,0 +1,151 @@
+use crate::entities::FieldType;
+use bytes::Bytes;
+use flowy_error::{internal_error, FlowyError, FlowyResult};
+use flowy_grid_data_model::revision::CellRevision;
+use serde::{Deserialize, Serialize};
+use std::str::FromStr;
+/// AnyCellData is a generic CellData, you can parse the cell_data according to the field_type.
+/// When the type of field is changed, it's different from the field_type of AnyCellData.
+/// So it will return an empty data. You could check the CellDataOperation trait for more information.
+#[derive(Debug, Serialize, Deserialize)]
+pub struct AnyCellData {
+    pub cell_data: String,
+    pub field_type: FieldType,
+}
+
+impl std::str::FromStr for AnyCellData {
+    type Err = FlowyError;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let type_option_cell_data: AnyCellData = serde_json::from_str(s)?;
+        Ok(type_option_cell_data)
+    }
+}
+
+impl std::convert::TryInto<AnyCellData> for String {
+    type Error = FlowyError;
+
+    fn try_into(self) -> Result<AnyCellData, Self::Error> {
+        AnyCellData::from_str(&self)
+    }
+}
+
+impl std::convert::TryFrom<&CellRevision> for AnyCellData {
+    type Error = FlowyError;
+
+    fn try_from(value: &CellRevision) -> Result<Self, Self::Error> {
+        Self::from_str(&value.data)
+    }
+}
+
+impl std::convert::TryFrom<&Option<CellRevision>> for AnyCellData {
+    type Error = FlowyError;
+
+    fn try_from(value: &Option<CellRevision>) -> Result<Self, Self::Error> {
+        match value {
+            None => Err(FlowyError::invalid_data().context("Expected CellRevision, but receive None")),
+            Some(cell_rev) => AnyCellData::try_from(cell_rev),
+        }
+    }
+}
+
+impl std::convert::TryFrom<Option<CellRevision>> for AnyCellData {
+    type Error = FlowyError;
+
+    fn try_from(value: Option<CellRevision>) -> Result<Self, Self::Error> {
+        Self::try_from(&value)
+    }
+}
+
+impl AnyCellData {
+    pub fn new(content: String, field_type: FieldType) -> Self {
+        AnyCellData {
+            cell_data: content,
+            field_type,
+        }
+    }
+
+    pub fn json(&self) -> String {
+        serde_json::to_string(self).unwrap_or_else(|_| "".to_owned())
+    }
+
+    pub fn is_number(&self) -> bool {
+        self.field_type == FieldType::Number
+    }
+
+    pub fn is_text(&self) -> bool {
+        self.field_type == FieldType::RichText
+    }
+
+    pub fn is_checkbox(&self) -> bool {
+        self.field_type == FieldType::Checkbox
+    }
+
+    pub fn is_date(&self) -> bool {
+        self.field_type == FieldType::DateTime
+    }
+
+    pub fn is_single_select(&self) -> bool {
+        self.field_type == FieldType::SingleSelect
+    }
+
+    pub fn is_multi_select(&self) -> bool {
+        self.field_type == FieldType::MultiSelect
+    }
+
+    pub fn is_url(&self) -> bool {
+        self.field_type == FieldType::URL
+    }
+
+    pub fn is_select_option(&self) -> bool {
+        self.field_type == FieldType::MultiSelect || self.field_type == FieldType::SingleSelect
+    }
+}
+
+/// The data is encoded by protobuf or utf8. You should choose the corresponding decode struct to parse it.
+///
+/// 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 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)]
+pub struct DecodedCellData {
+    pub data: Vec<u8>,
+}
+
+impl DecodedCellData {
+    pub fn new<T: AsRef<[u8]>>(data: T) -> Self {
+        Self {
+            data: data.as_ref().to_vec(),
+        }
+    }
+
+    pub fn try_from_bytes<T: TryInto<Bytes>>(bytes: T) -> FlowyResult<Self>
+    where
+        <T as TryInto<Bytes>>::Error: std::fmt::Debug,
+    {
+        let bytes = bytes.try_into().map_err(internal_error)?;
+        Ok(Self { data: bytes.to_vec() })
+    }
+
+    pub fn parse<'a, T: TryFrom<&'a [u8]>>(&'a self) -> FlowyResult<T>
+    where
+        <T as TryFrom<&'a [u8]>>::Error: std::fmt::Debug,
+    {
+        T::try_from(self.data.as_ref()).map_err(internal_error)
+    }
+}
+
+impl ToString for DecodedCellData {
+    fn to_string(&self) -> String {
+        match String::from_utf8(self.data.clone()) {
+            Ok(s) => s,
+            Err(e) => {
+                tracing::error!("DecodedCellData to string failed: {:?}", e);
+                "".to_string()
+            }
+        }
+    }
+}

+ 194 - 0
frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs

@@ -0,0 +1,194 @@
+use flowy_error::{ErrorCode, FlowyError, FlowyResult};
+use flowy_grid_data_model::revision::{CellRevision, FieldRevision, FieldTypeRevision};
+
+use crate::entities::FieldType;
+use crate::services::cell::{AnyCellData, DecodedCellData};
+use crate::services::field::*;
+
+pub trait CellFilterOperation<T> {
+    /// Return true if any_cell_data match the filter condition.
+    fn apply_filter(&self, any_cell_data: AnyCellData, filter: &T) -> FlowyResult<bool>;
+}
+
+pub trait CellDataOperation<D, C> {
+    /// The cell_data is able to parse into the specific data that was impl the FromCellData trait.
+    /// For example:
+    /// URLCellData, DateCellData. etc.
+    fn decode_cell_data(
+        &self,
+        cell_data: CellData<D>,
+        decoded_field_type: &FieldType,
+        field_rev: &FieldRevision,
+    ) -> FlowyResult<DecodedCellData>;
+
+    /// The changeset is able to parse into the specific data that was impl the FromCellChangeset trait.
+    /// For example:
+    /// SelectOptionCellChangeset,DateCellChangeset. etc.  
+    fn apply_changeset(&self, changeset: CellDataChangeset<C>, cell_rev: Option<CellRevision>) -> FlowyResult<String>;
+}
+/// The changeset will be deserialized into specific data base on the FieldType.
+/// For example, it's String on FieldType::RichText, and SelectOptionChangeset on FieldType::SingleSelect
+pub fn apply_cell_data_changeset<C: ToString, T: AsRef<FieldRevision>>(
+    changeset: C,
+    cell_rev: Option<CellRevision>,
+    field_rev: T,
+) -> Result<String, FlowyError> {
+    let field_rev = field_rev.as_ref();
+    let changeset = changeset.to_string();
+    let field_type = field_rev.field_type_rev.into();
+    let s = match field_type {
+        FieldType::RichText => RichTextTypeOption::from(field_rev).apply_changeset(changeset.into(), cell_rev),
+        FieldType::Number => NumberTypeOption::from(field_rev).apply_changeset(changeset.into(), cell_rev),
+        FieldType::DateTime => DateTypeOption::from(field_rev).apply_changeset(changeset.into(), cell_rev),
+        FieldType::SingleSelect => SingleSelectTypeOption::from(field_rev).apply_changeset(changeset.into(), cell_rev),
+        FieldType::MultiSelect => MultiSelectTypeOption::from(field_rev).apply_changeset(changeset.into(), cell_rev),
+        FieldType::Checkbox => CheckboxTypeOption::from(field_rev).apply_changeset(changeset.into(), cell_rev),
+        FieldType::URL => URLTypeOption::from(field_rev).apply_changeset(changeset.into(), cell_rev),
+    }?;
+
+    Ok(AnyCellData::new(s, field_type).json())
+}
+
+pub fn decode_any_cell_data<T: TryInto<AnyCellData>>(data: T, field_rev: &FieldRevision) -> DecodedCellData {
+    if let Ok(any_cell_data) = data.try_into() {
+        let AnyCellData { cell_data, field_type } = any_cell_data;
+        let to_field_type = field_rev.field_type_rev.into();
+        match try_decode_cell_data(CellData(Some(cell_data)), field_rev, &field_type, &to_field_type) {
+            Ok(cell_data) => cell_data,
+            Err(e) => {
+                tracing::error!("Decode cell data failed, {:?}", e);
+                DecodedCellData::default()
+            }
+        }
+    } else {
+        tracing::error!("Decode type option data failed");
+        DecodedCellData::default()
+    }
+}
+
+pub fn try_decode_cell_data(
+    cell_data: CellData<String>,
+    field_rev: &FieldRevision,
+    s_field_type: &FieldType,
+    t_field_type: &FieldType,
+) -> FlowyResult<DecodedCellData> {
+    let cell_data = cell_data.try_into_inner()?;
+    let get_cell_data = || {
+        let field_type: FieldTypeRevision = t_field_type.into();
+        let data = match t_field_type {
+            FieldType::RichText => field_rev
+                .get_type_option_entry::<RichTextTypeOption>(field_type)?
+                .decode_cell_data(cell_data.into(), s_field_type, field_rev),
+            FieldType::Number => field_rev
+                .get_type_option_entry::<NumberTypeOption>(field_type)?
+                .decode_cell_data(cell_data.into(), s_field_type, field_rev),
+            FieldType::DateTime => field_rev
+                .get_type_option_entry::<DateTypeOption>(field_type)?
+                .decode_cell_data(cell_data.into(), s_field_type, field_rev),
+            FieldType::SingleSelect => field_rev
+                .get_type_option_entry::<SingleSelectTypeOption>(field_type)?
+                .decode_cell_data(cell_data.into(), s_field_type, field_rev),
+            FieldType::MultiSelect => field_rev
+                .get_type_option_entry::<MultiSelectTypeOption>(field_type)?
+                .decode_cell_data(cell_data.into(), s_field_type, field_rev),
+            FieldType::Checkbox => field_rev
+                .get_type_option_entry::<CheckboxTypeOption>(field_type)?
+                .decode_cell_data(cell_data.into(), s_field_type, field_rev),
+            FieldType::URL => field_rev
+                .get_type_option_entry::<URLTypeOption>(field_type)?
+                .decode_cell_data(cell_data.into(), s_field_type, field_rev),
+        };
+        Some(data)
+    };
+
+    match get_cell_data() {
+        Some(Ok(data)) => Ok(data),
+        Some(Err(err)) => {
+            tracing::error!("{:?}", err);
+            Ok(DecodedCellData::default())
+        }
+        None => Ok(DecodedCellData::default()),
+    }
+}
+
+pub trait FromCellString {
+    fn from_cell_str(s: &str) -> FlowyResult<Self>
+    where
+        Self: Sized;
+}
+
+pub struct CellData<T>(pub Option<T>);
+
+impl<T> CellData<T> {
+    pub fn try_into_inner(self) -> FlowyResult<T> {
+        match self.0 {
+            None => Err(ErrorCode::InvalidData.into()),
+            Some(data) => Ok(data),
+        }
+    }
+}
+
+impl<T> std::convert::From<String> for CellData<T>
+where
+    T: FromCellString,
+{
+    fn from(s: String) -> Self {
+        match T::from_cell_str(&s) {
+            Ok(inner) => CellData(Some(inner)),
+            Err(e) => {
+                tracing::error!("Deserialize Cell Data failed: {}", e);
+                CellData(None)
+            }
+        }
+    }
+}
+
+impl std::convert::From<String> for CellData<String> {
+    fn from(s: String) -> Self {
+        CellData(Some(s))
+    }
+}
+
+impl std::convert::From<CellData<String>> for String {
+    fn from(p: CellData<String>) -> Self {
+        p.try_into_inner().unwrap_or_else(|_| String::new())
+    }
+}
+
+// CellChangeset
+pub trait FromCellChangeset {
+    fn from_changeset(changeset: String) -> FlowyResult<Self>
+    where
+        Self: Sized;
+}
+
+pub struct CellDataChangeset<T>(pub Option<T>);
+
+impl<T> CellDataChangeset<T> {
+    pub fn try_into_inner(self) -> FlowyResult<T> {
+        match self.0 {
+            None => Err(ErrorCode::InvalidData.into()),
+            Some(data) => Ok(data),
+        }
+    }
+}
+
+impl<T, C: ToString> std::convert::From<C> for CellDataChangeset<T>
+where
+    T: FromCellChangeset,
+{
+    fn from(changeset: C) -> Self {
+        match T::from_changeset(changeset.to_string()) {
+            Ok(data) => CellDataChangeset(Some(data)),
+            Err(e) => {
+                tracing::error!("Deserialize CellDataChangeset failed: {}", e);
+                CellDataChangeset(None)
+            }
+        }
+    }
+}
+impl std::convert::From<String> for CellDataChangeset<String> {
+    fn from(s: String) -> Self {
+        CellDataChangeset(Some(s))
+    }
+}

+ 5 - 0
frontend/rust-lib/flowy-grid/src/services/cell/mod.rs

@@ -0,0 +1,5 @@
+mod any_cell_data;
+mod cell_operation;
+
+pub use any_cell_data::*;
+pub use cell_operation::*;

+ 26 - 8
frontend/rust-lib/flowy-grid/src/services/field/select_option.rs

@@ -1,8 +1,8 @@
 use crate::entities::{CellChangeset, CellIdentifier, CellIdentifierPayload, FieldType};
+use crate::services::cell::{AnyCellData, FromCellChangeset, FromCellString};
 use crate::services::field::{MultiSelectTypeOption, SingleSelectTypeOption};
-use crate::services::row::AnyCellData;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
-use flowy_error::{ErrorCode, FlowyError, FlowyResult};
+use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
 use flowy_grid_data_model::parser::NotEmptyStr;
 use flowy_grid_data_model::revision::{FieldRevision, TypeOptionDataEntry};
 use nanoid::nanoid;
@@ -155,6 +155,15 @@ impl std::convert::TryFrom<AnyCellData> for SelectOptionIds {
     }
 }
 
+impl FromCellString for SelectOptionIds {
+    fn from_cell_str(s: &str) -> FlowyResult<Self>
+    where
+        Self: Sized,
+    {
+        Ok(Self::from(s.to_owned()))
+    }
+}
+
 impl std::convert::From<String> for SelectOptionIds {
     fn from(s: String) -> Self {
         let ids = s
@@ -208,7 +217,7 @@ pub struct SelectOptionCellChangesetParams {
 
 impl std::convert::From<SelectOptionCellChangesetParams> for CellChangeset {
     fn from(params: SelectOptionCellChangesetParams) -> Self {
-        let changeset = SelectOptionCellContentChangeset {
+        let changeset = SelectOptionCellChangeset {
             insert_option_id: params.insert_option_id,
             delete_option_id: params.delete_option_id,
         };
@@ -217,7 +226,7 @@ impl std::convert::From<SelectOptionCellChangesetParams> for CellChangeset {
             grid_id: params.cell_identifier.grid_id,
             row_id: params.cell_identifier.row_id,
             field_id: params.cell_identifier.field_id,
-            cell_content_changeset: Some(s),
+            content: Some(s),
         }
     }
 }
@@ -254,21 +263,30 @@ impl TryInto<SelectOptionCellChangesetParams> for SelectOptionCellChangesetPaylo
 }
 
 #[derive(Clone, Serialize, Deserialize)]
-pub struct SelectOptionCellContentChangeset {
+pub struct SelectOptionCellChangeset {
     pub insert_option_id: Option<String>,
     pub delete_option_id: Option<String>,
 }
 
-impl SelectOptionCellContentChangeset {
+impl FromCellChangeset for SelectOptionCellChangeset {
+    fn from_changeset(changeset: String) -> FlowyResult<Self>
+    where
+        Self: Sized,
+    {
+        serde_json::from_str::<SelectOptionCellChangeset>(&changeset).map_err(internal_error)
+    }
+}
+
+impl SelectOptionCellChangeset {
     pub fn from_insert(option_id: &str) -> Self {
-        SelectOptionCellContentChangeset {
+        SelectOptionCellChangeset {
             insert_option_id: Some(option_id.to_string()),
             delete_option_id: None,
         }
     }
 
     pub fn from_delete(option_id: &str) -> Self {
-        SelectOptionCellContentChangeset {
+        SelectOptionCellChangeset {
             insert_option_id: None,
             delete_option_id: Some(option_id.to_string()),
         }

+ 17 - 20
frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs

@@ -1,9 +1,9 @@
 use crate::entities::{FieldType, GridCheckboxFilter};
 use crate::impl_type_option;
-use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
-use crate::services::row::{
-    AnyCellData, CellContentChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
+use crate::services::cell::{
+    AnyCellData, CellData, CellDataChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
 };
+use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
 use flowy_error::{FlowyError, FlowyResult};
@@ -52,33 +52,31 @@ impl CellFilterOperation<GridCheckboxFilter> for CheckboxTypeOption {
     }
 }
 
-impl CellDataOperation<String> for CheckboxTypeOption {
-    fn decode_cell_data<T>(
+impl CellDataOperation<String, String> for CheckboxTypeOption {
+    fn decode_cell_data(
         &self,
-        cell_data: T,
+        cell_data: CellData<String>,
         decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
-    ) -> FlowyResult<DecodedCellData>
-    where
-        T: Into<String>,
-    {
+    ) -> FlowyResult<DecodedCellData> {
         if !decoded_field_type.is_checkbox() {
             return Ok(DecodedCellData::default());
         }
 
-        let encoded_data = cell_data.into();
-        if encoded_data == YES || encoded_data == NO {
-            return Ok(DecodedCellData::new(encoded_data));
+        let s: String = cell_data.try_into_inner()?;
+        if s == YES || s == NO {
+            return Ok(DecodedCellData::new(s));
         }
 
         Ok(DecodedCellData::default())
     }
 
-    fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
-    where
-        C: Into<CellContentChangeset>,
-    {
-        let changeset = changeset.into();
+    fn apply_changeset(
+        &self,
+        changeset: CellDataChangeset<String>,
+        _cell_rev: Option<CellRevision>,
+    ) -> Result<String, FlowyError> {
+        let changeset = changeset.try_into_inner()?;
         let s = match string_to_bool(&changeset) {
             true => YES,
             false => NO,
@@ -117,10 +115,9 @@ impl std::convert::TryFrom<AnyCellData> for CheckboxCellData {
 
 #[cfg(test)]
 mod tests {
+    use crate::services::cell::{apply_cell_data_changeset, decode_any_cell_data};
     use crate::services::field::type_options::checkbox_type_option::{NO, YES};
-
     use crate::services::field::FieldBuilder;
-    use crate::services::row::{apply_cell_data_changeset, decode_any_cell_data};
 
     use crate::entities::FieldType;
 

+ 65 - 60
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs

@@ -1,15 +1,16 @@
 use crate::entities::{CellChangeset, FieldType, GridDateFilter};
 use crate::entities::{CellIdentifier, CellIdentifierPayload};
 use crate::impl_type_option;
-use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
-use crate::services::row::{
-    AnyCellData, CellContentChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
+use crate::services::cell::{
+    AnyCellData, CellData, CellDataChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
+    FromCellChangeset, FromCellString,
 };
+use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
 use bytes::Bytes;
 use chrono::format::strftime::StrftimeItems;
 use chrono::{NaiveDateTime, Timelike};
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
-use flowy_error::{ErrorCode, FlowyError, FlowyResult};
+use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
 use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
 use serde::{Deserialize, Serialize};
 use strum_macros::EnumIter;
@@ -126,16 +127,13 @@ impl CellFilterOperation<GridDateFilter> for DateTypeOption {
     }
 }
 
-impl CellDataOperation<String> for DateTypeOption {
-    fn decode_cell_data<T>(
+impl CellDataOperation<TimestampParser, DateCellChangeset> for DateTypeOption {
+    fn decode_cell_data(
         &self,
-        cell_data: T,
+        cell_data: CellData<TimestampParser>,
         decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
-    ) -> FlowyResult<DecodedCellData>
-    where
-        T: Into<String>,
-    {
+    ) -> FlowyResult<DecodedCellData> {
         // Return default data if the type_option_cell_data is not FieldType::DateTime.
         // It happens when switching from one field to another.
         // For example:
@@ -143,20 +141,20 @@ impl CellDataOperation<String> for DateTypeOption {
         if !decoded_field_type.is_date() {
             return Ok(DecodedCellData::default());
         }
-
-        let timestamp = cell_data.into().parse::<i64>().unwrap_or(0);
-        let date = self.today_desc_from_timestamp(timestamp);
+        let timestamp = cell_data.try_into_inner()?;
+        let date = self.today_desc_from_timestamp(timestamp.0);
         DecodedCellData::try_from_bytes(date)
     }
 
-    fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
-    where
-        C: Into<CellContentChangeset>,
-    {
-        let content_changeset: DateCellContentChangeset = serde_json::from_str(&changeset.into())?;
-        let cell_data = match content_changeset.date_timestamp() {
+    fn apply_changeset(
+        &self,
+        changeset: CellDataChangeset<DateCellChangeset>,
+        _cell_rev: Option<CellRevision>,
+    ) -> Result<String, FlowyError> {
+        let changeset = changeset.try_into_inner()?;
+        let cell_data = match changeset.date_timestamp() {
             None => 0,
-            Some(date_timestamp) => match (self.include_time, content_changeset.time) {
+            Some(date_timestamp) => match (self.include_time, changeset.time) {
                 (true, Some(time)) => {
                     let time = Some(time.trim().to_uppercase());
                     let utc = self.utc_date_time_from_timestamp(date_timestamp);
@@ -170,6 +168,17 @@ impl CellDataOperation<String> for DateTypeOption {
     }
 }
 
+pub struct TimestampParser(i64);
+
+impl FromCellString for TimestampParser {
+    fn from_cell_str(s: &str) -> FlowyResult<Self>
+    where
+        Self: Sized,
+    {
+        let num = s.parse::<i64>().unwrap_or(0);
+        Ok(TimestampParser(num))
+    }
+}
 #[derive(Default)]
 pub struct DateTypeOptionBuilder(DateTypeOption);
 impl_into_box_type_option_builder!(DateTypeOptionBuilder);
@@ -323,7 +332,7 @@ impl TryInto<DateChangesetParams> for DateChangesetPayload {
 
 impl std::convert::From<DateChangesetParams> for CellChangeset {
     fn from(params: DateChangesetParams) -> Self {
-        let changeset = DateCellContentChangeset {
+        let changeset = DateCellChangeset {
             date: params.date,
             time: params.time,
         };
@@ -332,18 +341,18 @@ impl std::convert::From<DateChangesetParams> for CellChangeset {
             grid_id: params.cell_identifier.grid_id,
             row_id: params.cell_identifier.row_id,
             field_id: params.cell_identifier.field_id,
-            cell_content_changeset: Some(s),
+            content: Some(s),
         }
     }
 }
 
 #[derive(Clone, Serialize, Deserialize)]
-pub struct DateCellContentChangeset {
+pub struct DateCellChangeset {
     pub date: Option<String>,
     pub time: Option<String>,
 }
 
-impl DateCellContentChangeset {
+impl DateCellChangeset {
     pub fn date_timestamp(&self) -> Option<i64> {
         if let Some(date) = &self.date {
             match date.parse::<i64>() {
@@ -356,19 +365,21 @@ impl DateCellContentChangeset {
     }
 }
 
-impl std::convert::From<DateCellContentChangeset> for CellContentChangeset {
-    fn from(changeset: DateCellContentChangeset) -> Self {
-        let s = serde_json::to_string(&changeset).unwrap();
-        CellContentChangeset::from(s)
+impl FromCellChangeset for DateCellChangeset {
+    fn from_changeset(changeset: String) -> FlowyResult<Self>
+    where
+        Self: Sized,
+    {
+        serde_json::from_str::<DateCellChangeset>(&changeset).map_err(internal_error)
     }
 }
 
 #[cfg(test)]
 mod tests {
     use crate::entities::FieldType;
+    use crate::services::cell::{CellDataChangeset, CellDataOperation};
     use crate::services::field::FieldBuilder;
-    use crate::services::field::{DateCellContentChangeset, DateCellData, DateFormat, DateTypeOption, TimeFormat};
-    use crate::services::row::CellDataOperation;
+    use crate::services::field::{DateCellChangeset, DateCellData, DateFormat, DateTypeOption, TimeFormat};
     use flowy_grid_data_model::revision::FieldRevision;
     use strum::IntoEnumIterator;
 
@@ -379,7 +390,7 @@ mod tests {
         let field_rev = FieldBuilder::from_field_type(&field_type).build();
         assert_changeset_result(
             &type_option,
-            DateCellContentChangeset {
+            DateCellChangeset {
                 date: Some("1e".to_string()),
                 time: Some("23:00".to_owned()),
             },
@@ -425,7 +436,7 @@ mod tests {
                 TimeFormat::TwentyFourHour => {
                     assert_changeset_result(
                         &type_option,
-                        DateCellContentChangeset {
+                        DateCellChangeset {
                             date: Some(1653609600.to_string()),
                             time: None,
                         },
@@ -435,7 +446,7 @@ mod tests {
                     );
                     assert_changeset_result(
                         &type_option,
-                        DateCellContentChangeset {
+                        DateCellChangeset {
                             date: Some(1653609600.to_string()),
                             time: Some("23:00".to_owned()),
                         },
@@ -447,7 +458,7 @@ mod tests {
                 TimeFormat::TwelveHour => {
                     assert_changeset_result(
                         &type_option,
-                        DateCellContentChangeset {
+                        DateCellChangeset {
                             date: Some(1653609600.to_string()),
                             time: None,
                         },
@@ -458,7 +469,7 @@ mod tests {
                     //
                     assert_changeset_result(
                         &type_option,
-                        DateCellContentChangeset {
+                        DateCellChangeset {
                             date: Some(1653609600.to_string()),
                             time: Some("".to_owned()),
                         },
@@ -469,7 +480,7 @@ mod tests {
 
                     assert_changeset_result(
                         &type_option,
-                        DateCellContentChangeset {
+                        DateCellChangeset {
                             date: Some(1653609600.to_string()),
                             time: Some("11:23 pm".to_owned()),
                         },
@@ -491,7 +502,7 @@ mod tests {
 
         assert_changeset_result(
             &type_option,
-            DateCellContentChangeset {
+            DateCellChangeset {
                 date: Some(date_timestamp.clone()),
                 time: None,
             },
@@ -503,7 +514,7 @@ mod tests {
         type_option.include_time = true;
         assert_changeset_result(
             &type_option,
-            DateCellContentChangeset {
+            DateCellChangeset {
                 date: Some(date_timestamp.clone()),
                 time: None,
             },
@@ -514,7 +525,7 @@ mod tests {
 
         assert_changeset_result(
             &type_option,
-            DateCellContentChangeset {
+            DateCellChangeset {
                 date: Some(date_timestamp.clone()),
                 time: Some("1:00".to_owned()),
             },
@@ -526,7 +537,7 @@ mod tests {
         type_option.time_format = TimeFormat::TwelveHour;
         assert_changeset_result(
             &type_option,
-            DateCellContentChangeset {
+            DateCellChangeset {
                 date: Some(date_timestamp),
                 time: Some("1:00 am".to_owned()),
             },
@@ -546,7 +557,7 @@ mod tests {
 
         assert_changeset_result(
             &type_option,
-            DateCellContentChangeset {
+            DateCellChangeset {
                 date: Some(date_timestamp.clone()),
                 time: Some("1:".to_owned()),
             },
@@ -557,7 +568,7 @@ mod tests {
 
         assert_changeset_result(
             &type_option,
-            DateCellContentChangeset {
+            DateCellChangeset {
                 date: Some(date_timestamp),
                 time: Some("1:00".to_owned()),
             },
@@ -577,7 +588,7 @@ mod tests {
 
         assert_changeset_result(
             &type_option,
-            DateCellContentChangeset {
+            DateCellChangeset {
                 date: Some(date_timestamp),
                 time: Some("1:00 am".to_owned()),
             },
@@ -589,11 +600,12 @@ mod tests {
 
     fn assert_changeset_result(
         type_option: &DateTypeOption,
-        changeset: DateCellContentChangeset,
+        changeset: DateCellChangeset,
         _field_type: &FieldType,
         field_rev: &FieldRevision,
         expected: &str,
     ) {
+        let changeset = CellDataChangeset(Some(changeset));
         let encoded_data = type_option.apply_changeset(changeset, None).unwrap();
         assert_eq!(
             expected.to_owned(),
@@ -607,15 +619,12 @@ mod tests {
         field_rev: &FieldRevision,
         expected: &str,
     ) {
-        let encoded_data = type_option
-            .apply_changeset(
-                DateCellContentChangeset {
-                    date: Some(timestamp.to_string()),
-                    time: None,
-                },
-                None,
-            )
-            .unwrap();
+        let s = serde_json::to_string(&DateCellChangeset {
+            date: Some(timestamp.to_string()),
+            time: None,
+        })
+        .unwrap();
+        let encoded_data = type_option.apply_changeset(s.into(), None).unwrap();
 
         assert_eq!(
             expected.to_owned(),
@@ -623,13 +632,9 @@ mod tests {
         );
     }
 
-    fn decode_cell_data<T: Into<String>>(
-        encoded_data: T,
-        type_option: &DateTypeOption,
-        field_rev: &FieldRevision,
-    ) -> String {
+    fn decode_cell_data(encoded_data: String, type_option: &DateTypeOption, field_rev: &FieldRevision) -> String {
         let decoded_data = type_option
-            .decode_cell_data(encoded_data, &FieldType::DateTime, field_rev)
+            .decode_cell_data(encoded_data.into(), &FieldType::DateTime, field_rev)
             .unwrap()
             .parse::<DateCellData>()
             .unwrap();

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

@@ -1,15 +1,15 @@
 use crate::entities::{FieldType, GridSelectOptionFilter};
 
 use crate::impl_type_option;
+use crate::services::cell::{
+    AnyCellData, CellData, CellDataChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
+};
 use crate::services::field::select_option::{
-    make_selected_select_options, SelectOption, SelectOptionCellContentChangeset, SelectOptionCellData,
-    SelectOptionIds, SelectOptionOperation, SelectedSelectOptions, SELECTION_IDS_SEPARATOR,
+    make_selected_select_options, SelectOption, SelectOptionCellChangeset, SelectOptionCellData, SelectOptionIds,
+    SelectOptionOperation, SelectedSelectOptions, SELECTION_IDS_SEPARATOR,
 };
 use crate::services::field::type_options::util::get_cell_data;
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
-use crate::services::row::{
-    AnyCellData, CellContentChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
-};
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
 use flowy_error::{FlowyError, FlowyResult};
@@ -56,22 +56,18 @@ impl CellFilterOperation<GridSelectOptionFilter> for MultiSelectTypeOption {
         Ok(filter.apply(&selected_options))
     }
 }
-impl CellDataOperation<String> for MultiSelectTypeOption {
-    fn decode_cell_data<T>(
+impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for MultiSelectTypeOption {
+    fn decode_cell_data(
         &self,
-        cell_data: T,
+        cell_data: CellData<SelectOptionIds>,
         decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
-    ) -> FlowyResult<DecodedCellData>
-    where
-        T: Into<String>,
-    {
+    ) -> FlowyResult<DecodedCellData> {
         if !decoded_field_type.is_select_option() {
             return Ok(DecodedCellData::default());
         }
 
-        let encoded_data = cell_data.into();
-        let ids: SelectOptionIds = encoded_data.into();
+        let ids: SelectOptionIds = cell_data.try_into_inner()?;
         let select_options = ids
             .iter()
             .flat_map(|option_id| self.options.iter().find(|option| &option.id == option_id).cloned())
@@ -85,11 +81,12 @@ impl CellDataOperation<String> for MultiSelectTypeOption {
         DecodedCellData::try_from_bytes(cell_data)
     }
 
-    fn apply_changeset<T>(&self, changeset: T, cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
-    where
-        T: Into<CellContentChangeset>,
-    {
-        let content_changeset: SelectOptionCellContentChangeset = serde_json::from_str(&changeset.into())?;
+    fn apply_changeset(
+        &self,
+        changeset: CellDataChangeset<SelectOptionCellChangeset>,
+        cell_rev: Option<CellRevision>,
+    ) -> Result<String, FlowyError> {
+        let content_changeset = changeset.try_into_inner()?;
         let new_cell_data: String;
         match cell_rev {
             None => {
@@ -144,10 +141,10 @@ impl TypeOptionBuilder for MultiSelectTypeOptionBuilder {
 #[cfg(test)]
 mod tests {
     use crate::entities::FieldType;
+    use crate::services::cell::CellDataOperation;
     use crate::services::field::select_option::*;
     use crate::services::field::FieldBuilder;
     use crate::services::field::{MultiSelectTypeOption, MultiSelectTypeOptionBuilder};
-    use crate::services::row::CellDataOperation;
     use flowy_grid_data_model::revision::FieldRevision;
 
     #[test]
@@ -168,8 +165,8 @@ mod tests {
         let type_option = MultiSelectTypeOption::from(&field_rev);
 
         let option_ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR);
-        let data = SelectOptionCellContentChangeset::from_insert(&option_ids).to_str();
-        let cell_data = type_option.apply_changeset(data, None).unwrap();
+        let data = SelectOptionCellChangeset::from_insert(&option_ids).to_str();
+        let cell_data = type_option.apply_changeset(data.into(), None).unwrap();
         assert_multi_select_options(
             cell_data,
             &type_option,
@@ -177,24 +174,24 @@ mod tests {
             vec![google_option.clone(), facebook_option],
         );
 
-        let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str();
-        let cell_data = type_option.apply_changeset(data, None).unwrap();
+        let data = SelectOptionCellChangeset::from_insert(&google_option.id).to_str();
+        let cell_data = type_option.apply_changeset(data.into(), None).unwrap();
         assert_multi_select_options(cell_data, &type_option, &field_rev, vec![google_option]);
 
         // Invalid option id
         let cell_data = type_option
-            .apply_changeset(SelectOptionCellContentChangeset::from_insert("").to_str(), None)
+            .apply_changeset(SelectOptionCellChangeset::from_insert("").to_str().into(), None)
             .unwrap();
         assert_multi_select_options(cell_data, &type_option, &field_rev, vec![]);
 
         // Invalid option id
         let cell_data = type_option
-            .apply_changeset(SelectOptionCellContentChangeset::from_insert("123,456").to_str(), None)
+            .apply_changeset(SelectOptionCellChangeset::from_insert("123,456").to_str().into(), None)
             .unwrap();
         assert_multi_select_options(cell_data, &type_option, &field_rev, vec![]);
 
         // Invalid changeset
-        assert!(type_option.apply_changeset("123", None).is_err());
+        assert!(type_option.apply_changeset("123".to_owned().into(), None).is_err());
     }
 
     fn assert_multi_select_options(
@@ -207,7 +204,7 @@ mod tests {
         assert_eq!(
             expected,
             type_option
-                .decode_cell_data(cell_data, &field_type, field_rev)
+                .decode_cell_data(cell_data.into(), &field_type, field_rev)
                 .unwrap()
                 .parse::<SelectOptionCellData>()
                 .unwrap()

+ 16 - 18
frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs

@@ -1,12 +1,12 @@
 use crate::impl_type_option;
 
 use crate::entities::{FieldType, GridNumberFilter};
+use crate::services::cell::{
+    AnyCellData, CellData, CellDataChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
+};
 use crate::services::field::number_currency::Currency;
 use crate::services::field::type_options::number_type_option::format::*;
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
-use crate::services::row::{
-    AnyCellData, CellContentChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
-};
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
 use flowy_error::{FlowyError, FlowyResult};
@@ -118,32 +118,30 @@ impl CellFilterOperation<GridNumberFilter> for NumberTypeOption {
     }
 }
 
-impl CellDataOperation<String> for NumberTypeOption {
-    fn decode_cell_data<T>(
+impl CellDataOperation<String, String> for NumberTypeOption {
+    fn decode_cell_data(
         &self,
-        cell_data: T,
+        cell_data: CellData<String>,
         decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
-    ) -> FlowyResult<DecodedCellData>
-    where
-        T: Into<String>,
-    {
+    ) -> FlowyResult<DecodedCellData> {
         if decoded_field_type.is_date() {
             return Ok(DecodedCellData::default());
         }
 
-        let cell_data = cell_data.into();
+        let cell_data: String = cell_data.try_into_inner()?;
         match self.format_cell_data(&cell_data) {
             Ok(num) => Ok(DecodedCellData::new(num.to_string())),
             Err(_) => Ok(DecodedCellData::default()),
         }
     }
 
-    fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
-    where
-        C: Into<CellContentChangeset>,
-    {
-        let changeset = changeset.into();
+    fn apply_changeset(
+        &self,
+        changeset: CellDataChangeset<String>,
+        _cell_rev: Option<CellRevision>,
+    ) -> Result<String, FlowyError> {
+        let changeset = changeset.try_into_inner()?;
         let data = changeset.trim().to_string();
         let _ = self.format_cell_data(&data)?;
         Ok(data)
@@ -254,9 +252,9 @@ impl ToString for NumberCellData {
 #[cfg(test)]
 mod tests {
     use crate::entities::FieldType;
+    use crate::services::cell::CellDataOperation;
     use crate::services::field::FieldBuilder;
     use crate::services::field::{strip_currency_symbol, NumberFormat, NumberTypeOption};
-    use crate::services::row::CellDataOperation;
     use flowy_grid_data_model::revision::FieldRevision;
     use strum::IntoEnumIterator;
 
@@ -383,7 +381,7 @@ mod tests {
     ) {
         assert_eq!(
             type_option
-                .decode_cell_data(cell_data, field_type, field_rev)
+                .decode_cell_data(cell_data.to_owned().into(), field_type, field_rev)
                 .unwrap()
                 .to_string(),
             expected_str.to_owned()

+ 25 - 30
frontend/rust-lib/flowy-grid/src/services/field/type_options/single_select_type_option.rs

@@ -1,13 +1,13 @@
 use crate::entities::{FieldType, GridSelectOptionFilter};
 use crate::impl_type_option;
+use crate::services::cell::{
+    AnyCellData, CellData, CellDataChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
+};
 use crate::services::field::select_option::{
-    make_selected_select_options, SelectOption, SelectOptionCellContentChangeset, SelectOptionCellData,
-    SelectOptionIds, SelectOptionOperation,
+    make_selected_select_options, SelectOption, SelectOptionCellChangeset, SelectOptionCellData, SelectOptionIds,
+    SelectOptionOperation,
 };
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
-use crate::services::row::{
-    AnyCellData, CellContentChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
-};
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
 use flowy_error::{FlowyError, FlowyResult};
@@ -53,27 +53,22 @@ impl CellFilterOperation<GridSelectOptionFilter> for SingleSelectTypeOption {
     }
 }
 
-impl CellDataOperation<String> for SingleSelectTypeOption {
-    fn decode_cell_data<T>(
+impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for SingleSelectTypeOption {
+    fn decode_cell_data(
         &self,
-        cell_data: T,
+        cell_data: CellData<SelectOptionIds>,
         decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
-    ) -> FlowyResult<DecodedCellData>
-    where
-        T: Into<String>,
-    {
+    ) -> FlowyResult<DecodedCellData> {
         if !decoded_field_type.is_select_option() {
             return Ok(DecodedCellData::default());
         }
 
-        let encoded_data = cell_data.into();
+        let ids: SelectOptionIds = cell_data.try_into_inner()?;
         let mut cell_data = SelectOptionCellData {
             options: self.options.clone(),
             select_options: vec![],
         };
-
-        let ids: SelectOptionIds = encoded_data.into();
         if let Some(option_id) = ids.first() {
             if let Some(option) = self.options.iter().find(|option| &option.id == option_id) {
                 cell_data.select_options.push(option.clone());
@@ -83,12 +78,12 @@ impl CellDataOperation<String> for SingleSelectTypeOption {
         DecodedCellData::try_from_bytes(cell_data)
     }
 
-    fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
-    where
-        C: Into<CellContentChangeset>,
-    {
-        let changeset = changeset.into();
-        let select_option_changeset: SelectOptionCellContentChangeset = serde_json::from_str(&changeset)?;
+    fn apply_changeset(
+        &self,
+        changeset: CellDataChangeset<SelectOptionCellChangeset>,
+        _cell_rev: Option<CellRevision>,
+    ) -> Result<String, FlowyError> {
+        let select_option_changeset = changeset.try_into_inner()?;
         let new_cell_data: String;
         if let Some(insert_option_id) = select_option_changeset.insert_option_id {
             tracing::trace!("Insert single select option: {}", &insert_option_id);
@@ -127,10 +122,10 @@ impl TypeOptionBuilder for SingleSelectTypeOptionBuilder {
 #[cfg(test)]
 mod tests {
     use crate::entities::FieldType;
+    use crate::services::cell::CellDataOperation;
     use crate::services::field::select_option::*;
     use crate::services::field::type_options::*;
     use crate::services::field::FieldBuilder;
-    use crate::services::row::CellDataOperation;
     use flowy_grid_data_model::revision::FieldRevision;
 
     #[test]
@@ -151,29 +146,29 @@ mod tests {
         let type_option = SingleSelectTypeOption::from(&field_rev);
 
         let option_ids = vec![google_option.id.clone(), facebook_option.id].join(SELECTION_IDS_SEPARATOR);
-        let data = SelectOptionCellContentChangeset::from_insert(&option_ids).to_str();
-        let cell_data = type_option.apply_changeset(data, None).unwrap();
+        let data = SelectOptionCellChangeset::from_insert(&option_ids).to_str();
+        let cell_data = type_option.apply_changeset(data.into(), None).unwrap();
         assert_single_select_options(cell_data, &type_option, &field_rev, vec![google_option.clone()]);
 
-        let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str();
-        let cell_data = type_option.apply_changeset(data, None).unwrap();
+        let data = SelectOptionCellChangeset::from_insert(&google_option.id).to_str();
+        let cell_data = type_option.apply_changeset(data.into(), None).unwrap();
         assert_single_select_options(cell_data, &type_option, &field_rev, vec![google_option]);
 
         // Invalid option id
         let cell_data = type_option
-            .apply_changeset(SelectOptionCellContentChangeset::from_insert("").to_str(), None)
+            .apply_changeset(SelectOptionCellChangeset::from_insert("").to_str().into(), None)
             .unwrap();
         assert_single_select_options(cell_data, &type_option, &field_rev, vec![]);
 
         // Invalid option id
         let cell_data = type_option
-            .apply_changeset(SelectOptionCellContentChangeset::from_insert("123").to_str(), None)
+            .apply_changeset(SelectOptionCellChangeset::from_insert("123").to_str().into(), None)
             .unwrap();
 
         assert_single_select_options(cell_data, &type_option, &field_rev, vec![]);
 
         // Invalid changeset
-        assert!(type_option.apply_changeset("123", None).is_err());
+        assert!(type_option.apply_changeset("123".to_owned().into(), None).is_err());
     }
 
     fn assert_single_select_options(
@@ -186,7 +181,7 @@ mod tests {
         assert_eq!(
             expected,
             type_option
-                .decode_cell_data(cell_data, &field_type, field_rev)
+                .decode_cell_data(cell_data.into(), &field_type, field_rev)
                 .unwrap()
                 .parse::<SelectOptionCellData>()
                 .unwrap()

+ 30 - 25
frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs

@@ -1,9 +1,10 @@
 use crate::entities::{FieldType, GridTextFilter};
 use crate::impl_type_option;
-use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
-use crate::services::row::{
-    try_decode_cell_data, AnyCellData, CellContentChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
+use crate::services::cell::{
+    try_decode_cell_data, AnyCellData, CellData, CellDataChangeset, CellDataOperation, CellFilterOperation,
+    DecodedCellData,
 };
+use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
 use flowy_error::{FlowyError, FlowyResult};
@@ -43,37 +44,35 @@ impl CellFilterOperation<GridTextFilter> for RichTextTypeOption {
     }
 }
 
-impl CellDataOperation<String> for RichTextTypeOption {
-    fn decode_cell_data<T>(
+impl CellDataOperation<String, String> for RichTextTypeOption {
+    fn decode_cell_data(
         &self,
-        cell_data: T,
+        cell_data: CellData<String>,
         decoded_field_type: &FieldType,
         field_rev: &FieldRevision,
-    ) -> FlowyResult<DecodedCellData>
-    where
-        T: Into<String>,
-    {
+    ) -> FlowyResult<DecodedCellData> {
         if decoded_field_type.is_date()
             || decoded_field_type.is_single_select()
             || decoded_field_type.is_multi_select()
             || decoded_field_type.is_number()
         {
-            try_decode_cell_data(cell_data.into(), field_rev, decoded_field_type, decoded_field_type)
+            try_decode_cell_data(cell_data, field_rev, decoded_field_type, decoded_field_type)
         } else {
-            let cell_data = cell_data.into();
+            let cell_data: String = cell_data.try_into_inner()?;
             Ok(DecodedCellData::new(cell_data))
         }
     }
 
-    fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
-    where
-        C: Into<CellContentChangeset>,
-    {
-        let data = changeset.into();
+    fn apply_changeset(
+        &self,
+        changeset: CellDataChangeset<String>,
+        _cell_rev: Option<CellRevision>,
+    ) -> Result<String, FlowyError> {
+        let data = changeset.try_into_inner()?;
         if data.len() > 10000 {
             Err(FlowyError::text_too_long().context("The len of the text should not be more than 10000"))
         } else {
-            Ok(data.0)
+            Ok(data)
         }
     }
 }
@@ -96,10 +95,10 @@ impl std::convert::TryFrom<AnyCellData> for TextCellData {
 #[cfg(test)]
 mod tests {
     use crate::entities::FieldType;
+    use crate::services::cell::CellDataOperation;
     use crate::services::field::select_option::*;
     use crate::services::field::FieldBuilder;
     use crate::services::field::*;
-    use crate::services::row::CellDataOperation;
 
     #[test]
     fn text_description_test() {
@@ -111,7 +110,7 @@ mod tests {
 
         assert_eq!(
             type_option
-                .decode_cell_data(1647251762.to_string(), &field_type, &date_time_field_rev)
+                .decode_cell_data(1647251762.to_string().into(), &field_type, &date_time_field_rev)
                 .unwrap()
                 .parse::<DateCellData>()
                 .unwrap()
@@ -127,7 +126,11 @@ mod tests {
 
         assert_eq!(
             type_option
-                .decode_cell_data(done_option_id, &FieldType::SingleSelect, &single_select_field_rev)
+                .decode_cell_data(
+                    done_option_id.into(),
+                    &FieldType::SingleSelect,
+                    &single_select_field_rev
+                )
                 .unwrap()
                 .parse::<SelectOptionCellData>()
                 .unwrap()
@@ -139,16 +142,18 @@ mod tests {
         let google_option = SelectOption::new("Google");
         let facebook_option = SelectOption::new("Facebook");
         let ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR);
-        let cell_data_changeset = SelectOptionCellContentChangeset::from_insert(&ids).to_str();
+        let cell_data_changeset = SelectOptionCellChangeset::from_insert(&ids).to_str();
         let multi_select = MultiSelectTypeOptionBuilder::default()
             .option(google_option.clone())
             .option(facebook_option.clone());
         let multi_select_field_rev = FieldBuilder::new(multi_select).build();
         let multi_type_option = MultiSelectTypeOption::from(&multi_select_field_rev);
-        let cell_data = multi_type_option.apply_changeset(cell_data_changeset, None).unwrap();
+        let cell_data = multi_type_option
+            .apply_changeset(cell_data_changeset.into(), None)
+            .unwrap();
         assert_eq!(
             type_option
-                .decode_cell_data(cell_data, &FieldType::MultiSelect, &multi_select_field_rev)
+                .decode_cell_data(cell_data.into(), &FieldType::MultiSelect, &multi_select_field_rev)
                 .unwrap()
                 .parse::<SelectOptionCellData>()
                 .unwrap()
@@ -161,7 +166,7 @@ mod tests {
         let number_field_rev = FieldBuilder::new(number).build();
         assert_eq!(
             type_option
-                .decode_cell_data("18443".to_owned(), &FieldType::Number, &number_field_rev)
+                .decode_cell_data("18443".to_owned().into(), &FieldType::Number, &number_field_rev)
                 .unwrap()
                 .to_string(),
             "$18,443".to_owned()

+ 24 - 35
frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs

@@ -1,9 +1,9 @@
 use crate::entities::{FieldType, GridTextFilter};
 use crate::impl_type_option;
-use crate::services::field::{BoxTypeOptionBuilder, TextCellData, TypeOptionBuilder};
-use crate::services::row::{
-    AnyCellData, CellContentChangeset, CellDataOperation, CellFilterOperation, DecodedCellData, EncodedCellData,
+use crate::services::cell::{
+    AnyCellData, CellData, CellDataChangeset, CellDataOperation, CellFilterOperation, DecodedCellData, FromCellString,
 };
+use crate::services::field::{BoxTypeOptionBuilder, TextCellData, TypeOptionBuilder};
 use bytes::Bytes;
 use fancy_regex::Regex;
 use flowy_derive::ProtoBuf;
@@ -11,7 +11,6 @@ use flowy_error::{internal_error, FlowyError, FlowyResult};
 use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
 use lazy_static::lazy_static;
 use serde::{Deserialize, Serialize};
-use std::str::FromStr;
 
 #[derive(Default)]
 pub struct URLTypeOptionBuilder(URLTypeOption);
@@ -46,35 +45,33 @@ impl CellFilterOperation<GridTextFilter> for URLTypeOption {
     }
 }
 
-impl CellDataOperation<EncodedCellData<URLCellData>> for URLTypeOption {
-    fn decode_cell_data<T>(
+impl CellDataOperation<URLCellData, String> for URLTypeOption {
+    fn decode_cell_data(
         &self,
-        cell_data: T,
+        cell_data: CellData<URLCellData>,
         decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
-    ) -> FlowyResult<DecodedCellData>
-    where
-        T: Into<EncodedCellData<URLCellData>>,
-    {
+    ) -> FlowyResult<DecodedCellData> {
         if !decoded_field_type.is_url() {
             return Ok(DecodedCellData::default());
         }
-        let cell_data = cell_data.into().try_into_inner()?;
+        let cell_data: URLCellData = cell_data.try_into_inner()?;
         DecodedCellData::try_from_bytes(cell_data)
     }
 
-    fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
-    where
-        C: Into<CellContentChangeset>,
-    {
-        let changeset = changeset.into();
+    fn apply_changeset(
+        &self,
+        changeset: CellDataChangeset<String>,
+        _cell_rev: Option<CellRevision>,
+    ) -> Result<String, FlowyError> {
+        let changeset = changeset.try_into_inner()?;
         let mut url = "".to_string();
         if let Ok(Some(m)) = URL_REGEX.find(&changeset) {
             url = auto_append_scheme(m.as_str());
         }
         URLCellData {
             url,
-            content: changeset.to_string(),
+            content: changeset,
         }
         .to_json()
     }
@@ -118,25 +115,17 @@ impl URLCellData {
     }
 }
 
-impl FromStr for URLCellData {
-    type Err = FlowyError;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
+impl FromCellString for URLCellData {
+    fn from_cell_str(s: &str) -> FlowyResult<Self> {
         serde_json::from_str::<URLCellData>(s).map_err(internal_error)
     }
 }
 
-// impl std::convert::From<AnyCellData> for URLCellData {
-//     fn from(any_cell_data: AnyCellData) -> Self {
-//         URLCellData::from_str(&any_cell_data.cell_data).unwrap_or_default()
-//     }
-// }
-
 impl std::convert::TryFrom<AnyCellData> for URLCellData {
-    type Error = ();
+    type Error = FlowyError;
 
-    fn try_from(_value: AnyCellData) -> Result<Self, Self::Error> {
-        todo!()
+    fn try_from(data: AnyCellData) -> Result<Self, Self::Error> {
+        serde_json::from_str::<URLCellData>(&data.cell_data).map_err(internal_error)
     }
 }
 
@@ -150,9 +139,9 @@ lazy_static! {
 #[cfg(test)]
 mod tests {
     use crate::entities::FieldType;
+    use crate::services::cell::{CellData, CellDataOperation};
     use crate::services::field::FieldBuilder;
     use crate::services::field::{URLCellData, URLTypeOption};
-    use crate::services::row::{CellDataOperation, EncodedCellData};
     use flowy_grid_data_model::revision::FieldRevision;
 
     #[test]
@@ -195,20 +184,20 @@ mod tests {
         expected: &str,
         expected_url: &str,
     ) {
-        let encoded_data = type_option.apply_changeset(cell_data, None).unwrap();
+        let encoded_data = type_option.apply_changeset(cell_data.to_owned().into(), None).unwrap();
         let decode_cell_data = decode_cell_data(encoded_data, type_option, field_rev, field_type);
         assert_eq!(expected.to_owned(), decode_cell_data.content);
         assert_eq!(expected_url.to_owned(), decode_cell_data.url);
     }
 
-    fn decode_cell_data<T: Into<EncodedCellData<URLCellData>>>(
+    fn decode_cell_data<T: Into<CellData<URLCellData>>>(
         encoded_data: T,
         type_option: &URLTypeOption,
         field_rev: &FieldRevision,
         field_type: &FieldType,
     ) -> URLCellData {
         type_option
-            .decode_cell_data(encoded_data, field_type, field_rev)
+            .decode_cell_data(encoded_data.into(), field_type, field_rev)
             .unwrap()
             .parse::<URLCellData>()
             .unwrap()

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

@@ -1,4 +1,4 @@
-use crate::services::row::AnyCellData;
+use crate::services::cell::AnyCellData;
 use flowy_grid_data_model::revision::CellRevision;
 use std::str::FromStr;
 

+ 2 - 3
frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs

@@ -1,6 +1,7 @@
 use crate::dart_notification::{send_dart_notification, GridNotification};
 use crate::entities::{FieldType, GridBlockChangeset};
 use crate::services::block_manager::GridBlockManager;
+use crate::services::cell::{AnyCellData, CellFilterOperation};
 use crate::services::field::{
     CheckboxTypeOption, DateTypeOption, MultiSelectTypeOption, NumberTypeOption, RichTextTypeOption,
     SingleSelectTypeOption, URLTypeOption,
@@ -9,15 +10,13 @@ use crate::services::filter::filter_cache::{
     reload_filter_cache, FilterCache, FilterId, FilterResult, FilterResultCache,
 };
 use crate::services::grid_editor_task::GridServiceTaskScheduler;
-use crate::services::row::{AnyCellData, CellFilterOperation, GridBlockSnapshot};
+use crate::services::row::GridBlockSnapshot;
 use crate::services::tasks::{FilterTaskContext, Task, TaskContent};
-
 use flowy_error::FlowyResult;
 use flowy_grid_data_model::revision::{CellRevision, FieldId, FieldRevision, RowRevision};
 use flowy_sync::client_grid::GridRevisionPad;
 use flowy_sync::entities::grid::GridSettingChangesetParams;
 use rayon::prelude::*;
-
 use std::collections::HashMap;
 use std::sync::Arc;
 use tokio::sync::RwLock;

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

@@ -1,13 +1,16 @@
 use crate::dart_notification::{send_dart_notification, GridNotification};
 use crate::entities::CellIdentifier;
+use crate::entities::*;
 use crate::manager::{GridTaskSchedulerRwLock, GridUser};
 use crate::services::block_manager::GridBlockManager;
+use crate::services::cell::{apply_cell_data_changeset, decode_any_cell_data};
 use crate::services::field::{default_type_option_builder_from_type, type_option_builder_from_bytes, FieldBuilder};
 use crate::services::filter::{GridFilterChangeset, GridFilterService};
 use crate::services::persistence::block_index::BlockIndexCache;
-use crate::services::row::*;
-
-use crate::entities::*;
+use crate::services::row::{
+    make_grid_blocks, make_row_from_row_rev, make_row_rev_from_context, make_rows_from_row_revs,
+    CreateRowRevisionBuilder, CreateRowRevisionPayload, GridBlockSnapshot,
+};
 use bytes::Bytes;
 use flowy_error::{ErrorCode, FlowyError, FlowyResult};
 use flowy_grid_data_model::revision::*;
@@ -369,7 +372,7 @@ impl GridRevisionEditor {
 
     #[tracing::instrument(level = "trace", skip_all, err)]
     pub async fn update_cell(&self, cell_changeset: CellChangeset) -> FlowyResult<()> {
-        if cell_changeset.cell_content_changeset.as_ref().is_none() {
+        if cell_changeset.content.as_ref().is_none() {
             return Ok(());
         }
 
@@ -377,7 +380,7 @@ impl GridRevisionEditor {
             grid_id,
             row_id,
             field_id,
-            mut cell_content_changeset,
+            mut content,
         } = cell_changeset;
 
         match self.grid_pad.read().await.get_field_rev(&field_id) {
@@ -386,21 +389,17 @@ impl GridRevisionEditor {
                 Err(FlowyError::internal().context(msg))
             }
             Some((_, field_rev)) => {
-                tracing::trace!("field changeset: id:{} / value:{:?}", &field_id, cell_content_changeset);
+                tracing::trace!("field changeset: id:{} / value:{:?}", &field_id, content);
 
                 let cell_rev = self.get_cell_rev(&row_id, &field_id).await?;
                 // Update the changeset.data property with the return value.
-                cell_content_changeset = Some(apply_cell_data_changeset(
-                    cell_content_changeset.unwrap(),
-                    cell_rev,
-                    field_rev,
-                )?);
+                content = Some(apply_cell_data_changeset(content.unwrap(), cell_rev, field_rev)?);
                 let field_revs = self.get_field_revs(None).await?;
                 let cell_changeset = CellChangeset {
                     grid_id,
                     row_id,
                     field_id,
-                    cell_content_changeset,
+                    content,
                 };
                 let _ = self
                     .block_manager

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

@@ -2,6 +2,7 @@ mod util;
 
 mod block_manager;
 pub mod block_revision_editor;
+pub mod cell;
 pub mod field;
 mod filter;
 pub mod grid_editor;

+ 0 - 305
frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs

@@ -1,305 +0,0 @@
-use crate::entities::FieldType;
-use crate::services::field::*;
-use bytes::Bytes;
-use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
-use flowy_grid_data_model::revision::{CellRevision, FieldRevision, FieldTypeRevision};
-use serde::{Deserialize, Serialize};
-use std::fmt::Formatter;
-use std::str::FromStr;
-
-pub trait CellFilterOperation<T> {
-    fn apply_filter(&self, any_cell_data: AnyCellData, filter: &T) -> FlowyResult<bool>;
-}
-
-pub trait CellDataOperation<D> {
-    fn decode_cell_data<T>(
-        &self,
-        cell_data: T,
-        decoded_field_type: &FieldType,
-        field_rev: &FieldRevision,
-    ) -> FlowyResult<DecodedCellData>
-    where
-        T: Into<D>;
-
-    fn apply_changeset<C: Into<CellContentChangeset>>(
-        &self,
-        changeset: C,
-        cell_rev: Option<CellRevision>,
-    ) -> FlowyResult<String>;
-}
-
-#[derive(Debug)]
-pub struct CellContentChangeset(pub String);
-
-impl std::fmt::Display for CellContentChangeset {
-    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{}", &self.0)
-    }
-}
-
-impl<T: AsRef<str>> std::convert::From<T> for CellContentChangeset {
-    fn from(s: T) -> Self {
-        let s = s.as_ref().to_owned();
-        CellContentChangeset(s)
-    }
-}
-
-impl std::ops::Deref for CellContentChangeset {
-    type Target = str;
-
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct AnyCellData {
-    pub cell_data: String,
-    pub field_type: FieldType,
-}
-
-impl std::str::FromStr for AnyCellData {
-    type Err = FlowyError;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        let type_option_cell_data: AnyCellData = serde_json::from_str(s)?;
-        Ok(type_option_cell_data)
-    }
-}
-
-impl std::convert::TryInto<AnyCellData> for String {
-    type Error = FlowyError;
-
-    fn try_into(self) -> Result<AnyCellData, Self::Error> {
-        AnyCellData::from_str(&self)
-    }
-}
-
-impl std::convert::TryFrom<&CellRevision> for AnyCellData {
-    type Error = FlowyError;
-
-    fn try_from(value: &CellRevision) -> Result<Self, Self::Error> {
-        Self::from_str(&value.data)
-    }
-}
-
-impl std::convert::TryFrom<&Option<CellRevision>> for AnyCellData {
-    type Error = FlowyError;
-
-    fn try_from(value: &Option<CellRevision>) -> Result<Self, Self::Error> {
-        match value {
-            None => Err(FlowyError::invalid_data().context("Expected CellRevision, but receive None")),
-            Some(cell_rev) => AnyCellData::try_from(cell_rev),
-        }
-    }
-}
-
-impl std::convert::TryFrom<Option<CellRevision>> for AnyCellData {
-    type Error = FlowyError;
-
-    fn try_from(value: Option<CellRevision>) -> Result<Self, Self::Error> {
-        Self::try_from(&value)
-    }
-}
-
-impl AnyCellData {
-    pub fn new(content: String, field_type: FieldType) -> Self {
-        AnyCellData {
-            cell_data: content,
-            field_type,
-        }
-    }
-
-    pub fn json(&self) -> String {
-        serde_json::to_string(self).unwrap_or_else(|_| "".to_owned())
-    }
-
-    pub fn is_number(&self) -> bool {
-        self.field_type == FieldType::Number
-    }
-
-    pub fn is_text(&self) -> bool {
-        self.field_type == FieldType::RichText
-    }
-
-    pub fn is_checkbox(&self) -> bool {
-        self.field_type == FieldType::Checkbox
-    }
-
-    pub fn is_date(&self) -> bool {
-        self.field_type == FieldType::DateTime
-    }
-
-    pub fn is_single_select(&self) -> bool {
-        self.field_type == FieldType::SingleSelect
-    }
-
-    pub fn is_multi_select(&self) -> bool {
-        self.field_type == FieldType::MultiSelect
-    }
-
-    pub fn is_url(&self) -> bool {
-        self.field_type == FieldType::URL
-    }
-
-    pub fn is_select_option(&self) -> bool {
-        self.field_type == FieldType::MultiSelect || self.field_type == FieldType::SingleSelect
-    }
-}
-
-/// The changeset will be deserialized into specific data base on the FieldType.
-/// For example, it's String on FieldType::RichText, and SelectOptionChangeset on FieldType::SingleSelect
-pub fn apply_cell_data_changeset<C: Into<CellContentChangeset>, T: AsRef<FieldRevision>>(
-    changeset: C,
-    cell_rev: Option<CellRevision>,
-    field_rev: T,
-) -> Result<String, FlowyError> {
-    let field_rev = field_rev.as_ref();
-    let field_type = field_rev.field_type_rev.into();
-    let s = match field_type {
-        FieldType::RichText => RichTextTypeOption::from(field_rev).apply_changeset(changeset, cell_rev),
-        FieldType::Number => NumberTypeOption::from(field_rev).apply_changeset(changeset, cell_rev),
-        FieldType::DateTime => DateTypeOption::from(field_rev).apply_changeset(changeset, cell_rev),
-        FieldType::SingleSelect => SingleSelectTypeOption::from(field_rev).apply_changeset(changeset, cell_rev),
-        FieldType::MultiSelect => MultiSelectTypeOption::from(field_rev).apply_changeset(changeset, cell_rev),
-        FieldType::Checkbox => CheckboxTypeOption::from(field_rev).apply_changeset(changeset, cell_rev),
-        FieldType::URL => URLTypeOption::from(field_rev).apply_changeset(changeset, cell_rev),
-    }?;
-
-    Ok(AnyCellData::new(s, field_type).json())
-}
-
-pub fn decode_any_cell_data<T: TryInto<AnyCellData>>(data: T, field_rev: &FieldRevision) -> DecodedCellData {
-    if let Ok(any_cell_data) = data.try_into() {
-        let AnyCellData { cell_data, field_type } = any_cell_data;
-        let to_field_type = field_rev.field_type_rev.into();
-        match try_decode_cell_data(cell_data, field_rev, &field_type, &to_field_type) {
-            Ok(cell_data) => cell_data,
-            Err(e) => {
-                tracing::error!("Decode cell data failed, {:?}", e);
-                DecodedCellData::default()
-            }
-        }
-    } else {
-        tracing::error!("Decode type option data failed");
-        DecodedCellData::default()
-    }
-}
-
-pub fn try_decode_cell_data(
-    cell_data: String,
-    field_rev: &FieldRevision,
-    s_field_type: &FieldType,
-    t_field_type: &FieldType,
-) -> FlowyResult<DecodedCellData> {
-    let get_cell_data = || {
-        let field_type: FieldTypeRevision = t_field_type.into();
-        let data = match t_field_type {
-            FieldType::RichText => field_rev
-                .get_type_option_entry::<RichTextTypeOption>(field_type)?
-                .decode_cell_data(cell_data, s_field_type, field_rev),
-            FieldType::Number => field_rev
-                .get_type_option_entry::<NumberTypeOption>(field_type)?
-                .decode_cell_data(cell_data, s_field_type, field_rev),
-            FieldType::DateTime => field_rev
-                .get_type_option_entry::<DateTypeOption>(field_type)?
-                .decode_cell_data(cell_data, s_field_type, field_rev),
-            FieldType::SingleSelect => field_rev
-                .get_type_option_entry::<SingleSelectTypeOption>(field_type)?
-                .decode_cell_data(cell_data, s_field_type, field_rev),
-            FieldType::MultiSelect => field_rev
-                .get_type_option_entry::<MultiSelectTypeOption>(field_type)?
-                .decode_cell_data(cell_data, s_field_type, field_rev),
-            FieldType::Checkbox => field_rev
-                .get_type_option_entry::<CheckboxTypeOption>(field_type)?
-                .decode_cell_data(cell_data, s_field_type, field_rev),
-            FieldType::URL => field_rev
-                .get_type_option_entry::<URLTypeOption>(field_type)?
-                .decode_cell_data(cell_data, s_field_type, field_rev),
-        };
-        Some(data)
-    };
-
-    match get_cell_data() {
-        Some(Ok(data)) => Ok(data),
-        Some(Err(err)) => {
-            tracing::error!("{:?}", err);
-            Ok(DecodedCellData::default())
-        }
-        None => Ok(DecodedCellData::default()),
-    }
-}
-
-pub(crate) struct EncodedCellData<T>(pub Option<T>);
-
-impl<T> EncodedCellData<T> {
-    pub fn try_into_inner(self) -> FlowyResult<T> {
-        match self.0 {
-            None => Err(ErrorCode::InvalidData.into()),
-            Some(data) => Ok(data),
-        }
-    }
-}
-
-impl<T> std::convert::From<String> for EncodedCellData<T>
-where
-    T: FromStr<Err = FlowyError>,
-{
-    fn from(s: String) -> Self {
-        match T::from_str(&s) {
-            Ok(inner) => EncodedCellData(Some(inner)),
-            Err(e) => {
-                tracing::error!("Deserialize Cell Data failed: {}", e);
-                EncodedCellData(None)
-            }
-        }
-    }
-}
-
-/// The data is encoded by protobuf or utf8. You should choose the corresponding decode struct to parse it.
-///
-/// 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 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)]
-pub struct DecodedCellData {
-    pub data: Vec<u8>,
-}
-
-impl DecodedCellData {
-    pub fn new<T: AsRef<[u8]>>(data: T) -> Self {
-        Self {
-            data: data.as_ref().to_vec(),
-        }
-    }
-
-    pub fn try_from_bytes<T: TryInto<Bytes>>(bytes: T) -> FlowyResult<Self>
-    where
-        <T as TryInto<Bytes>>::Error: std::fmt::Debug,
-    {
-        let bytes = bytes.try_into().map_err(internal_error)?;
-        Ok(Self { data: bytes.to_vec() })
-    }
-
-    pub fn parse<'a, T: TryFrom<&'a [u8]>>(&'a self) -> FlowyResult<T>
-    where
-        <T as TryFrom<&'a [u8]>>::Error: std::fmt::Debug,
-    {
-        T::try_from(self.data.as_ref()).map_err(internal_error)
-    }
-}
-
-impl ToString for DecodedCellData {
-    fn to_string(&self) -> String {
-        match String::from_utf8(self.data.clone()) {
-            Ok(s) => s,
-            Err(e) => {
-                tracing::error!("DecodedCellData to string failed: {:?}", e);
-                "".to_string()
-            }
-        }
-    }
-}

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

@@ -1,7 +1,5 @@
-mod cell_data_operation;
 mod row_builder;
 mod row_loader;
 
-pub use cell_data_operation::*;
 pub use row_builder::*;
 pub(crate) use row_loader::*;

+ 5 - 5
frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs

@@ -1,5 +1,5 @@
-use crate::services::field::select_option::SelectOptionCellContentChangeset;
-use crate::services::row::apply_cell_data_changeset;
+use crate::services::cell::apply_cell_data_changeset;
+use crate::services::field::select_option::SelectOptionCellChangeset;
 use flowy_error::{FlowyError, FlowyResult};
 use flowy_grid_data_model::revision::{gen_row_id, CellRevision, FieldRevision, RowRevision, DEFAULT_ROW_HEIGHT};
 use indexmap::IndexMap;
@@ -35,7 +35,7 @@ impl<'a> CreateRowRevisionBuilder<'a> {
                 Err(FlowyError::internal().context(msg))
             }
             Some(field_rev) => {
-                let data = apply_cell_data_changeset(&data, None, field_rev)?;
+                let data = apply_cell_data_changeset(data, None, field_rev)?;
                 let cell = CellRevision::new(data);
                 self.payload.cell_by_field_id.insert(field_id.to_owned(), cell);
                 Ok(())
@@ -50,8 +50,8 @@ impl<'a> CreateRowRevisionBuilder<'a> {
                 Err(FlowyError::internal().context(msg))
             }
             Some(field_rev) => {
-                let cell_data = SelectOptionCellContentChangeset::from_insert(&data).to_str();
-                let data = apply_cell_data_changeset(&cell_data, None, field_rev)?;
+                let cell_data = SelectOptionCellChangeset::from_insert(&data).to_str();
+                let data = apply_cell_data_changeset(cell_data, None, field_rev)?;
                 let cell = CellRevision::new(data);
                 self.payload.cell_by_field_id.insert(field_id.to_owned(), cell);
                 Ok(())

+ 4 - 4
frontend/rust-lib/flowy-grid/tests/grid/cell_test.rs

@@ -2,7 +2,7 @@ use crate::grid::field_util::make_date_cell_string;
 use crate::grid::script::EditorScript::*;
 use crate::grid::script::*;
 use flowy_grid::entities::{CellChangeset, FieldType};
-use flowy_grid::services::field::select_option::SelectOptionCellContentChangeset;
+use flowy_grid::services::field::select_option::SelectOptionCellChangeset;
 use flowy_grid::services::field::{MultiSelectTypeOption, SingleSelectTypeOption};
 
 #[tokio::test]
@@ -25,11 +25,11 @@ async fn grid_cell_update() {
                 FieldType::DateTime => make_date_cell_string("123"),
                 FieldType::SingleSelect => {
                     let type_option = SingleSelectTypeOption::from(field_rev);
-                    SelectOptionCellContentChangeset::from_insert(&type_option.options.first().unwrap().id).to_str()
+                    SelectOptionCellChangeset::from_insert(&type_option.options.first().unwrap().id).to_str()
                 }
                 FieldType::MultiSelect => {
                     let type_option = MultiSelectTypeOption::from(field_rev);
-                    SelectOptionCellContentChangeset::from_insert(&type_option.options.first().unwrap().id).to_str()
+                    SelectOptionCellChangeset::from_insert(&type_option.options.first().unwrap().id).to_str()
                 }
                 FieldType::Checkbox => "1".to_string(),
                 FieldType::URL => "1".to_string(),
@@ -40,7 +40,7 @@ async fn grid_cell_update() {
                     grid_id: block_id.to_string(),
                     row_id: row_rev.id.clone(),
                     field_id: field_rev.id.clone(),
-                    cell_content_changeset: Some(data),
+                    content: Some(data),
                 },
                 is_err: false,
             });

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

@@ -73,7 +73,7 @@ pub fn create_single_select_field(grid_id: &str) -> (InsertFieldParams, FieldRev
 //  The grid will contains all existing field types and there are three empty rows in this grid.
 
 pub fn make_date_cell_string(s: &str) -> String {
-    serde_json::to_string(&DateCellContentChangeset {
+    serde_json::to_string(&DateCellChangeset {
         date: Some(s.to_string()),
         time: None,
     })

+ 2 - 1
frontend/rust-lib/flowy-grid/tests/grid/row_test.rs

@@ -4,9 +4,10 @@ use crate::grid::script::EditorScript::*;
 use crate::grid::script::*;
 use chrono::NaiveDateTime;
 use flowy_grid::entities::FieldType;
+use flowy_grid::services::cell::decode_any_cell_data;
 use flowy_grid::services::field::select_option::SELECTION_IDS_SEPARATOR;
 use flowy_grid::services::field::{DateCellData, MultiSelectTypeOption, SingleSelectTypeOption};
-use flowy_grid::services::row::{decode_any_cell_data, CreateRowRevisionBuilder};
+use flowy_grid::services::row::CreateRowRevisionBuilder;
 use flowy_grid_data_model::revision::RowMetaChangeset;
 
 #[tokio::test]

+ 2 - 2
frontend/rust-lib/flowy-grid/tests/grid/row_util.rs

@@ -1,6 +1,6 @@
 use crate::grid::script::GridEditorTest;
 use flowy_grid::entities::FieldType;
-use flowy_grid::services::field::DateCellContentChangeset;
+use flowy_grid::services::field::DateCellChangeset;
 use flowy_grid::services::row::{CreateRowRevisionBuilder, CreateRowRevisionPayload};
 use flowy_grid_data_model::revision::FieldRevision;
 use strum::EnumCount;
@@ -33,7 +33,7 @@ impl<'a> GridRowTestBuilder<'a> {
 
     #[allow(dead_code)]
     pub fn update_date_cell(mut self, value: i64) -> Self {
-        let value = serde_json::to_string(&DateCellContentChangeset {
+        let value = serde_json::to_string(&DateCellChangeset {
             date: Some(value.to_string()),
             time: None,
         })