refactor: provide default type option transform (#1579)

This commit is contained in:
Nathan.fooo 2022-12-17 18:12:47 +08:00 committed by GitHub
parent 85e489babb
commit 67e350e797
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 253 additions and 144 deletions

View File

@ -9,24 +9,6 @@ pub trait TypeOptionBuilder {
/// Returns a serializer that can be used to serialize the type-option data
fn serializer(&self) -> &dyn TypeOptionDataSerializer;
/// Transform the data from passed-in type-option to current type-option
///
/// The current type-option data may be changed if it supports transform
/// the data from the other kind of type-option data.
///
/// For example, when switching from `checkbox` type-option to `single-select`
/// type-option, adding the `Yes` option if the `single-select` type-option doesn't contain it.
/// But the cell content is a string, `Yes`, it's need to do the cell content transform.
/// The `Yes` string will be transformed to the `Yes` option id.
///
///
/// # Arguments
///
/// * `field_type`: represents as the field type of the passed-in type-option data
/// * `type_option_data`: passed-in type-option data
//
fn transform(&mut self, field_type: &FieldType, type_option_data: String);
}
pub fn default_type_option_builder_from_type(field_type: &FieldType) -> Box<dyn TypeOptionBuilder> {

View File

@ -3,6 +3,7 @@ use crate::impl_type_option;
use crate::services::cell::{AnyCellChangeset, CellDataChangeset, CellDataDecoder, FromCellString};
use crate::services::field::{
BoxTypeOptionBuilder, CheckboxCellData, TypeOption, TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration,
TypeOptionTransform,
};
use bytes::Bytes;
use flowy_derive::ProtoBuf;
@ -31,10 +32,6 @@ impl TypeOptionBuilder for CheckboxTypeOptionBuilder {
fn serializer(&self) -> &dyn TypeOptionDataSerializer {
&self.0
}
fn transform(&mut self, _field_type: &FieldType, _type_option_data: String) {
// Do nothing
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, ProtoBuf)]
@ -47,15 +44,17 @@ impl_type_option!(CheckboxTypeOptionPB, FieldType::Checkbox);
impl TypeOption for CheckboxTypeOptionPB {
type CellData = CheckboxCellData;
type CellChangeset = CheckboxCellChangeset;
type CellPBType = CheckboxCellData;
type CellProtobufType = CheckboxCellData;
}
impl TypeOptionTransform for CheckboxTypeOptionPB {}
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 {
fn convert_to_protobuf(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellProtobufType {
cell_data
}

View File

@ -165,7 +165,7 @@ mod tests {
let decoded_data = type_option
.try_decode_cell_data(encoded_data, &FieldType::DateTime, field_rev)
.unwrap();
let decoded_data = type_option.convert_into_pb_type(decoded_data);
let decoded_data = type_option.convert_to_protobuf(decoded_data);
if type_option.include_time {
format!("{} {}", decoded_data.date, decoded_data.time)
.trim_end()

View File

@ -3,7 +3,7 @@ use crate::impl_type_option;
use crate::services::cell::{AnyCellChangeset, CellDataChangeset, CellDataDecoder, FromCellString};
use crate::services::field::{
BoxTypeOptionBuilder, DateCellChangeset, DateCellData, DateCellDataPB, DateFormat, TimeFormat, TypeOption,
TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration,
TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration, TypeOptionTransform,
};
use bytes::Bytes;
use chrono::format::strftime::StrftimeItems;
@ -30,7 +30,7 @@ impl_type_option!(DateTypeOptionPB, FieldType::DateTime);
impl TypeOption for DateTypeOptionPB {
type CellData = DateCellData;
type CellChangeset = DateCellChangeset;
type CellPBType = DateCellDataPB;
type CellProtobufType = DateCellDataPB;
}
impl TypeOptionConfiguration for DateTypeOptionPB {
@ -38,7 +38,7 @@ impl TypeOptionConfiguration for DateTypeOptionPB {
}
impl TypeOptionCellData for DateTypeOptionPB {
fn convert_into_pb_type(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellPBType {
fn convert_to_protobuf(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellProtobufType {
self.today_desc_from_timestamp(cell_data)
}
@ -128,6 +128,8 @@ impl DateTypeOptionPB {
}
}
impl TypeOptionTransform for DateTypeOptionPB {}
impl CellDataDecoder for DateTypeOptionPB {
fn try_decode_cell_data(
&self,
@ -207,7 +209,4 @@ impl TypeOptionBuilder for DateTypeOptionBuilder {
fn serializer(&self) -> &dyn TypeOptionDataSerializer {
&self.0
}
fn transform(&mut self, _field_type: &FieldType, _type_option_data: String) {
// Do nothing
}
}

View File

@ -4,7 +4,7 @@ use crate::services::cell::{AnyCellChangeset, CellComparable, CellDataChangeset,
use crate::services::field::type_options::number_type_option::format::*;
use crate::services::field::{
BoxTypeOptionBuilder, NumberCellData, StrCellData, TypeOption, TypeOptionBuilder, TypeOptionCellData,
TypeOptionConfiguration,
TypeOptionConfiguration, TypeOptionTransform,
};
use bytes::Bytes;
use flowy_derive::ProtoBuf;
@ -51,9 +51,6 @@ impl TypeOptionBuilder for NumberTypeOptionBuilder {
fn serializer(&self) -> &dyn TypeOptionDataSerializer {
&self.0
}
fn transform(&mut self, _field_type: &FieldType, _type_option_data: String) {
// Do nothing
}
}
// Number
@ -79,7 +76,7 @@ impl_type_option!(NumberTypeOptionPB, FieldType::Number);
impl TypeOption for NumberTypeOptionPB {
type CellData = StrCellData;
type CellChangeset = NumberCellChangeset;
type CellPBType = StrCellData;
type CellProtobufType = StrCellData;
}
impl TypeOptionConfiguration for NumberTypeOptionPB {
@ -87,7 +84,7 @@ impl TypeOptionConfiguration for NumberTypeOptionPB {
}
impl TypeOptionCellData for NumberTypeOptionPB {
fn convert_into_pb_type(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellPBType {
fn convert_to_protobuf(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellProtobufType {
cell_data
}
@ -128,6 +125,8 @@ pub(crate) fn strip_currency_symbol<T: ToString>(s: T) -> String {
s
}
impl TypeOptionTransform for NumberTypeOptionPB {}
impl CellDataDecoder for NumberTypeOptionPB {
fn try_decode_cell_data(
&self,

View File

@ -1,7 +1,7 @@
use crate::entities::{ChecklistFilterPB, FieldType};
use crate::impl_type_option;
use crate::services::cell::{AnyCellChangeset, CellDataChangeset, FromCellString, TypeCellData};
use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer;
use crate::services::field::{
BoxTypeOptionBuilder, SelectOptionCellChangeset, SelectOptionCellDataPB, SelectOptionIds, SelectOptionPB,
SelectTypeOptionSharedAction, TypeOption, TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration,
@ -26,7 +26,7 @@ impl_type_option!(ChecklistTypeOptionPB, FieldType::Checklist);
impl TypeOption for ChecklistTypeOptionPB {
type CellData = SelectOptionIds;
type CellChangeset = SelectOptionCellChangeset;
type CellPBType = SelectOptionCellDataPB;
type CellProtobufType = SelectOptionCellDataPB;
}
impl TypeOptionConfiguration for ChecklistTypeOptionPB {
@ -34,7 +34,7 @@ impl TypeOptionConfiguration for ChecklistTypeOptionPB {
}
impl TypeOptionCellData for ChecklistTypeOptionPB {
fn convert_into_pb_type(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellPBType {
fn convert_to_protobuf(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellProtobufType {
self.get_selected_options(cell_data)
}
@ -113,8 +113,4 @@ impl TypeOptionBuilder for ChecklistTypeOptionBuilder {
fn serializer(&self) -> &dyn TypeOptionDataSerializer {
&self.0
}
fn transform(&mut self, field_type: &FieldType, type_option_data: String) {
SelectOptionTypeOptionTransformer::transform_type_option(&mut self.0, field_type, type_option_data)
}
}

View File

@ -1,7 +1,7 @@
use crate::entities::{FieldType, SelectOptionFilterPB};
use crate::impl_type_option;
use crate::services::cell::{AnyCellChangeset, CellDataChangeset, FromCellString, TypeCellData};
use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer;
use crate::services::field::{
BoxTypeOptionBuilder, SelectOptionCellChangeset, SelectOptionCellDataPB, SelectOptionIds, SelectOptionPB,
SelectTypeOptionSharedAction, TypeOption, TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration,
@ -26,7 +26,7 @@ impl_type_option!(MultiSelectTypeOptionPB, FieldType::MultiSelect);
impl TypeOption for MultiSelectTypeOptionPB {
type CellData = SelectOptionIds;
type CellChangeset = SelectOptionCellChangeset;
type CellPBType = SelectOptionCellDataPB;
type CellProtobufType = SelectOptionCellDataPB;
}
impl TypeOptionConfiguration for MultiSelectTypeOptionPB {
@ -34,7 +34,7 @@ impl TypeOptionConfiguration for MultiSelectTypeOptionPB {
}
impl TypeOptionCellData for MultiSelectTypeOptionPB {
fn convert_into_pb_type(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellPBType {
fn convert_to_protobuf(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellProtobufType {
self.get_selected_options(cell_data)
}
@ -119,17 +119,14 @@ impl TypeOptionBuilder for MultiSelectTypeOptionBuilder {
fn serializer(&self) -> &dyn TypeOptionDataSerializer {
&self.0
}
fn transform(&mut self, field_type: &FieldType, type_option_data: String) {
SelectOptionTypeOptionTransformer::transform_type_option(&mut self.0, field_type, type_option_data)
}
}
#[cfg(test)]
mod tests {
use crate::entities::FieldType;
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::{CheckboxTypeOptionBuilder, FieldBuilder, TypeOptionBuilder, TypeOptionTransform};
use crate::services::field::{MultiSelectTypeOptionBuilder, MultiSelectTypeOptionPB};
#[test]
@ -137,13 +134,13 @@ mod tests {
let checkbox_type_option_builder = CheckboxTypeOptionBuilder::default();
let checkbox_type_option_data = checkbox_type_option_builder.serializer().json_str();
let mut multi_select = MultiSelectTypeOptionBuilder::default();
multi_select.transform(&FieldType::Checkbox, checkbox_type_option_data.clone());
debug_assert_eq!(multi_select.0.options.len(), 2);
let mut multi_select = MultiSelectTypeOptionBuilder::default().0;
multi_select.transform_type_option(FieldType::Checkbox, checkbox_type_option_data.clone());
debug_assert_eq!(multi_select.options.len(), 2);
// Already contain the yes/no option. It doesn't need to insert new options
multi_select.transform(&FieldType::Checkbox, checkbox_type_option_data);
debug_assert_eq!(multi_select.0.options.len(), 2);
multi_select.transform_type_option(FieldType::Checkbox, checkbox_type_option_data);
debug_assert_eq!(multi_select.options.len(), 2);
}
#[test]
@ -158,13 +155,13 @@ mod tests {
let singleselect_type_option_data = singleselect_type_option_builder.serializer().json_str();
let mut multi_select = MultiSelectTypeOptionBuilder::default();
multi_select.transform(&FieldType::MultiSelect, singleselect_type_option_data.clone());
debug_assert_eq!(multi_select.0.options.len(), 2);
let mut multi_select = MultiSelectTypeOptionBuilder::default().0;
multi_select.transform_type_option(FieldType::MultiSelect, singleselect_type_option_data.clone());
debug_assert_eq!(multi_select.options.len(), 2);
// Already contain the yes/no option. It doesn't need to insert new options
multi_select.transform(&FieldType::MultiSelect, singleselect_type_option_data);
debug_assert_eq!(multi_select.0.options.len(), 2);
multi_select.transform_type_option(FieldType::MultiSelect, singleselect_type_option_data);
debug_assert_eq!(multi_select.options.len(), 2);
}
// #[test]

View File

@ -4,9 +4,10 @@ use crate::services::cell::{
CellDataDecoder, CellProtobufBlobParser, DecodedCellData, FromCellChangeset, FromCellString,
};
use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer;
use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformHelper;
use crate::services::field::{
ChecklistTypeOptionPB, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB, TypeOption, TypeOptionCellData,
TypeOptionTransform,
};
use bytes::Bytes;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
@ -128,6 +129,31 @@ pub trait SelectTypeOptionSharedAction: TypeOptionDataSerializer + Send + Sync {
fn mut_options(&mut self) -> &mut Vec<SelectOptionPB>;
}
impl<T> TypeOptionTransform for T
where
T: SelectTypeOptionSharedAction + TypeOption<CellData = SelectOptionIds> + TypeOptionDataSerializer,
{
fn transformable(&self) -> bool {
true
}
fn transform_type_option(&mut self, old_type_option_field_type: FieldType, old_type_option_data: String) {
SelectOptionTypeOptionTransformHelper::transform_type_option(
self,
&old_type_option_field_type,
old_type_option_data,
);
}
fn transform_type_option_cell_data(
&self,
cell_data: <Self as TypeOption>::CellData,
decoded_field_type: &FieldType,
) -> <Self as TypeOption>::CellData {
SelectOptionTypeOptionTransformHelper::transform_type_option_cell_data(self, cell_data, decoded_field_type)
}
}
impl<T> CellDataDecoder for T
where
T: SelectTypeOptionSharedAction + TypeOption<CellData = SelectOptionIds> + TypeOptionCellData,
@ -135,16 +161,10 @@ where
fn try_decode_cell_data(
&self,
cell_data: String,
decoded_field_type: &FieldType,
field_rev: &FieldRevision,
_decoded_field_type: &FieldType,
_field_rev: &FieldRevision,
) -> 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,
))
self.decode_type_option_cell_data(cell_data)
}
fn decode_cell_data_to_str(

View File

@ -1,7 +1,7 @@
use crate::entities::{FieldType, SelectOptionFilterPB};
use crate::impl_type_option;
use crate::services::cell::{AnyCellChangeset, CellDataChangeset, FromCellString};
use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer;
use crate::services::field::{
BoxTypeOptionBuilder, SelectOptionCellDataPB, TypeOption, TypeOptionBuilder, TypeOptionCellData,
TypeOptionConfiguration,
@ -29,7 +29,7 @@ impl_type_option!(SingleSelectTypeOptionPB, FieldType::SingleSelect);
impl TypeOption for SingleSelectTypeOptionPB {
type CellData = SelectOptionIds;
type CellChangeset = SelectOptionCellChangeset;
type CellPBType = SelectOptionCellDataPB;
type CellProtobufType = SelectOptionCellDataPB;
}
impl TypeOptionConfiguration for SingleSelectTypeOptionPB {
@ -37,7 +37,7 @@ impl TypeOptionConfiguration for SingleSelectTypeOptionPB {
}
impl TypeOptionCellData for SingleSelectTypeOptionPB {
fn convert_into_pb_type(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellPBType {
fn convert_to_protobuf(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellProtobufType {
self.get_selected_options(cell_data)
}
@ -107,10 +107,6 @@ impl TypeOptionBuilder for SingleSelectTypeOptionBuilder {
fn serializer(&self) -> &dyn TypeOptionDataSerializer {
&self.0
}
fn transform(&mut self, field_type: &FieldType, type_option_data: String) {
SelectOptionTypeOptionTransformer::transform_type_option(&mut self.0, field_type, type_option_data)
}
}
#[cfg(test)]
@ -125,13 +121,13 @@ mod tests {
let checkbox_type_option_builder = CheckboxTypeOptionBuilder::default();
let checkbox_type_option_data = checkbox_type_option_builder.serializer().json_str();
let mut single_select = SingleSelectTypeOptionBuilder::default();
single_select.transform(&FieldType::Checkbox, checkbox_type_option_data.clone());
debug_assert_eq!(single_select.0.options.len(), 2);
let mut single_select = SingleSelectTypeOptionBuilder::default().0;
single_select.transform_type_option(FieldType::Checkbox, checkbox_type_option_data.clone());
debug_assert_eq!(single_select.options.len(), 2);
// Already contain the yes/no option. It doesn't need to insert new options
single_select.transform(&FieldType::Checkbox, checkbox_type_option_data);
debug_assert_eq!(single_select.0.options.len(), 2);
single_select.transform_type_option(FieldType::Checkbox, checkbox_type_option_data);
debug_assert_eq!(single_select.options.len(), 2);
}
#[test]
@ -146,13 +142,13 @@ mod tests {
let multiselect_type_option_data = multiselect_type_option_builder.serializer().json_str();
let mut single_select = SingleSelectTypeOptionBuilder::default();
single_select.transform(&FieldType::MultiSelect, multiselect_type_option_data.clone());
debug_assert_eq!(single_select.0.options.len(), 2);
let mut single_select = SingleSelectTypeOptionBuilder::default().0;
single_select.transform_type_option(FieldType::MultiSelect, multiselect_type_option_data.clone());
debug_assert_eq!(single_select.options.len(), 2);
// Already contain the yes/no option. It doesn't need to insert new options
single_select.transform(&FieldType::MultiSelect, multiselect_type_option_data);
debug_assert_eq!(single_select.0.options.len(), 2);
single_select.transform_type_option(FieldType::MultiSelect, multiselect_type_option_data);
debug_assert_eq!(single_select.options.len(), 2);
}
#[test]

View File

@ -5,24 +5,22 @@ use crate::services::field::{
SingleSelectTypeOptionPB, TypeOption, CHECK, UNCHECK,
};
use grid_rev_model::FieldRevision;
use serde_json;
use grid_rev_model::TypeOptionDataDeserializer;
/// Handles how to transform the cell data when switching between different field types
pub struct SelectOptionTypeOptionTransformer();
impl SelectOptionTypeOptionTransformer {
pub(crate) struct SelectOptionTypeOptionTransformHelper();
impl SelectOptionTypeOptionTransformHelper {
/// Transform the TypeOptionData from 'field_type' to single select option type.
///
/// # Arguments
///
/// * `field_type`: the FieldType of the passed-in TypeOptionData
/// * `type_option_data`: the data that can be parsed into corresponding TypeOptionData.
/// * `old_field_type`: the FieldType of the passed-in TypeOptionData
///
pub fn transform_type_option<T>(shared: &mut T, field_type: &FieldType, _type_option_data: String)
pub fn transform_type_option<T>(shared: &mut T, old_field_type: &FieldType, old_type_option_data: String)
where
T: SelectTypeOptionSharedAction,
T: SelectTypeOptionSharedAction + TypeOption<CellData = SelectOptionIds>,
{
match field_type {
match old_field_type {
FieldType::Checkbox => {
//add Yes and No options if it does not exist.
if !shared.options().iter().any(|option| option.name == CHECK) {
@ -36,16 +34,16 @@ impl SelectOptionTypeOptionTransformer {
}
}
FieldType::MultiSelect => {
let option_pb: MultiSelectTypeOptionPB = serde_json::from_str(_type_option_data.as_str()).unwrap();
option_pb.options.iter().for_each(|new_option| {
let options = MultiSelectTypeOptionPB::from_json_str(&old_type_option_data).options;
options.iter().for_each(|new_option| {
if !shared.options().iter().any(|option| option.name == new_option.name) {
shared.mut_options().push(new_option.clone());
}
})
}
FieldType::SingleSelect => {
let option_pb: SingleSelectTypeOptionPB = serde_json::from_str(_type_option_data.as_str()).unwrap();
option_pb.options.iter().for_each(|new_option| {
let options = SingleSelectTypeOptionPB::from_json_str(&old_type_option_data).options;
options.iter().for_each(|new_option| {
if !shared.options().iter().any(|option| option.name == new_option.name) {
shared.mut_options().push(new_option.clone());
}
@ -59,7 +57,6 @@ impl SelectOptionTypeOptionTransformer {
shared: &T,
cell_data: <T as TypeOption>::CellData,
decoded_field_type: &FieldType,
_field_rev: &FieldRevision,
) -> <T as TypeOption>::CellData
where
T: SelectTypeOptionSharedAction + TypeOption<CellData = SelectOptionIds>,
@ -75,7 +72,6 @@ impl SelectOptionTypeOptionTransformer {
transformed_ids.push(option.id.clone());
}
});
SelectOptionIds::from(transformed_ids)
}
_ => SelectOptionIds::from(vec![]),

View File

@ -6,6 +6,7 @@ use crate::services::cell::{
};
use crate::services::field::{
BoxTypeOptionBuilder, TypeOption, TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration,
TypeOptionTransform,
};
use bytes::Bytes;
use flowy_derive::ProtoBuf;
@ -28,9 +29,6 @@ impl TypeOptionBuilder for RichTextTypeOptionBuilder {
fn serializer(&self) -> &dyn TypeOptionDataSerializer {
&self.0
}
fn transform(&mut self, _field_type: &FieldType, _type_option_data: String) {
// Do nothing
}
}
/// For the moment, the `RichTextTypeOptionPB` is empty. The `data` property is not
@ -46,15 +44,17 @@ impl_type_option!(RichTextTypeOptionPB, FieldType::RichText);
impl TypeOption for RichTextTypeOptionPB {
type CellData = StrCellData;
type CellChangeset = String;
type CellPBType = StrCellData;
type CellProtobufType = StrCellData;
}
impl TypeOptionTransform for RichTextTypeOptionPB {}
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 {
fn convert_to_protobuf(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellProtobufType {
cell_data
}

View File

@ -6,20 +6,112 @@ use crate::services::field::{
};
use bytes::Bytes;
use flowy_error::FlowyResult;
use grid_rev_model::FieldRevision;
use grid_rev_model::{FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer};
use protobuf::ProtobufError;
use std::fmt::Debug;
pub trait TypeOption {
/// `CellData` represents as the decoded model for current type option. Each of them impl the
/// `FromCellString` and `Default` trait. If the cell string can not be decoded into the specified
/// cell data type then the default value will be returned.
/// For example:
/// FieldType::Checkbox => CheckboxCellData
/// FieldType::Date => DateCellData
/// FieldType::URL => URLCellData
///
/// Uses `StrCellData` for any `TypeOption` if their cell data is pure `String`.
///
type CellData: FromCellString + Default;
///
type CellChangeset;
type CellPBType: TryInto<Bytes, Error = ProtobufError> + Debug;
/// For the moment, the protobuf type only be used in the FFI of `Dart`. If the decoded cell
/// struct is just a `String`, then use the `StrCellData` as its `CellProtobufType`.
/// Otherwise, providing a custom protobuf type as its `CellProtobufType`.
/// For example:
/// FieldType::Date => DateCellDataPB
/// FieldType::URL => URLCellDataPB
///
type CellProtobufType: TryInto<Bytes, Error = ProtobufError> + Debug;
}
pub trait TypeOptionCellData: TypeOption {
/// Convert the decoded cell data into corresponding `Protobuf struct`.
/// For example:
/// FieldType::URL => URLCellDataPB
/// FieldType::Date=> DateCellDataPB
fn convert_to_protobuf(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellProtobufType;
/// 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>;
}
pub trait TypeOptionConfiguration {
type CellFilterConfiguration;
}
pub trait TypeOptionTransform: TypeOption {
/// Returns true if the current `TypeOption` provides custom type option transformation
fn transformable(&self) -> bool {
false
}
/// Transform the TypeOption from one field type to another
/// For example, when switching from `checkbox` type-option to `single-select`
/// type-option, adding the `Yes` option if the `single-select` type-option doesn't contain it.
/// But the cell content is a string, `Yes`, it's need to do the cell content transform.
/// The `Yes` string will be transformed to the `Yes` option id.
///
/// # Arguments
///
/// * `old_type_option_field_type`: the FieldType of the passed-in TypeOption
/// * `old_type_option_data`: the data that can be parsed into corresponding `TypeOption`.
///
///
fn transform_type_option(&mut self, _old_type_option_field_type: FieldType, _old_type_option_data: String) {}
/// Transform the cell data from one field type to another
///
/// # Arguments
///
/// * `cell_data`: the cell data of the current field type
/// * `decoded_field_type`: the field type of the cell data that's going to be transformed into.
///
fn transform_type_option_cell_data(
&self,
cell_data: <Self as TypeOption>::CellData,
_decoded_field_type: &FieldType,
) -> <Self as TypeOption>::CellData {
// Do nothing, just return the passed-in cell data
cell_data
}
}
pub trait TypeOptionTransformHandler {
fn transform(&mut self, old_type_option_field_type: FieldType, old_type_option_data: String);
fn json_str(&self) -> String;
}
impl<T> TypeOptionTransformHandler for T
where
T: TypeOptionTransform + TypeOptionDataSerializer,
{
fn transform(&mut self, old_type_option_field_type: FieldType, old_type_option_data: String) {
if self.transformable() {
self.transform_type_option(old_type_option_field_type, old_type_option_data)
}
}
fn json_str(&self) -> String {
self.json_str()
}
}
pub trait TypeOptionCellDataHandler {
fn handle_cell_data(
&self,
@ -31,24 +123,9 @@ pub trait TypeOptionCellDataHandler {
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,
T: TypeOption + CellDataDecoder + CellDataChangeset + TypeOptionCellData + TypeOptionTransform,
{
fn handle_cell_data(
&self,
@ -56,8 +133,11 @@ where
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))
let mut cell_data = self.try_decode_cell_data(cell_data, field_type, field_rev)?;
if self.transformable() {
cell_data = self.transform_type_option_cell_data(cell_data, field_type);
}
CellProtobufBlob::from(self.convert_to_protobuf(cell_data))
}
fn stringify_cell_data(
@ -117,3 +197,48 @@ impl<'a> FieldRevisionExt<'a> {
}
}
}
pub fn transform_type_option(
type_option_data: &str,
new_field_type: &FieldType,
old_type_option_data: Option<String>,
old_field_type: FieldType,
) -> String {
let mut transform_handler = get_type_option_transform_handler(type_option_data, new_field_type);
if let Some(old_type_option_data) = old_type_option_data {
transform_handler.transform(old_field_type, old_type_option_data);
}
transform_handler.json_str()
}
pub fn get_type_option_transform_handler(
type_option_data: &str,
field_type: &FieldType,
) -> Box<dyn TypeOptionTransformHandler> {
match field_type {
FieldType::RichText => {
Box::new(RichTextTypeOptionPB::from_json_str(type_option_data)) as Box<dyn TypeOptionTransformHandler>
}
FieldType::Number => {
Box::new(NumberTypeOptionPB::from_json_str(type_option_data)) as Box<dyn TypeOptionTransformHandler>
}
FieldType::DateTime => {
Box::new(DateTypeOptionPB::from_json_str(type_option_data)) as Box<dyn TypeOptionTransformHandler>
}
FieldType::SingleSelect => {
Box::new(SingleSelectTypeOptionPB::from_json_str(type_option_data)) as Box<dyn TypeOptionTransformHandler>
}
FieldType::MultiSelect => {
Box::new(MultiSelectTypeOptionPB::from_json_str(type_option_data)) as Box<dyn TypeOptionTransformHandler>
}
FieldType::Checkbox => {
Box::new(CheckboxTypeOptionPB::from_json_str(type_option_data)) as Box<dyn TypeOptionTransformHandler>
}
FieldType::URL => {
Box::new(URLTypeOptionPB::from_json_str(type_option_data)) as Box<dyn TypeOptionTransformHandler>
}
FieldType::Checklist => {
Box::new(ChecklistTypeOptionPB::from_json_str(type_option_data)) as Box<dyn TypeOptionTransformHandler>
}
}
}

View File

@ -2,8 +2,8 @@ use crate::entities::{FieldType, TextFilterPB};
use crate::impl_type_option;
use crate::services::cell::{AnyCellChangeset, CellDataChangeset, CellDataDecoder, FromCellString};
use crate::services::field::{
BoxTypeOptionBuilder, TypeOption, TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration, URLCellData,
URLCellDataPB,
BoxTypeOptionBuilder, TypeOption, TypeOptionBuilder, TypeOptionCellData, TypeOptionConfiguration,
TypeOptionTransform, URLCellData, URLCellDataPB,
};
use bytes::Bytes;
use fancy_regex::Regex;
@ -26,10 +26,6 @@ impl TypeOptionBuilder for URLTypeOptionBuilder {
fn serializer(&self) -> &dyn TypeOptionDataSerializer {
&self.0
}
fn transform(&mut self, _field_type: &FieldType, _type_option_data: String) {
// Do nothing
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, ProtoBuf)]
@ -42,15 +38,17 @@ impl_type_option!(URLTypeOptionPB, FieldType::URL);
impl TypeOption for URLTypeOptionPB {
type CellData = URLCellData;
type CellChangeset = URLCellChangeset;
type CellPBType = URLCellDataPB;
type CellProtobufType = URLCellDataPB;
}
impl TypeOptionTransform for URLTypeOptionPB {}
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 {
fn convert_to_protobuf(&self, cell_data: <Self as TypeOption>::CellData) -> <Self as TypeOption>::CellProtobufType {
cell_data.into()
}

View File

@ -5,8 +5,7 @@ use crate::manager::GridUser;
use crate::services::block_manager::GridBlockManager;
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,
default_type_option_builder_from_type, transform_type_option, type_option_builder_from_bytes, FieldBuilder,
};
use crate::services::filter::FilterType;
@ -280,11 +279,13 @@ impl GridRevisionEditor {
let type_option_transform =
|old_field_type: FieldTypeRevision, old_type_option: Option<String>, new_type_option: String| {
let old_field_type: FieldType = old_field_type.into();
let mut type_option_builder = type_option_builder_from_json_str(&new_type_option, new_field_type);
if let Some(old_type_option) = old_type_option {
type_option_builder.transform(&old_field_type, old_type_option)
}
type_option_builder.serializer().json_str()
// let mut type_option_builder = type_option_builder_from_json_str(&new_type_option, new_field_type);
// if let Some(old_type_option) = old_type_option {
// type_option_builder.transform(&old_field_type, old_type_option)
// }
// type_option_builder.serializer().json_str()
transform_type_option(&new_type_option, new_field_type, old_type_option, old_field_type)
};
let _ = self

View File

@ -3,6 +3,7 @@ use bytes::Bytes;
use indexmap::IndexMap;
use nanoid::nanoid;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use std::sync::Arc;
pub fn gen_grid_id() -> String {