use crate::entities::FieldType; use crate::services::cell::{AnyCellData, CellBytes}; use crate::services::field::*; use std::fmt::Debug; use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_grid_data_model::revision::{CellRevision, FieldRevision, FieldTypeRevision}; /// This trait is used when doing filter/search on the grid. pub trait CellFilterOperation { /// Return true if any_cell_data match the filter condition. fn apply_filter(&self, any_cell_data: AnyCellData, filter: &T) -> FlowyResult; } pub trait CellGroupOperation { fn apply_group(&self, any_cell_data: AnyCellData, group_content: &str) -> FlowyResult; } /// Return object that describes the cell. pub trait CellDisplayable { fn display_data( &self, cell_data: CellData, decoded_field_type: &FieldType, field_rev: &FieldRevision, ) -> FlowyResult; } // CD: Short for CellData. This type is the type return by apply_changeset function. // CS: Short for Changeset. Parse the string into specific Changeset type. pub trait CellDataOperation { /// The cell_data is able to parse into the specific data if CD impl the FromCellData trait. /// For example: /// URLCellData, DateCellData. etc. fn decode_cell_data( &self, cell_data: CellData, decoded_field_type: &FieldType, field_rev: &FieldRevision, ) -> FlowyResult; /// The changeset is able to parse into the specific data if CS impl the FromCellChangeset trait. /// For example: /// SelectOptionCellChangeset,DateCellChangeset. etc. fn apply_changeset(&self, changeset: CellDataChangeset, cell_rev: Option) -> FlowyResult; } /// changeset: It will be deserialized into specific data base on the FieldType. /// For example, /// FieldType::RichText => String /// FieldType::SingleSelect => SelectOptionChangeset /// /// cell_rev: It will be None if the cell does not contain any data. pub fn apply_cell_data_changeset>( changeset: C, cell_rev: Option, field_rev: T, ) -> Result { let field_rev = field_rev.as_ref(); let changeset = changeset.to_string(); let field_type = field_rev.ty.into(); let s = match field_type { FieldType::RichText => RichTextTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev), FieldType::Number => NumberTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev), FieldType::DateTime => DateTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev), FieldType::SingleSelect => { SingleSelectTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev) } FieldType::MultiSelect => MultiSelectTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev), FieldType::Checkbox => CheckboxTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev), FieldType::URL => URLTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev), }?; Ok(AnyCellData::new(s, field_type).json()) } pub fn decode_any_cell_data + Debug>( data: T, field_rev: &FieldRevision, ) -> CellBytes { match data.try_into() { Ok(any_cell_data) => { let AnyCellData { data, field_type } = any_cell_data; let to_field_type = field_rev.ty.into(); match try_decode_cell_data(data.into(), field_rev, &field_type, &to_field_type) { Ok(cell_bytes) => cell_bytes, Err(e) => { tracing::error!("Decode cell data failed, {:?}", e); CellBytes::default() } } } Err(_err) => { // It's okay to ignore this error, because it's okay that the current cell can't // display the existing cell data. For example, the UI of the text cell will be blank if // the type of the data of cell is Number. CellBytes::default() } } } pub fn try_decode_cell_data( cell_data: CellData, field_rev: &FieldRevision, s_field_type: &FieldType, t_field_type: &FieldType, ) -> FlowyResult { 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::(field_type)? .decode_cell_data(cell_data.into(), s_field_type, field_rev), FieldType::Number => field_rev .get_type_option::(field_type)? .decode_cell_data(cell_data.into(), s_field_type, field_rev), FieldType::DateTime => field_rev .get_type_option::(field_type)? .decode_cell_data(cell_data.into(), s_field_type, field_rev), FieldType::SingleSelect => field_rev .get_type_option::(field_type)? .decode_cell_data(cell_data.into(), s_field_type, field_rev), FieldType::MultiSelect => field_rev .get_type_option::(field_type)? .decode_cell_data(cell_data.into(), s_field_type, field_rev), FieldType::Checkbox => field_rev .get_type_option::(field_type)? .decode_cell_data(cell_data.into(), s_field_type, field_rev), FieldType::URL => field_rev .get_type_option::(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(CellBytes::default()) } None => Ok(CellBytes::default()), } } pub fn insert_text_cell(s: String, field_rev: &FieldRevision) -> CellRevision { let data = apply_cell_data_changeset(s, None, field_rev).unwrap(); CellRevision::new(data) } pub fn insert_number_cell(num: i64, field_rev: &FieldRevision) -> CellRevision { let data = apply_cell_data_changeset(num, None, field_rev).unwrap(); CellRevision::new(data) } pub fn insert_url_cell(url: String, field_rev: &FieldRevision) -> CellRevision { let data = apply_cell_data_changeset(url, None, field_rev).unwrap(); CellRevision::new(data) } pub fn insert_checkbox_cell(is_check: bool, field_rev: &FieldRevision) -> CellRevision { let s = if is_check { CHECK.to_string() } else { UNCHECK.to_string() }; let data = apply_cell_data_changeset(s, None, field_rev).unwrap(); CellRevision::new(data) } pub fn insert_date_cell(timestamp: i64, field_rev: &FieldRevision) -> CellRevision { let cell_data = serde_json::to_string(&DateCellChangesetPB { date: Some(timestamp.to_string()), time: None, }) .unwrap(); let data = apply_cell_data_changeset(cell_data, None, field_rev).unwrap(); CellRevision::new(data) } pub fn insert_select_option_cell(option_id: String, field_rev: &FieldRevision) -> CellRevision { let cell_data = SelectOptionCellChangeset::from_insert(&option_id).to_str(); let data = apply_cell_data_changeset(cell_data, None, field_rev).unwrap(); CellRevision::new(data) } pub fn delete_select_option_cell(option_id: String, field_rev: &FieldRevision) -> CellRevision { let cell_data = SelectOptionCellChangeset::from_delete(&option_id).to_str(); let data = apply_cell_data_changeset(cell_data, None, field_rev).unwrap(); CellRevision::new(data) } /// Deserialize the String into cell specific data type. pub trait FromCellString { fn from_cell_str(s: &str) -> FlowyResult where Self: Sized; } /// CellData is a helper struct. String will be parser into Option only if the T impl the FromCellString trait. pub struct CellData(pub Option); impl CellData { pub fn try_into_inner(self) -> FlowyResult { match self.0 { None => Err(ErrorCode::InvalidData.into()), Some(data) => Ok(data), } } } impl std::convert::From for CellData 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 for CellData { fn from(val: T) -> Self { CellData(Some(val)) } } impl std::convert::From> for String { fn from(p: CellData) -> Self { p.try_into_inner().unwrap_or_else(|_| String::new()) } } /// If the changeset applying to the cell is not String type, it should impl this trait. /// Deserialize the string into cell specific changeset. pub trait FromCellChangeset { fn from_changeset(changeset: String) -> FlowyResult where Self: Sized; } pub struct CellDataChangeset(pub Option); impl CellDataChangeset { pub fn try_into_inner(self) -> FlowyResult { match self.0 { None => Err(ErrorCode::InvalidData.into()), Some(data) => Ok(data), } } } impl std::convert::From for CellDataChangeset 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 for CellDataChangeset { fn from(s: String) -> Self { CellDataChangeset(Some(s)) } }