From 284755eccf4f65b95d6b1a511831f0b6609d4b52 Mon Sep 17 00:00:00 2001 From: appflowy Date: Tue, 12 Jul 2022 15:49:14 +0800 Subject: [PATCH 1/7] refactor: row builder --- .../rust-lib/flowy-grid/src/event_handler.rs | 11 +- .../src/services/cell/cell_operation.rs | 15 +- .../flowy-grid/src/services/grid_editor.rs | 37 ++-- .../src/services/row/row_builder.rs | 30 ++-- .../flowy-grid/src/services/row/row_loader.rs | 28 +-- frontend/rust-lib/flowy-grid/src/util.rs | 16 +- .../tests/grid/block_test/row_test.rs | 109 +++--------- .../tests/grid/block_test/script.rs | 44 +++-- .../flowy-grid/tests/grid/block_test/util.rs | 113 +++++++----- .../flowy-grid/tests/grid/grid_editor.rs | 163 ++++++++++-------- .../src/client_grid/grid_builder.rs | 17 +- 11 files changed, 288 insertions(+), 295 deletions(-) diff --git a/frontend/rust-lib/flowy-grid/src/event_handler.rs b/frontend/rust-lib/flowy-grid/src/event_handler.rs index a3b1471a4e..c2faceb9b5 100644 --- a/frontend/rust-lib/flowy-grid/src/event_handler.rs +++ b/frontend/rust-lib/flowy-grid/src/event_handler.rs @@ -5,6 +5,7 @@ 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::make_row_from_row_rev; use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_grid_data_model::revision::FieldRevision; use flowy_sync::entities::grid::{FieldChangesetParams, GridSettingChangesetParams}; @@ -229,10 +230,12 @@ pub(crate) async fn get_row_handler( ) -> DataResult { let params: GridRowId = data.into_inner().try_into()?; let editor = manager.get_grid_editor(¶ms.grid_id)?; - let row = OptionalRow { - row: editor.get_row(¶ms.row_id).await?, - }; - data_result(row) + let row = editor + .get_row_rev(¶ms.row_id) + .await? + .and_then(make_row_from_row_rev); + + data_result(OptionalRow { row }) } #[tracing::instrument(level = "debug", skip(data, manager), err)] diff --git a/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs b/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs index 5b74c26aa4..05c331bda8 100644 --- a/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs +++ b/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs @@ -26,8 +26,12 @@ pub trait CellDataOperation { /// SelectOptionCellChangeset,DateCellChangeset. etc. fn apply_changeset(&self, changeset: CellDataChangeset, cell_rev: Option) -> FlowyResult; } -/// 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 +/// 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, @@ -114,14 +118,16 @@ pub fn try_decode_cell_data( } } +/// If the cell data is not String type, it should impl this trait. +/// 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 { @@ -158,7 +164,8 @@ impl std::convert::From> for String { } } -// CellChangeset +/// 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 diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs index 1d63b6dd69..93d9317565 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -8,8 +8,7 @@ use crate::services::field::{default_type_option_builder_from_type, type_option_ use crate::services::filter::{GridFilterChangeset, GridFilterService}; use crate::services::persistence::block_index::BlockIndexCache; 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, + make_grid_blocks, make_row_from_row_rev, make_rows_from_row_revs, GridBlockSnapshot, RowRevisionBuilder, }; use crate::services::setting::make_grid_setting; use bytes::Bytes; @@ -274,8 +273,7 @@ impl GridRevisionEditor { let block_id = self.block_id().await?; // insert empty row below the row whose id is upper_row_id - let row_rev_ctx = CreateRowRevisionBuilder::new(&field_revs).build(); - let row_rev = make_row_rev_from_context(&block_id, row_rev_ctx); + let row_rev = RowRevisionBuilder::new(&field_revs).build(&block_id); let row_order = RowInfo::from(&row_rev); // insert the row @@ -287,12 +285,11 @@ impl GridRevisionEditor { Ok(row_order) } - pub async fn insert_rows(&self, contexts: Vec) -> FlowyResult> { + pub async fn insert_rows(&self, row_revs: Vec) -> FlowyResult> { let block_id = self.block_id().await?; let mut rows_by_block_id: HashMap> = HashMap::new(); let mut row_orders = vec![]; - for ctx in contexts { - let row_rev = make_row_rev_from_context(&block_id, ctx); + for row_rev in row_revs { row_orders.push(RowInfo::from(&row_rev)); rows_by_block_id .entry(block_id.clone()) @@ -307,10 +304,7 @@ impl GridRevisionEditor { } pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> { - let field_revs = self.get_field_revs(None).await?; - self.block_manager - .update_row(changeset, |row_rev| make_row_from_row_rev(&field_revs, row_rev)) - .await + self.block_manager.update_row(changeset, make_row_from_row_rev).await } pub async fn get_rows(&self, block_id: &str) -> FlowyResult { @@ -322,26 +316,20 @@ impl GridRevisionEditor { debug_assert_eq!(grid_block_snapshot.len(), 1); if grid_block_snapshot.len() == 1 { let snapshot = grid_block_snapshot.pop().unwrap(); - let field_revs = self.get_field_revs(None).await?; - let rows = make_rows_from_row_revs(&field_revs, &snapshot.row_revs); + let rows = make_rows_from_row_revs(&snapshot.row_revs); Ok(rows.into()) } else { Ok(vec![].into()) } } - pub async fn get_row(&self, row_id: &str) -> FlowyResult> { + pub async fn get_row_rev(&self, row_id: &str) -> FlowyResult>> { match self.block_manager.get_row_rev(row_id).await? { None => Ok(None), - Some(row_rev) => { - let field_revs = self.get_field_revs(None).await?; - let row_revs = vec![row_rev]; - let mut rows = make_rows_from_row_revs(&field_revs, &row_revs); - debug_assert!(rows.len() == 1); - Ok(rows.pop()) - } + Some(row_rev) => Ok(Some(row_rev)), } } + pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> { let _ = self.block_manager.delete_row(row_id).await?; Ok(()) @@ -360,6 +348,10 @@ impl GridRevisionEditor { Some(Cell::new(¶ms.field_id, data)) } + pub async fn get_cell_display(&self, _params: &CellIdentifier) -> Option { + todo!() + } + pub async fn get_cell_rev(&self, row_id: &str, field_id: &str) -> FlowyResult> { let row_rev = self.block_manager.get_row_rev(row_id).await?; match row_rev { @@ -395,7 +387,6 @@ impl GridRevisionEditor { let cell_rev = self.get_cell_rev(&row_id, &field_id).await?; // Update the changeset.data property with the return value. 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, @@ -404,7 +395,7 @@ impl GridRevisionEditor { }; let _ = self .block_manager - .update_cell(cell_changeset, |row_rev| make_row_from_row_rev(&field_revs, row_rev)) + .update_cell(cell_changeset, make_row_from_row_rev) .await?; Ok(()) } diff --git a/frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs b/frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs index 3302276cd8..cc86fa3d9d 100644 --- a/frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs +++ b/frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs @@ -6,12 +6,12 @@ use indexmap::IndexMap; use std::collections::HashMap; use std::sync::Arc; -pub struct CreateRowRevisionBuilder<'a> { +pub struct RowRevisionBuilder<'a> { field_rev_map: HashMap<&'a String, &'a Arc>, payload: CreateRowRevisionPayload, } -impl<'a> CreateRowRevisionBuilder<'a> { +impl<'a> RowRevisionBuilder<'a> { pub fn new(fields: &'a [Arc]) -> Self { let field_rev_map = fields .iter() @@ -28,10 +28,10 @@ impl<'a> CreateRowRevisionBuilder<'a> { Self { field_rev_map, payload } } - pub fn add_cell(&mut self, field_id: &str, data: String) -> FlowyResult<()> { + pub fn insert_cell(&mut self, field_id: &str, data: String) -> FlowyResult<()> { match self.field_rev_map.get(&field_id.to_owned()) { None => { - let msg = format!("Invalid field_id: {}", field_id); + let msg = format!("Can't find the field with id: {}", field_id); Err(FlowyError::internal().context(msg)) } Some(field_rev) => { @@ -43,7 +43,7 @@ impl<'a> CreateRowRevisionBuilder<'a> { } } - pub fn add_select_option_cell(&mut self, field_id: &str, data: String) -> FlowyResult<()> { + pub fn insert_select_option_cell(&mut self, field_id: &str, data: String) -> FlowyResult<()> { match self.field_rev_map.get(&field_id.to_owned()) { None => { let msg = format!("Invalid field_id: {}", field_id); @@ -71,18 +71,14 @@ impl<'a> CreateRowRevisionBuilder<'a> { self } - pub fn build(self) -> CreateRowRevisionPayload { - self.payload - } -} - -pub fn make_row_rev_from_context(block_id: &str, payload: CreateRowRevisionPayload) -> RowRevision { - RowRevision { - id: payload.row_id, - block_id: block_id.to_owned(), - cells: payload.cell_by_field_id, - height: payload.height, - visibility: payload.visibility, + pub fn build(self, block_id: &str) -> RowRevision { + RowRevision { + id: self.payload.row_id, + block_id: block_id.to_owned(), + cells: self.payload.cell_by_field_id, + height: self.payload.height, + visibility: self.payload.visibility, + } } } diff --git a/frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs b/frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs index 1b9ce80101..81d9420d88 100644 --- a/frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs +++ b/frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs @@ -1,6 +1,6 @@ use crate::entities::{GridBlock, RepeatedGridBlock, Row, RowInfo}; use flowy_error::FlowyResult; -use flowy_grid_data_model::revision::{FieldRevision, RowRevision}; +use flowy_grid_data_model::revision::RowRevision; use std::collections::HashMap; use std::sync::Arc; @@ -39,28 +39,14 @@ pub(crate) fn make_row_orders_from_row_revs(row_revs: &[Arc]) -> Ve row_revs.iter().map(RowInfo::from).collect::>() } -pub(crate) fn make_row_from_row_rev(fields: &[Arc], row_rev: Arc) -> Option { - make_rows_from_row_revs(fields, &[row_rev]).pop() +pub(crate) fn make_row_from_row_rev(row_rev: Arc) -> Option { + make_rows_from_row_revs(&[row_rev]).pop() } -pub(crate) fn make_rows_from_row_revs(_fields: &[Arc], row_revs: &[Arc]) -> Vec { - // let field_rev_map = fields - // .iter() - // .map(|field_rev| (&field_rev.id, field_rev)) - // .collect::>(); - - let make_row = |row_rev: &Arc| { - // let cell_by_field_id = row_rev - // .cells - // .clone() - // .into_iter() - // .flat_map(|(field_id, cell_rev)| make_cell_by_field_id(&field_rev_map, field_id, cell_rev)) - // .collect::>(); - - Row { - id: row_rev.id.clone(), - height: row_rev.height, - } +pub(crate) fn make_rows_from_row_revs(row_revs: &[Arc]) -> Vec { + let make_row = |row_rev: &Arc| Row { + id: row_rev.id.clone(), + height: row_rev.height, }; row_revs.iter().map(make_row).collect::>() diff --git a/frontend/rust-lib/flowy-grid/src/util.rs b/frontend/rust-lib/flowy-grid/src/util.rs index e0055d09b6..3b48e313a9 100644 --- a/frontend/rust-lib/flowy-grid/src/util.rs +++ b/frontend/rust-lib/flowy-grid/src/util.rs @@ -4,29 +4,29 @@ use flowy_grid_data_model::revision::BuildGridContext; use flowy_sync::client_grid::GridBuilder; pub fn make_default_grid() -> BuildGridContext { + let mut grid_builder = GridBuilder::new(); // text let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default()) .name("Name") .visibility(true) .primary(true) .build(); + grid_builder.add_field(text_field); // single select let single_select = SingleSelectTypeOptionBuilder::default(); let single_select_field = FieldBuilder::new(single_select).name("Type").visibility(true).build(); + grid_builder.add_field(single_select_field); // checkbox let checkbox_field = FieldBuilder::from_field_type(&FieldType::Checkbox) .name("Done") .visibility(true) .build(); + grid_builder.add_field(checkbox_field); - GridBuilder::default() - .add_field(text_field) - .add_field(single_select_field) - .add_field(checkbox_field) - .add_empty_row() - .add_empty_row() - .add_empty_row() - .build() + grid_builder.add_empty_row(); + grid_builder.add_empty_row(); + grid_builder.add_empty_row(); + grid_builder.build() } diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs index 96c23611b0..ec57893d9b 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs @@ -1,13 +1,5 @@ use crate::grid::block_test::script::GridRowTest; use crate::grid::block_test::script::RowScript::*; -use crate::grid::block_test::util::GridRowTestBuilder; -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 crate::grid::field_test::util::make_date_cell_string; use flowy_grid_data_model::revision::RowMetaChangeset; #[tokio::test] @@ -18,7 +10,7 @@ async fn grid_create_row_count_test() { CreateEmptyRow, CreateEmptyRow, CreateRow { - payload: GridRowTestBuilder::new(&test).build(), + row_rev: test.row_builder().build(), }, AssertRowCount(6), ]; @@ -28,15 +20,15 @@ async fn grid_create_row_count_test() { #[tokio::test] async fn grid_update_row() { let mut test = GridRowTest::new().await; - let payload = GridRowTestBuilder::new(&test).build(); + let row_rev = test.row_builder().build(); let changeset = RowMetaChangeset { - row_id: payload.row_id.clone(), + row_id: row_rev.id.clone(), height: None, visibility: None, cell_by_field_id: Default::default(), }; - let scripts = vec![AssertRowCount(3), CreateRow { payload }, UpdateRow { changeset }]; + let scripts = vec![AssertRowCount(3), CreateRow { row_rev }, UpdateRow { changeset }]; test.run_scripts(scripts).await; let expected_row = test.last_row().unwrap(); @@ -47,13 +39,13 @@ async fn grid_update_row() { #[tokio::test] async fn grid_delete_row() { let mut test = GridRowTest::new().await; - let payload1 = GridRowTestBuilder::new(&test).build(); - let payload2 = GridRowTestBuilder::new(&test).build(); - let row_ids = vec![payload1.row_id.clone(), payload2.row_id.clone()]; + let row_1 = test.row_builder().build(); + let row_2 = test.row_builder().build(); + let row_ids = vec![row_1.id.clone(), row_2.id.clone()]; let scripts = vec![ AssertRowCount(3), - CreateRow { payload: payload1 }, - CreateRow { payload: payload2 }, + CreateRow { row_rev: row_1 }, + CreateRow { row_rev: row_2 }, AssertBlockCount(1), AssertBlock { block_index: 0, @@ -73,78 +65,17 @@ async fn grid_delete_row() { #[tokio::test] async fn grid_row_add_cells_test() { let mut test = GridRowTest::new().await; - let mut builder = test.builder(); - for field in test.field_revs() { - let field_type: FieldType = field.field_type_rev.into(); - match field_type { - FieldType::RichText => { - builder.add_cell(&field.id, "hello world".to_owned()).unwrap(); - } - FieldType::Number => { - builder.add_cell(&field.id, "18,443".to_owned()).unwrap(); - } - FieldType::DateTime => { - builder - .add_cell(&field.id, make_date_cell_string("1647251762")) - .unwrap(); - } - FieldType::SingleSelect => { - let type_option = SingleSelectTypeOption::from(field); - let option = type_option.options.first().unwrap(); - builder.add_select_option_cell(&field.id, option.id.clone()).unwrap(); - } - FieldType::MultiSelect => { - let type_option = MultiSelectTypeOption::from(field); - let ops_ids = type_option - .options - .iter() - .map(|option| option.id.clone()) - .collect::>() - .join(SELECTION_IDS_SEPARATOR); - builder.add_select_option_cell(&field.id, ops_ids).unwrap(); - } - FieldType::Checkbox => { - builder.add_cell(&field.id, "false".to_string()).unwrap(); - } - FieldType::URL => { - builder.add_cell(&field.id, "1".to_string()).unwrap(); - } - } - } - let context = builder.build(); - let scripts = vec![CreateRow { payload: context }]; - test.run_scripts(scripts).await; -} + let mut builder = test.row_builder(); -#[tokio::test] -async fn grid_row_add_date_cell_test() { - let mut test = GridRowTest::new().await; - let mut builder = test.builder(); - let mut date_field = None; - let timestamp = 1647390674; - for field in test.field_revs() { - let field_type: FieldType = field.field_type_rev.into(); - if field_type == FieldType::DateTime { - date_field = Some(field.clone()); - NaiveDateTime::from_timestamp(123, 0); - // The data should not be empty - assert!(builder.add_cell(&field.id, "".to_string()).is_err()); - assert!(builder.add_cell(&field.id, make_date_cell_string("123")).is_ok()); - assert!(builder - .add_cell(&field.id, make_date_cell_string(×tamp.to_string())) - .is_ok()); - } - } - let context = builder.build(); - let date_field = date_field.unwrap(); - let cell_rev = context.cell_by_field_id.get(&date_field.id).unwrap(); - assert_eq!( - decode_any_cell_data(cell_rev, &date_field) - .parse::() - .unwrap() - .date, - "2022/03/16", - ); - let scripts = vec![CreateRow { payload: context }]; + builder.insert_text_cell("hello world"); + builder.insert_number_cell("18,443"); + builder.insert_date_cell("1647251762"); + builder.insert_single_select_cell(|options| options.first().unwrap()); + builder.insert_multi_select_cell(|options| options); + builder.insert_checkbox_cell("false"); + builder.insert_url_cell("1"); + + let row_rev = builder.build(); + let scripts = vec![CreateRow { row_rev }]; test.run_scripts(scripts).await; } diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs index 288133958b..642cd51670 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs @@ -1,15 +1,16 @@ +use crate::grid::block_test::util::GridRowTestBuilder; use crate::grid::grid_editor::GridEditorTest; -use flowy_grid::entities::RowInfo; -use flowy_grid::services::row::{CreateRowRevisionBuilder, CreateRowRevisionPayload}; +use flowy_grid::entities::{CellIdentifier, RowInfo}; + use flowy_grid_data_model::revision::{ - FieldRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision, + GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision, }; use std::sync::Arc; pub enum RowScript { CreateEmptyRow, CreateRow { - payload: CreateRowRevisionPayload, + row_rev: RowRevision, }, UpdateRow { changeset: RowMetaChangeset, @@ -20,6 +21,11 @@ pub enum RowScript { DeleteRows { row_ids: Vec, }, + AssertCell { + row_id: String, + field_id: String, + expected_display: Option, + }, AssertRowCount(usize), CreateBlock { block: GridBlockMetaRevision, @@ -49,10 +55,6 @@ impl GridRowTest { Self { inner: editor_test } } - pub fn field_revs(&self) -> &Vec> { - &self.field_revs - } - pub fn last_row(&self) -> Option { self.row_revs.last().map(|a| a.clone().as_ref().clone()) } @@ -63,8 +65,8 @@ impl GridRowTest { } } - pub fn builder(&self) -> CreateRowRevisionBuilder { - CreateRowRevisionBuilder::new(&self.field_revs) + pub fn row_builder(&self) -> GridRowTestBuilder { + GridRowTestBuilder::new(self.block_id(), &self.field_revs) } pub async fn run_script(&mut self, script: RowScript) { @@ -76,8 +78,8 @@ impl GridRowTest { self.row_revs = self.get_row_revs().await; self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap(); } - RowScript::CreateRow { payload: context } => { - let row_orders = self.editor.insert_rows(vec![context]).await.unwrap(); + RowScript::CreateRow { row_rev } => { + let row_orders = self.editor.insert_rows(vec![row_rev]).await.unwrap(); for row_order in row_orders { self.row_order_by_row_id .insert(row_order.row_id().to_owned(), row_order); @@ -96,6 +98,24 @@ impl GridRowTest { self.row_revs = self.get_row_revs().await; self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap(); } + RowScript::AssertCell { + row_id, + field_id, + expected_display, + } => { + let id = CellIdentifier { + grid_id: self.grid_id.clone(), + field_id, + row_id, + }; + let display = self.editor.get_cell_display(&id).await; + match expected_display { + None => {} + Some(expected_display) => { + assert_eq!(display.unwrap(), expected_display); + } + } + } RowScript::AssertRow { expected_row } => { let row = &*self .row_revs diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs index 49b8383fee..baf1bf6ae0 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs @@ -1,66 +1,97 @@ -use crate::grid::block_test::script::GridRowTest; - use flowy_grid::entities::FieldType; -use flowy_grid::services::field::DateCellChangeset; -use flowy_grid::services::row::{CreateRowRevisionBuilder, CreateRowRevisionPayload}; -use flowy_grid_data_model::revision::FieldRevision; +use flowy_grid::services::field::select_option::{SelectOption, SELECTION_IDS_SEPARATOR}; +use flowy_grid::services::field::{DateCellChangeset, MultiSelectTypeOption, SingleSelectTypeOption}; +use flowy_grid::services::row::RowRevisionBuilder; +use flowy_grid_data_model::revision::{FieldRevision, RowRevision}; +use std::sync::Arc; use strum::EnumCount; pub struct GridRowTestBuilder<'a> { - test: &'a GridRowTest, - inner_builder: CreateRowRevisionBuilder<'a>, + block_id: String, + field_revs: &'a [Arc], + inner_builder: RowRevisionBuilder<'a>, } impl<'a> GridRowTestBuilder<'a> { - pub fn new(test: &'a GridRowTest) -> Self { - assert_eq!(test.field_revs().len(), FieldType::COUNT); - - let inner_builder = CreateRowRevisionBuilder::new(test.field_revs()); - Self { test, inner_builder } - } - #[allow(dead_code)] - pub fn update_text_cell(mut self, data: String) -> Self { - let text_field = self.field_rev_with_type(&FieldType::DateTime); - self.inner_builder.add_cell(&text_field.id, data).unwrap(); - self + pub fn new(block_id: &str, field_revs: &'a [Arc]) -> Self { + assert_eq!(field_revs.len(), FieldType::COUNT); + let inner_builder = RowRevisionBuilder::new(field_revs); + Self { + block_id: block_id.to_owned(), + field_revs, + inner_builder, + } } - #[allow(dead_code)] - pub fn update_number_cell(mut self, data: String) -> Self { - let number_field = self.field_rev_with_type(&FieldType::DateTime); - self.inner_builder.add_cell(&number_field.id, data).unwrap(); - self + pub fn insert_text_cell(&mut self, data: &str) { + let text_field = self.field_rev_with_type(&FieldType::RichText); + self.inner_builder + .insert_cell(&text_field.id, data.to_string()) + .unwrap(); } - #[allow(dead_code)] - pub fn update_date_cell(mut self, value: i64) -> Self { + pub fn insert_number_cell(&mut self, data: &str) { + let number_field = self.field_rev_with_type(&FieldType::Number); + self.inner_builder + .insert_cell(&number_field.id, data.to_string()) + .unwrap(); + } + + pub fn insert_date_cell(&mut self, data: &str) { let value = serde_json::to_string(&DateCellChangeset { - date: Some(value.to_string()), + date: Some(data.to_string()), time: None, }) .unwrap(); let date_field = self.field_rev_with_type(&FieldType::DateTime); - self.inner_builder.add_cell(&date_field.id, value).unwrap(); - self + self.inner_builder.insert_cell(&date_field.id, value).unwrap(); } - #[allow(dead_code)] - pub fn update_checkbox_cell(mut self, data: bool) -> Self { + pub fn insert_checkbox_cell(&mut self, data: &str) { let number_field = self.field_rev_with_type(&FieldType::Checkbox); - self.inner_builder.add_cell(&number_field.id, data.to_string()).unwrap(); - self + self.inner_builder + .insert_cell(&number_field.id, data.to_string()) + .unwrap(); } - #[allow(dead_code)] - pub fn update_url_cell(mut self, data: String) -> Self { - let number_field = self.field_rev_with_type(&FieldType::Checkbox); - self.inner_builder.add_cell(&number_field.id, data).unwrap(); - self + pub fn insert_url_cell(&mut self, data: &str) { + let number_field = self.field_rev_with_type(&FieldType::URL); + self.inner_builder + .insert_cell(&number_field.id, data.to_string()) + .unwrap(); + } + + pub fn insert_single_select_cell(&mut self, f: F) + where + F: Fn(&Vec) -> &SelectOption, + { + let single_select_field = self.field_rev_with_type(&FieldType::SingleSelect); + let type_option = SingleSelectTypeOption::from(&single_select_field); + let option = f(&type_option.options); + self.inner_builder + .insert_select_option_cell(&single_select_field.id, option.id.clone()) + .unwrap(); + } + + pub fn insert_multi_select_cell(&mut self, f: F) + where + F: Fn(&Vec) -> &Vec, + { + let multi_select_field = self.field_rev_with_type(&FieldType::MultiSelect); + let type_option = MultiSelectTypeOption::from(&multi_select_field); + let options = f(&type_option.options); + let ops_ids = options + .iter() + .map(|option| option.id.clone()) + .collect::>() + .join(SELECTION_IDS_SEPARATOR); + self.inner_builder + .insert_select_option_cell(&multi_select_field.id, ops_ids) + .unwrap(); } pub fn field_rev_with_type(&self, field_type: &FieldType) -> FieldRevision { - self.test - .field_revs() + self.field_revs .iter() .find(|field_rev| { let t_field_type: FieldType = field_rev.field_type_rev.into(); @@ -71,7 +102,7 @@ impl<'a> GridRowTestBuilder<'a> { .clone() } - pub fn build(self) -> CreateRowRevisionPayload { - self.inner_builder.build() + pub fn build(self) -> RowRevision { + self.inner_builder.build(&self.block_id) } } diff --git a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs index d944aac6c5..85c81c4058 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs @@ -6,7 +6,7 @@ use flowy_grid::entities::*; use flowy_grid::services::field::select_option::SelectOption; use flowy_grid::services::field::*; use flowy_grid::services::grid_editor::{GridPadBuilder, GridRevisionEditor}; -use flowy_grid::services::row::CreateRowRevisionPayload; +use flowy_grid::services::row::{CreateRowRevisionPayload, RowRevisionBuilder}; use flowy_grid::services::setting::GridSettingChangesetBuilder; use flowy_grid_data_model::revision::*; use flowy_revision::REVISION_WRITE_INTERVAL_IN_MILLIS; @@ -20,6 +20,7 @@ use std::collections::HashMap; use std::sync::Arc; use std::time::Duration; use strum::EnumCount; +use strum::IntoEnumIterator; use tokio::time::sleep; pub struct GridEditorTest { @@ -37,14 +38,13 @@ impl GridEditorTest { pub async fn new() -> Self { let sdk = FlowySDKTest::default(); let _ = sdk.init_user().await; - let build_context = make_all_field_test_grid(); + let build_context = make_test_grid(); let view_data: Bytes = build_context.into(); let test = ViewTest::new_grid_view(&sdk, view_data.to_vec()).await; let editor = sdk.grid_manager.open_grid(&test.view.id).await.unwrap(); let field_revs = editor.get_field_revs(None).await.unwrap(); let block_meta_revs = editor.get_block_meta_revs().await.unwrap(); let row_revs = editor.grid_block_snapshots(None).await.unwrap().pop().unwrap().row_revs; - assert_eq!(row_revs.len(), 3); assert_eq!(block_meta_revs.len(), 1); // It seems like you should add the field in the make_test_grid() function. @@ -90,75 +90,98 @@ impl GridEditorTest { .pop() .unwrap() } + + pub fn block_id(&self) -> &str { + &self.block_meta_revs.last().unwrap().block_id + } } -fn make_all_field_test_grid() -> BuildGridContext { - let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default()) - .name("Name") - .visibility(true) - .build(); +// This grid is assumed to contain all the Fields. +fn make_test_grid() -> BuildGridContext { + let mut grid_builder = GridBuilder::new(); + // Iterate through the FieldType to create the corresponding Field. + for field_type in FieldType::iter() { + let field_type: FieldType = field_type; - // Single Select - let single_select = SingleSelectTypeOptionBuilder::default() - .option(SelectOption::new("Live")) - .option(SelectOption::new("Completed")) - .option(SelectOption::new("Planned")) - .option(SelectOption::new("Paused")); - let single_select_field = FieldBuilder::new(single_select).name("Status").visibility(true).build(); + // The + match field_type { + FieldType::RichText => { + let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default()) + .name("Name") + .visibility(true) + .build(); + grid_builder.add_field(text_field); + } + FieldType::Number => { + // Number + let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD); + let number_field = FieldBuilder::new(number).name("Price").visibility(true).build(); + grid_builder.add_field(number_field); + } + FieldType::DateTime => { + // Date + let date = DateTypeOptionBuilder::default() + .date_format(DateFormat::US) + .time_format(TimeFormat::TwentyFourHour); + let date_field = FieldBuilder::new(date).name("Time").visibility(true).build(); + grid_builder.add_field(date_field); + } + FieldType::SingleSelect => { + // Single Select + let single_select = SingleSelectTypeOptionBuilder::default() + .option(SelectOption::new("Live")) + .option(SelectOption::new("Completed")) + .option(SelectOption::new("Planned")) + .option(SelectOption::new("Paused")); + let single_select_field = FieldBuilder::new(single_select).name("Status").visibility(true).build(); + grid_builder.add_field(single_select_field); + } + FieldType::MultiSelect => { + // MultiSelect + let multi_select = MultiSelectTypeOptionBuilder::default() + .option(SelectOption::new("Google")) + .option(SelectOption::new("Facebook")) + .option(SelectOption::new("Twitter")); + let multi_select_field = FieldBuilder::new(multi_select) + .name("Platform") + .visibility(true) + .build(); + grid_builder.add_field(multi_select_field); + } + FieldType::Checkbox => { + // Checkbox + let checkbox = CheckboxTypeOptionBuilder::default(); + let checkbox_field = FieldBuilder::new(checkbox).name("is done").visibility(true).build(); + grid_builder.add_field(checkbox_field); + } + FieldType::URL => { + // URL + let url = URLTypeOptionBuilder::default(); + let url_field = FieldBuilder::new(url).name("link").visibility(true).build(); + grid_builder.add_field(url_field); + } + } + } - // MultiSelect - let multi_select = MultiSelectTypeOptionBuilder::default() - .option(SelectOption::new("Google")) - .option(SelectOption::new("Facebook")) - .option(SelectOption::new("Twitter")); - let multi_select_field = FieldBuilder::new(multi_select) - .name("Platform") - .visibility(true) - .build(); - - // Number - let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD); - let number_field = FieldBuilder::new(number).name("Price").visibility(true).build(); - - // Date - let date = DateTypeOptionBuilder::default() - .date_format(DateFormat::US) - .time_format(TimeFormat::TwentyFourHour); - let date_field = FieldBuilder::new(date).name("Time").visibility(true).build(); - - // Checkbox - let checkbox = CheckboxTypeOptionBuilder::default(); - let checkbox_field = FieldBuilder::new(checkbox).name("is done").visibility(true).build(); - - // URL - let url = URLTypeOptionBuilder::default(); - let url_field = FieldBuilder::new(url).name("link").visibility(true).build(); - - // for i in 0..3 { - // for field_type in FieldType::iter() { - // let field_type: FieldType = field_type; - // match field_type { - // FieldType::RichText => {} - // FieldType::Number => {} - // FieldType::DateTime => {} - // FieldType::SingleSelect => {} - // FieldType::MultiSelect => {} - // FieldType::Checkbox => {} - // FieldType::URL => {} - // } - // } - // } - - GridBuilder::default() - .add_field(text_field) - .add_field(single_select_field) - .add_field(multi_select_field) - .add_field(number_field) - .add_field(date_field) - .add_field(checkbox_field) - .add_field(url_field) - .add_empty_row() - .add_empty_row() - .add_empty_row() - .build() + // We have many assumptions base on the number of the rows, so do not change the number of the loop. + for _i in 0..10 { + for field_type in FieldType::iter() { + let field_type: FieldType = field_type; + // let mut row_builder = RowRevisionBuilder::new() + match field_type { + FieldType::RichText => {} + FieldType::Number => {} + FieldType::DateTime => {} + FieldType::SingleSelect => {} + FieldType::MultiSelect => {} + FieldType::Checkbox => {} + FieldType::URL => {} + } + } + } + // assert_eq!(row_revs.len(), 10); + // .add_empty_row() + // .add_empty_row() + // .add_empty_row() + grid_builder.build() } diff --git a/shared-lib/flowy-sync/src/client_grid/grid_builder.rs b/shared-lib/flowy-sync/src/client_grid/grid_builder.rs index d1fbed3f53..1dffa6c0c6 100644 --- a/shared-lib/flowy-sync/src/client_grid/grid_builder.rs +++ b/shared-lib/flowy-sync/src/client_grid/grid_builder.rs @@ -26,18 +26,23 @@ impl std::default::Default for GridBuilder { } impl GridBuilder { - pub fn add_field(mut self, field: FieldRevision) -> Self { + pub fn new() -> Self { + Self::default() + } + pub fn add_field(&mut self, field: FieldRevision) { self.build_context.field_revs.push(field); - self } - pub fn add_empty_row(mut self) -> Self { - let row = RowRevision::new(&self.build_context.blocks.first().unwrap().block_id); + pub fn add_row(&mut self, row_rev: RowRevision) { let block_meta_rev = self.build_context.blocks.first_mut().unwrap(); let block_rev = self.build_context.blocks_meta_data.first_mut().unwrap(); - block_rev.rows.push(Arc::new(row)); + block_rev.rows.push(Arc::new(row_rev)); block_meta_rev.row_count += 1; - self + } + + pub fn add_empty_row(&mut self) { + let row = RowRevision::new(&self.build_context.blocks.first().unwrap().block_id); + self.add_row(row); } pub fn build(self) -> BuildGridContext { From 1e3640f8acb88601a9aad9a0cbb578a0a34558f1 Mon Sep 17 00:00:00 2001 From: appflowy Date: Tue, 12 Jul 2022 22:24:01 +0800 Subject: [PATCH 2/7] chore: config cell displayable data --- .../rust-lib/flowy-grid/src/event_handler.rs | 2 +- .../src/services/cell/any_cell_data.rs | 29 ++++++---- .../src/services/cell/cell_operation.rs | 55 +++++++++++-------- .../src/services/field/select_option.rs | 31 +++++++++-- .../type_options/checkbox_type_option.rs | 22 ++++++-- .../field/type_options/date_type_option.rs | 25 ++++++--- .../type_options/multi_select_type_option.rs | 26 +++------ .../number_type_option/number_type_option.rs | 10 ++-- .../type_options/single_select_type_option.rs | 28 ++++------ .../field/type_options/text_type_option.rs | 20 +++++-- .../field/type_options/url_type_option.rs | 24 ++++++-- .../filter/impls/select_option_filter.rs | 4 +- .../flowy-grid/src/services/grid_editor.rs | 2 +- 13 files changed, 171 insertions(+), 107 deletions(-) diff --git a/frontend/rust-lib/flowy-grid/src/event_handler.rs b/frontend/rust-lib/flowy-grid/src/event_handler.rs index c2faceb9b5..27f3da7cc1 100644 --- a/frontend/rust-lib/flowy-grid/src/event_handler.rs +++ b/frontend/rust-lib/flowy-grid/src/event_handler.rs @@ -376,7 +376,7 @@ pub(crate) async fn get_select_option_handler( }, Some(cell_rev) => cell_rev.try_into()?, }; - let option_context = type_option.selected_select_option(any_cell_data); + let option_context = type_option.selected_select_option(any_cell_data.into()); data_result(option_context) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs b/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs index 259fcc79ef..1b85f3d1a9 100644 --- a/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs +++ b/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs @@ -100,36 +100,33 @@ impl AnyCellData { /// * 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, -} +pub struct CellBytes(pub Bytes); -impl DecodedCellData { +impl CellBytes { pub fn new>(data: T) -> Self { - Self { - data: data.as_ref().to_vec(), - } + let bytes = Bytes::from(data.as_ref().to_vec()); + Self(bytes) } - pub fn try_from_bytes>(bytes: T) -> FlowyResult + pub fn from>(bytes: T) -> FlowyResult where >::Error: std::fmt::Debug, { let bytes = bytes.try_into().map_err(internal_error)?; - Ok(Self { data: bytes.to_vec() }) + Ok(Self(bytes)) } pub fn parse<'a, T: TryFrom<&'a [u8]>>(&'a self) -> FlowyResult where >::Error: std::fmt::Debug, { - T::try_from(self.data.as_ref()).map_err(internal_error) + T::try_from(self.0.as_ref()).map_err(internal_error) } } -impl ToString for DecodedCellData { +impl ToString for CellBytes { fn to_string(&self) -> String { - match String::from_utf8(self.data.clone()) { + match String::from_utf8(self.0.to_vec()) { Ok(s) => s, Err(e) => { tracing::error!("DecodedCellData to string failed: {:?}", e); @@ -138,3 +135,11 @@ impl ToString for DecodedCellData { } } } + +impl std::ops::Deref for CellBytes { + type Target = Bytes; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs b/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs index 05c331bda8..584623811d 100644 --- a/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs +++ b/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs @@ -1,31 +1,45 @@ +use crate::entities::FieldType; +use crate::services::cell::{AnyCellData, CellBytes}; +use crate::services::field::*; + 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::*; - +/// 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 CellDataOperation { - /// The cell_data is able to parse into the specific data that was impl the FromCellData trait. +/// 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, + cell_data: CellData, decoded_field_type: &FieldType, field_rev: &FieldRevision, - ) -> FlowyResult; + ) -> FlowyResult; - /// The changeset is able to parse into the specific data that was impl the FromCellChangeset trait. + /// 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; + 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 @@ -53,23 +67,20 @@ pub fn apply_cell_data_changeset>( Ok(AnyCellData::new(s, field_type).json()) } -pub fn decode_any_cell_data>(data: T, field_rev: &FieldRevision) -> DecodedCellData { +pub fn decode_any_cell_data>(data: T, field_rev: &FieldRevision) -> CellBytes { if let Ok(any_cell_data) = data.try_into() { - let AnyCellData { - data: cell_data, - field_type, - } = any_cell_data; + let AnyCellData { 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, + 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); - DecodedCellData::default() + CellBytes::default() } } } else { tracing::error!("Decode type option data failed"); - DecodedCellData::default() + CellBytes::default() } } @@ -78,7 +89,7 @@ pub fn try_decode_cell_data( field_rev: &FieldRevision, s_field_type: &FieldType, t_field_type: &FieldType, -) -> FlowyResult { +) -> FlowyResult { let cell_data = cell_data.try_into_inner()?; let get_cell_data = || { let field_type: FieldTypeRevision = t_field_type.into(); @@ -112,9 +123,9 @@ pub fn try_decode_cell_data( Some(Ok(data)) => Ok(data), Some(Err(err)) => { tracing::error!("{:?}", err); - Ok(DecodedCellData::default()) + Ok(CellBytes::default()) } - None => Ok(DecodedCellData::default()), + None => Ok(CellBytes::default()), } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/select_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/select_option.rs index 3e974e9f56..d429899587 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/select_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/select_option.rs @@ -1,5 +1,5 @@ use crate::entities::{CellChangeset, CellIdentifier, CellIdentifierPayload, FieldType}; -use crate::services::cell::{AnyCellData, FromCellChangeset, FromCellString}; +use crate::services::cell::{AnyCellData, CellData, CellDisplayable, FromCellChangeset, FromCellString}; use crate::services::field::{MultiSelectTypeOption, SingleSelectTypeOption}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult}; @@ -60,12 +60,11 @@ impl std::default::Default for SelectOptionColor { } } -pub fn make_selected_select_options>( - any_cell_data: T, +pub fn make_selected_select_options( + cell_data: CellData, options: &[SelectOption], ) -> Vec { - if let Ok(type_option_cell_data) = any_cell_data.try_into() { - let ids = SelectOptionIds::from(type_option_cell_data.data); + if let Ok(ids) = cell_data.try_into_inner() { ids.iter() .flat_map(|option_id| options.iter().find(|option| &option.id == option_id).cloned()) .collect() @@ -100,13 +99,27 @@ pub trait SelectOptionOperation: TypeOptionDataEntry + Send + Sync { SelectOption::with_color(name, color) } - fn selected_select_option(&self, any_cell_data: AnyCellData) -> SelectOptionCellData; + fn selected_select_option(&self, cell_data: CellData) -> SelectOptionCellData; fn options(&self) -> &Vec; fn mut_options(&mut self) -> &mut Vec; } +impl CellDisplayable for T +where + T: SelectOptionOperation, +{ + fn display_data( + &self, + cell_data: CellData, + _decoded_field_type: &FieldType, + _field_rev: &FieldRevision, + ) -> FlowyResult { + Ok(self.selected_select_option(cell_data)) + } +} + pub fn select_option_operation(field_rev: &FieldRevision) -> FlowyResult> { let field_type: FieldType = field_rev.field_type_rev.into(); match &field_type { @@ -155,6 +168,12 @@ impl std::convert::TryFrom for SelectOptionIds { } } +impl std::convert::From for CellData { + fn from(any_cell_data: AnyCellData) -> Self { + any_cell_data.data.into() + } +} + impl FromCellString for SelectOptionIds { fn from_cell_str(s: &str) -> FlowyResult where diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs index 1808f0dc82..3266c04840 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs @@ -1,6 +1,6 @@ use crate::entities::FieldType; use crate::impl_type_option; -use crate::services::cell::{AnyCellData, CellData, CellDataChangeset, CellDataOperation, DecodedCellData}; +use crate::services::cell::{AnyCellData, CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable}; use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; use bytes::Bytes; use flowy_derive::ProtoBuf; @@ -40,23 +40,35 @@ impl_type_option!(CheckboxTypeOption, FieldType::Checkbox); const YES: &str = "Yes"; const NO: &str = "No"; +impl CellDisplayable for CheckboxTypeOption { + fn display_data( + &self, + cell_data: CellData, + _decoded_field_type: &FieldType, + _field_rev: &FieldRevision, + ) -> FlowyResult { + let s: String = cell_data.try_into_inner()?; + Ok(s == YES) + } +} + impl CellDataOperation for CheckboxTypeOption { fn decode_cell_data( &self, cell_data: CellData, decoded_field_type: &FieldType, _field_rev: &FieldRevision, - ) -> FlowyResult { + ) -> FlowyResult { if !decoded_field_type.is_checkbox() { - return Ok(DecodedCellData::default()); + return Ok(CellBytes::default()); } let s: String = cell_data.try_into_inner()?; if s == YES || s == NO { - return Ok(DecodedCellData::new(s)); + return Ok(CellBytes::new(s)); } - Ok(DecodedCellData::default()) + Ok(CellBytes::default()) } fn apply_changeset( diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs index 2200dad63c..f26e29de53 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs @@ -2,7 +2,8 @@ use crate::entities::{CellChangeset, FieldType}; use crate::entities::{CellIdentifier, CellIdentifierPayload}; use crate::impl_type_option; use crate::services::cell::{ - AnyCellData, CellData, CellDataChangeset, CellDataOperation, DecodedCellData, FromCellChangeset, FromCellString, + AnyCellData, CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable, FromCellChangeset, + FromCellString, }; use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; use bytes::Bytes; @@ -121,23 +122,33 @@ impl DateTypeOption { } } +impl CellDisplayable for DateTypeOption { + fn display_data( + &self, + cell_data: CellData, + _decoded_field_type: &FieldType, + _field_rev: &FieldRevision, + ) -> FlowyResult { + let timestamp = cell_data.try_into_inner()?; + Ok(self.today_desc_from_timestamp(timestamp.0)) + } +} + impl CellDataOperation for DateTypeOption { fn decode_cell_data( &self, cell_data: CellData, decoded_field_type: &FieldType, - _field_rev: &FieldRevision, - ) -> FlowyResult { + field_rev: &FieldRevision, + ) -> FlowyResult { // Return default data if the type_option_cell_data is not FieldType::DateTime. // It happens when switching from one field to another. // For example: // FieldType::RichText -> FieldType::DateTime, it will display empty content on the screen. if !decoded_field_type.is_date() { - return Ok(DecodedCellData::default()); + return Ok(CellBytes::default()); } - let timestamp = cell_data.try_into_inner()?; - let date = self.today_desc_from_timestamp(timestamp.0); - DecodedCellData::try_from_bytes(date) + CellBytes::from(self.display_data(cell_data, decoded_field_type, field_rev)?) } fn apply_changeset( diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/multi_select_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/multi_select_type_option.rs index 209ed234f7..902170307b 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/multi_select_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/multi_select_type_option.rs @@ -1,7 +1,7 @@ use crate::entities::FieldType; use crate::impl_type_option; -use crate::services::cell::{AnyCellData, CellData, CellDataChangeset, CellDataOperation, DecodedCellData}; +use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable}; use crate::services::field::select_option::{ make_selected_select_options, SelectOption, SelectOptionCellChangeset, SelectOptionCellData, SelectOptionIds, SelectOptionOperation, SELECTION_IDS_SEPARATOR, @@ -28,8 +28,8 @@ pub struct MultiSelectTypeOption { impl_type_option!(MultiSelectTypeOption, FieldType::MultiSelect); impl SelectOptionOperation for MultiSelectTypeOption { - fn selected_select_option(&self, any_cell_data: AnyCellData) -> SelectOptionCellData { - let select_options = make_selected_select_options(any_cell_data, &self.options); + fn selected_select_option(&self, cell_data: CellData) -> SelectOptionCellData { + let select_options = make_selected_select_options(cell_data, &self.options); SelectOptionCellData { options: self.options.clone(), select_options, @@ -50,24 +50,14 @@ impl CellDataOperation for MultiSele &self, cell_data: CellData, decoded_field_type: &FieldType, - _field_rev: &FieldRevision, - ) -> FlowyResult { + field_rev: &FieldRevision, + ) -> FlowyResult { if !decoded_field_type.is_select_option() { - return Ok(DecodedCellData::default()); + return Ok(CellBytes::default()); } - 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()) - .collect::>(); - - let cell_data = SelectOptionCellData { - options: self.options.clone(), - select_options, - }; - - DecodedCellData::try_from_bytes(cell_data) + let cell_data = self.display_data(cell_data, decoded_field_type, field_rev)?; + CellBytes::from(cell_data) } fn apply_changeset( diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs index c8f79df20c..c1c3dbdae3 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs @@ -1,7 +1,7 @@ use crate::impl_type_option; use crate::entities::FieldType; -use crate::services::cell::{CellData, CellDataChangeset, CellDataOperation, DecodedCellData}; +use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation}; use crate::services::field::number_currency::Currency; use crate::services::field::type_options::number_type_option::format::*; use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; @@ -110,15 +110,15 @@ impl CellDataOperation for NumberTypeOption { cell_data: CellData, decoded_field_type: &FieldType, _field_rev: &FieldRevision, - ) -> FlowyResult { + ) -> FlowyResult { if decoded_field_type.is_date() { - return Ok(DecodedCellData::default()); + return Ok(CellBytes::default()); } 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()), + Ok(num) => Ok(CellBytes::new(num.to_string())), + Err(_) => Ok(CellBytes::default()), } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/single_select_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/single_select_type_option.rs index 6094f8567b..35a2ac4853 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/single_select_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/single_select_type_option.rs @@ -1,6 +1,6 @@ use crate::entities::FieldType; use crate::impl_type_option; -use crate::services::cell::{AnyCellData, CellData, CellDataChangeset, CellDataOperation, DecodedCellData}; +use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable}; use crate::services::field::select_option::{ make_selected_select_options, SelectOption, SelectOptionCellChangeset, SelectOptionCellData, SelectOptionIds, SelectOptionOperation, @@ -24,8 +24,10 @@ pub struct SingleSelectTypeOption { impl_type_option!(SingleSelectTypeOption, FieldType::SingleSelect); impl SelectOptionOperation for SingleSelectTypeOption { - fn selected_select_option(&self, any_cell_data: AnyCellData) -> SelectOptionCellData { - let select_options = make_selected_select_options(any_cell_data, &self.options); + fn selected_select_option(&self, cell_data: CellData) -> SelectOptionCellData { + let mut select_options = make_selected_select_options(cell_data, &self.options); + // only keep option in single select + select_options.truncate(1); SelectOptionCellData { options: self.options.clone(), select_options, @@ -46,24 +48,14 @@ impl CellDataOperation for SingleSel &self, cell_data: CellData, decoded_field_type: &FieldType, - _field_rev: &FieldRevision, - ) -> FlowyResult { + field_rev: &FieldRevision, + ) -> FlowyResult { if !decoded_field_type.is_select_option() { - return Ok(DecodedCellData::default()); + return Ok(CellBytes::default()); } - let ids: SelectOptionIds = cell_data.try_into_inner()?; - let mut cell_data = SelectOptionCellData { - options: self.options.clone(), - select_options: vec![], - }; - 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()); - } - } - - DecodedCellData::try_from_bytes(cell_data) + let cell_data = self.display_data(cell_data, decoded_field_type, field_rev)?; + CellBytes::from(cell_data) } fn apply_changeset( diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs index a23366e2a4..4d4b4e65fc 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs @@ -1,7 +1,7 @@ use crate::entities::FieldType; use crate::impl_type_option; use crate::services::cell::{ - try_decode_cell_data, AnyCellData, CellData, CellDataChangeset, CellDataOperation, DecodedCellData, + try_decode_cell_data, AnyCellData, CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable, }; use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; use bytes::Bytes; @@ -32,13 +32,25 @@ pub struct RichTextTypeOption { } impl_type_option!(RichTextTypeOption, FieldType::RichText); +impl CellDisplayable for RichTextTypeOption { + fn display_data( + &self, + cell_data: CellData, + _decoded_field_type: &FieldType, + _field_rev: &FieldRevision, + ) -> FlowyResult { + let cell_str: String = cell_data.try_into_inner()?; + Ok(cell_str) + } +} + impl CellDataOperation for RichTextTypeOption { fn decode_cell_data( &self, cell_data: CellData, decoded_field_type: &FieldType, field_rev: &FieldRevision, - ) -> FlowyResult { + ) -> FlowyResult { if decoded_field_type.is_date() || decoded_field_type.is_single_select() || decoded_field_type.is_multi_select() @@ -46,8 +58,8 @@ impl CellDataOperation for RichTextTypeOption { { try_decode_cell_data(cell_data, field_rev, decoded_field_type, decoded_field_type) } else { - let cell_data: String = cell_data.try_into_inner()?; - Ok(DecodedCellData::new(cell_data)) + let content = self.display_data(cell_data, decoded_field_type, field_rev)?; + Ok(CellBytes::new(content)) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs index 74fc9fc26b..18144e3f2a 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs @@ -1,7 +1,7 @@ use crate::entities::FieldType; use crate::impl_type_option; use crate::services::cell::{ - AnyCellData, CellData, CellDataChangeset, CellDataOperation, DecodedCellData, FromCellString, + AnyCellData, CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable, FromCellString, }; use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; use bytes::Bytes; @@ -34,18 +34,30 @@ pub struct URLTypeOption { } impl_type_option!(URLTypeOption, FieldType::URL); +impl CellDisplayable for URLTypeOption { + fn display_data( + &self, + cell_data: CellData, + _decoded_field_type: &FieldType, + _field_rev: &FieldRevision, + ) -> FlowyResult { + let cell_data: URLCellData = cell_data.try_into_inner()?; + Ok(cell_data) + } +} + impl CellDataOperation for URLTypeOption { fn decode_cell_data( &self, cell_data: CellData, decoded_field_type: &FieldType, - _field_rev: &FieldRevision, - ) -> FlowyResult { + field_rev: &FieldRevision, + ) -> FlowyResult { if !decoded_field_type.is_url() { - return Ok(DecodedCellData::default()); + return Ok(CellBytes::default()); } - let cell_data: URLCellData = cell_data.try_into_inner()?; - DecodedCellData::try_from_bytes(cell_data) + let cell_data = self.display_data(cell_data, decoded_field_type, field_rev)?; + CellBytes::from(cell_data) } fn apply_changeset( diff --git a/frontend/rust-lib/flowy-grid/src/services/filter/impls/select_option_filter.rs b/frontend/rust-lib/flowy-grid/src/services/filter/impls/select_option_filter.rs index cd25bfef0e..3ef3a77b84 100644 --- a/frontend/rust-lib/flowy-grid/src/services/filter/impls/select_option_filter.rs +++ b/frontend/rust-lib/flowy-grid/src/services/filter/impls/select_option_filter.rs @@ -45,7 +45,7 @@ impl CellFilterOperation for MultiSelectTypeOption { return Ok(true); } - let selected_options = SelectedSelectOptions::from(self.selected_select_option(any_cell_data)); + let selected_options = SelectedSelectOptions::from(self.selected_select_option(any_cell_data.into())); Ok(filter.is_visible(&selected_options)) } } @@ -55,7 +55,7 @@ impl CellFilterOperation for SingleSelectTypeOption { if !any_cell_data.is_single_select() { return Ok(true); } - let selected_options = SelectedSelectOptions::from(self.selected_select_option(any_cell_data)); + let selected_options = SelectedSelectOptions::from(self.selected_select_option(any_cell_data.into())); Ok(filter.is_visible(&selected_options)) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs index 93d9317565..919e24f2bf 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -344,7 +344,7 @@ impl GridRevisionEditor { let row_rev = self.block_manager.get_row_rev(¶ms.row_id).await.ok()??; let cell_rev = row_rev.cells.get(¶ms.field_id)?.clone(); - let data = decode_any_cell_data(cell_rev.data, &field_rev).data; + let data = decode_any_cell_data(cell_rev.data, &field_rev).to_vec(); Some(Cell::new(¶ms.field_id, data)) } From f10e324b735a1d0cabdddae8c1694502072b00b0 Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 13 Jul 2022 11:09:13 +0800 Subject: [PATCH 3/7] refactor: type options directory --- frontend/rust-lib/flowy-grid/Flowy.toml | 1 - .../filter_entities/select_option_filter.rs | 2 +- .../rust-lib/flowy-grid/src/event_handler.rs | 6 +- .../src/services/cell/cell_operation.rs | 4 +- .../flowy-grid/src/services/field/mod.rs | 1 - .../type_options/checkbox_type_option.rs | 145 ---- .../checkbox_type_option/checkbox_option.rs | 75 ++ .../checkbox_option_entities.rs | 61 ++ .../type_options/checkbox_type_option/mod.rs | 6 + .../checkbox_type_option/tests.rs | 30 + .../field/type_options/date_type_option.rs | 672 ------------------ .../date_type_option/date_option.rs | 199 ++++++ .../date_type_option/date_option_entities.rs | 206 ++++++ .../type_options/date_type_option/mod.rs | 6 + .../type_options/date_type_option/tests.rs | 272 +++++++ .../src/services/field/type_options/mod.rs | 17 +- .../type_options/number_type_option/mod.rs | 7 +- .../number_type_option/number_option.rs | 149 ++++ .../number_option_entities.rs | 93 +++ .../number_type_option/number_type_option.rs | 376 ---------- .../type_options/number_type_option/tests.rs | 139 ++++ .../type_options/selection_type_option/mod.rs | 7 + .../multi_select_type_option.rs | 17 +- .../selection_type_option}/select_option.rs | 8 +- .../single_select_type_option.rs | 7 +- .../type_options/text_type_option/mod.rs | 2 + .../text_option.rs} | 11 +- .../field/type_options/url_type_option.rs | 206 ------ .../field/type_options/url_type_option/mod.rs | 6 + .../type_options/url_type_option/tests.rs | 67 ++ .../url_type_option/url_option.rs | 101 +++ .../url_type_option/url_option_entities.rs | 40 ++ .../services/field/type_options/util/mod.rs | 1 - .../filter/impls/select_option_filter.rs | 4 +- .../src/services/row/row_builder.rs | 2 +- .../flowy-grid/tests/grid/block_test/util.rs | 2 +- .../flowy-grid/tests/grid/cell_test/test.rs | 2 +- .../flowy-grid/tests/grid/field_test/test.rs | 2 +- .../flowy-grid/tests/grid/field_test/util.rs | 2 +- .../flowy-grid/tests/grid/grid_editor.rs | 2 +- 40 files changed, 1504 insertions(+), 1452 deletions(-) delete mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option_entities.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/mod.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/tests.rs delete mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option_entities.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/mod.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/tests.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option_entities.rs delete mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/tests.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/mod.rs rename frontend/rust-lib/flowy-grid/src/services/field/type_options/{ => selection_type_option}/multi_select_type_option.rs (93%) rename frontend/rust-lib/flowy-grid/src/services/field/{ => type_options/selection_type_option}/select_option.rs (97%) rename frontend/rust-lib/flowy-grid/src/services/field/type_options/{ => selection_type_option}/single_select_type_option.rs (96%) create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/mod.rs rename frontend/rust-lib/flowy-grid/src/services/field/type_options/{text_type_option.rs => text_type_option/text_option.rs} (95%) delete mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/mod.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/tests.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option_entities.rs diff --git a/frontend/rust-lib/flowy-grid/Flowy.toml b/frontend/rust-lib/flowy-grid/Flowy.toml index 94bd0c0225..00e9cf7c04 100644 --- a/frontend/rust-lib/flowy-grid/Flowy.toml +++ b/frontend/rust-lib/flowy-grid/Flowy.toml @@ -2,7 +2,6 @@ proto_input = [ "src/event_map.rs", "src/services/field/type_options", - "src/services/field/select_option.rs", "src/entities", "src/dart_notification.rs" ] diff --git a/frontend/rust-lib/flowy-grid/src/entities/filter_entities/select_option_filter.rs b/frontend/rust-lib/flowy-grid/src/entities/filter_entities/select_option_filter.rs index 73c39cb657..9eb4ff3fe9 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/filter_entities/select_option_filter.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/filter_entities/select_option_filter.rs @@ -1,4 +1,4 @@ -use crate::services::field::select_option::SelectOptionIds; +use crate::services::field::SelectOptionIds; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_error::ErrorCode; use flowy_grid_data_model::revision::GridFilterRevision; diff --git a/frontend/rust-lib/flowy-grid/src/event_handler.rs b/frontend/rust-lib/flowy-grid/src/event_handler.rs index 27f3da7cc1..52c3c3424c 100644 --- a/frontend/rust-lib/flowy-grid/src/event_handler.rs +++ b/frontend/rust-lib/flowy-grid/src/event_handler.rs @@ -1,9 +1,11 @@ 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, + default_type_option_builder_from_type, select_option_operation, type_option_builder_from_json_str, + DateChangesetParams, DateChangesetPayload, SelectOption, SelectOptionCellChangeset, + SelectOptionCellChangesetParams, SelectOptionCellChangesetPayload, SelectOptionCellData, SelectOptionChangeset, + SelectOptionChangesetPayload, }; use crate::services::row::make_row_from_row_rev; use flowy_error::{ErrorCode, FlowyError, FlowyResult}; diff --git a/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs b/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs index 584623811d..4bbeafa814 100644 --- a/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs +++ b/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs @@ -12,13 +12,13 @@ pub trait CellFilterOperation { } /// Return object that describes the cell. -pub trait CellDisplayable { +pub trait CellDisplayable { fn display_data( &self, cell_data: CellData, decoded_field_type: &FieldType, field_rev: &FieldRevision, - ) -> FlowyResult; + ) -> FlowyResult; } // CD: Short for CellData. This type is the type return by apply_changeset function. diff --git a/frontend/rust-lib/flowy-grid/src/services/field/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/mod.rs index 8a01d90d1f..c1b689fbf4 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/mod.rs @@ -1,5 +1,4 @@ mod field_builder; -pub mod select_option; pub(crate) mod type_options; pub use field_builder::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs deleted file mode 100644 index 3266c04840..0000000000 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs +++ /dev/null @@ -1,145 +0,0 @@ -use crate::entities::FieldType; -use crate::impl_type_option; -use crate::services::cell::{AnyCellData, CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable}; -use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; -use bytes::Bytes; -use flowy_derive::ProtoBuf; -use flowy_error::{FlowyError, FlowyResult}; -use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry}; -use serde::{Deserialize, Serialize}; - -#[derive(Default)] -pub struct CheckboxTypeOptionBuilder(CheckboxTypeOption); -impl_into_box_type_option_builder!(CheckboxTypeOptionBuilder); -impl_builder_from_json_str_and_from_bytes!(CheckboxTypeOptionBuilder, CheckboxTypeOption); - -impl CheckboxTypeOptionBuilder { - pub fn set_selected(mut self, is_selected: bool) -> Self { - self.0.is_selected = is_selected; - self - } -} - -impl TypeOptionBuilder for CheckboxTypeOptionBuilder { - fn field_type(&self) -> FieldType { - FieldType::Checkbox - } - - fn entry(&self) -> &dyn TypeOptionDataEntry { - &self.0 - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default, ProtoBuf)] -pub struct CheckboxTypeOption { - #[pb(index = 1)] - pub is_selected: bool, -} -impl_type_option!(CheckboxTypeOption, FieldType::Checkbox); - -const YES: &str = "Yes"; -const NO: &str = "No"; - -impl CellDisplayable for CheckboxTypeOption { - fn display_data( - &self, - cell_data: CellData, - _decoded_field_type: &FieldType, - _field_rev: &FieldRevision, - ) -> FlowyResult { - let s: String = cell_data.try_into_inner()?; - Ok(s == YES) - } -} - -impl CellDataOperation for CheckboxTypeOption { - fn decode_cell_data( - &self, - cell_data: CellData, - decoded_field_type: &FieldType, - _field_rev: &FieldRevision, - ) -> FlowyResult { - if !decoded_field_type.is_checkbox() { - return Ok(CellBytes::default()); - } - - let s: String = cell_data.try_into_inner()?; - if s == YES || s == NO { - return Ok(CellBytes::new(s)); - } - - Ok(CellBytes::default()) - } - - fn apply_changeset( - &self, - changeset: CellDataChangeset, - _cell_rev: Option, - ) -> Result { - let changeset = changeset.try_into_inner()?; - let s = match string_to_bool(&changeset) { - true => YES, - false => NO, - }; - Ok(s.to_string()) - } -} - -fn string_to_bool(bool_str: &str) -> bool { - let lower_case_str: &str = &bool_str.to_lowercase(); - match lower_case_str { - "1" => true, - "true" => true, - "yes" => true, - "0" => false, - "false" => false, - "no" => false, - _ => false, - } -} - -pub struct CheckboxCellData(pub String); - -impl CheckboxCellData { - pub fn is_check(&self) -> bool { - string_to_bool(&self.0) - } -} -impl std::convert::TryFrom for CheckboxCellData { - type Error = FlowyError; - - fn try_from(_value: AnyCellData) -> Result { - todo!() - } -} - -#[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::entities::FieldType; - - #[test] - fn checkout_box_description_test() { - let field_rev = FieldBuilder::from_field_type(&FieldType::Checkbox).build(); - let data = apply_cell_data_changeset("true", None, &field_rev).unwrap(); - assert_eq!(decode_any_cell_data(data, &field_rev).to_string(), YES); - - let data = apply_cell_data_changeset("1", None, &field_rev).unwrap(); - assert_eq!(decode_any_cell_data(data, &field_rev,).to_string(), YES); - - let data = apply_cell_data_changeset("yes", None, &field_rev).unwrap(); - assert_eq!(decode_any_cell_data(data, &field_rev,).to_string(), YES); - - let data = apply_cell_data_changeset("false", None, &field_rev).unwrap(); - assert_eq!(decode_any_cell_data(data, &field_rev,).to_string(), NO); - - let data = apply_cell_data_changeset("no", None, &field_rev).unwrap(); - assert_eq!(decode_any_cell_data(data, &field_rev,).to_string(), NO); - - let data = apply_cell_data_changeset("12", None, &field_rev).unwrap(); - assert_eq!(decode_any_cell_data(data, &field_rev,).to_string(), NO); - } -} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option.rs new file mode 100644 index 0000000000..b6e1d39410 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option.rs @@ -0,0 +1,75 @@ +use crate::entities::FieldType; +use crate::impl_type_option; +use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable}; +use crate::services::field::{BoxTypeOptionBuilder, CheckboxCellData, TypeOptionBuilder}; +use bytes::Bytes; +use flowy_derive::ProtoBuf; +use flowy_error::{FlowyError, FlowyResult}; +use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry}; +use serde::{Deserialize, Serialize}; + +#[derive(Default)] +pub struct CheckboxTypeOptionBuilder(CheckboxTypeOption); +impl_into_box_type_option_builder!(CheckboxTypeOptionBuilder); +impl_builder_from_json_str_and_from_bytes!(CheckboxTypeOptionBuilder, CheckboxTypeOption); + +impl CheckboxTypeOptionBuilder { + pub fn set_selected(mut self, is_selected: bool) -> Self { + self.0.is_selected = is_selected; + self + } +} + +impl TypeOptionBuilder for CheckboxTypeOptionBuilder { + fn field_type(&self) -> FieldType { + FieldType::Checkbox + } + + fn entry(&self) -> &dyn TypeOptionDataEntry { + &self.0 + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default, ProtoBuf)] +pub struct CheckboxTypeOption { + #[pb(index = 1)] + pub is_selected: bool, +} +impl_type_option!(CheckboxTypeOption, FieldType::Checkbox); + +impl CellDisplayable for CheckboxTypeOption { + fn display_data( + &self, + cell_data: CellData, + _decoded_field_type: &FieldType, + _field_rev: &FieldRevision, + ) -> FlowyResult { + let cell_data = cell_data.try_into_inner()?; + Ok(CellBytes::new(cell_data)) + } +} + +impl CellDataOperation for CheckboxTypeOption { + fn decode_cell_data( + &self, + cell_data: CellData, + decoded_field_type: &FieldType, + field_rev: &FieldRevision, + ) -> FlowyResult { + if !decoded_field_type.is_checkbox() { + return Ok(CellBytes::default()); + } + + self.display_data(cell_data, decoded_field_type, field_rev) + } + + fn apply_changeset( + &self, + changeset: CellDataChangeset, + _cell_rev: Option, + ) -> Result { + let changeset = changeset.try_into_inner()?; + let cell_data = CheckboxCellData::from_str(&changeset); + Ok(cell_data.to_string()) + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option_entities.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option_entities.rs new file mode 100644 index 0000000000..99f9866d41 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option_entities.rs @@ -0,0 +1,61 @@ +use crate::services::cell::{AnyCellData, FromCellString}; +use flowy_error::{FlowyError, FlowyResult}; + +pub const YES: &str = "Yes"; +pub const NO: &str = "No"; + +pub struct CheckboxCellData(pub String); + +impl CheckboxCellData { + pub fn from_str(s: &str) -> Self { + let lower_case_str: &str = &s.to_lowercase(); + let val = match lower_case_str { + "1" => Some(true), + "true" => Some(true), + "yes" => Some(true), + "0" => Some(false), + "false" => Some(false), + "no" => Some(false), + _ => None, + }; + + match val { + Some(true) => Self(YES.to_string()), + Some(false) => Self(NO.to_string()), + None => Self("".to_string()), + } + } + + pub fn is_check(&self) -> bool { + &self.0 == YES + } +} + +impl AsRef<[u8]> for CheckboxCellData { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl std::convert::TryFrom for CheckboxCellData { + type Error = FlowyError; + + fn try_from(value: AnyCellData) -> Result { + Ok(Self::from_str(&value.data)) + } +} + +impl FromCellString for CheckboxCellData { + fn from_cell_str(s: &str) -> FlowyResult + where + Self: Sized, + { + Ok(Self::from_str(s)) + } +} + +impl ToString for CheckboxCellData { + fn to_string(&self) -> String { + self.0.clone() + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/mod.rs new file mode 100644 index 0000000000..705b9bcdb0 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/mod.rs @@ -0,0 +1,6 @@ +mod checkbox_option; +mod checkbox_option_entities; +mod tests; + +pub use checkbox_option::*; +pub use checkbox_option_entities::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/tests.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/tests.rs new file mode 100644 index 0000000000..9e041cc415 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/tests.rs @@ -0,0 +1,30 @@ +#[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::entities::FieldType; + + #[test] + fn checkout_box_description_test() { + let field_rev = FieldBuilder::from_field_type(&FieldType::Checkbox).build(); + let data = apply_cell_data_changeset("true", None, &field_rev).unwrap(); + assert_eq!(decode_any_cell_data(data, &field_rev).to_string(), YES); + + let data = apply_cell_data_changeset("1", None, &field_rev).unwrap(); + assert_eq!(decode_any_cell_data(data, &field_rev,).to_string(), YES); + + let data = apply_cell_data_changeset("yes", None, &field_rev).unwrap(); + assert_eq!(decode_any_cell_data(data, &field_rev,).to_string(), YES); + + let data = apply_cell_data_changeset("false", None, &field_rev).unwrap(); + assert_eq!(decode_any_cell_data(data, &field_rev,).to_string(), NO); + + let data = apply_cell_data_changeset("no", None, &field_rev).unwrap(); + assert_eq!(decode_any_cell_data(data, &field_rev,).to_string(), NO); + + let data = apply_cell_data_changeset("12", None, &field_rev).unwrap(); + assert_eq!(decode_any_cell_data(data, &field_rev,).to_string(), NO); + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs deleted file mode 100644 index f26e29de53..0000000000 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs +++ /dev/null @@ -1,672 +0,0 @@ -use crate::entities::{CellChangeset, FieldType}; -use crate::entities::{CellIdentifier, CellIdentifierPayload}; -use crate::impl_type_option; -use crate::services::cell::{ - AnyCellData, CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable, 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::{internal_error, ErrorCode, FlowyError, FlowyResult}; -use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry}; -use serde::{Deserialize, Serialize}; -use strum_macros::EnumIter; - -// Date -#[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)] -pub struct DateTypeOption { - #[pb(index = 1)] - pub date_format: DateFormat, - - #[pb(index = 2)] - pub time_format: TimeFormat, - - #[pb(index = 3)] - pub include_time: bool, -} -impl_type_option!(DateTypeOption, FieldType::DateTime); - -impl DateTypeOption { - #[allow(dead_code)] - pub fn new() -> Self { - Self::default() - } - - fn today_desc_from_timestamp(&self, timestamp: i64) -> DateCellData { - let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0); - self.date_from_native(native) - } - - fn date_from_native(&self, native: chrono::NaiveDateTime) -> DateCellData { - if native.timestamp() == 0 { - return DateCellData::default(); - } - - let time = native.time(); - let has_time = time.hour() != 0 || time.second() != 0; - - let utc = self.utc_date_time_from_native(native); - let fmt = self.date_format.format_str(); - let date = format!("{}", utc.format_with_items(StrftimeItems::new(fmt))); - - let mut time = "".to_string(); - if has_time { - let fmt = format!("{} {}", self.date_format.format_str(), self.time_format.format_str()); - time = format!("{}", utc.format_with_items(StrftimeItems::new(&fmt))).replace(&date, ""); - } - - let timestamp = native.timestamp(); - DateCellData { date, time, timestamp } - } - - fn date_fmt(&self, time: &Option) -> String { - if self.include_time { - match time.as_ref() { - None => self.date_format.format_str().to_string(), - Some(time_str) => { - if time_str.is_empty() { - self.date_format.format_str().to_string() - } else { - format!("{} {}", self.date_format.format_str(), self.time_format.format_str()) - } - } - } - } else { - self.date_format.format_str().to_string() - } - } - - fn timestamp_from_utc_with_time( - &self, - utc: &chrono::DateTime, - time: &Option, - ) -> FlowyResult { - if let Some(time_str) = time.as_ref() { - if !time_str.is_empty() { - let date_str = format!( - "{}{}", - utc.format_with_items(StrftimeItems::new(self.date_format.format_str())), - &time_str - ); - - return match NaiveDateTime::parse_from_str(&date_str, &self.date_fmt(time)) { - Ok(native) => { - let utc = self.utc_date_time_from_native(native); - Ok(utc.timestamp()) - } - Err(_e) => { - let msg = format!("Parse {} failed", date_str); - Err(FlowyError::new(ErrorCode::InvalidDateTimeFormat, &msg)) - } - }; - } - } - - Ok(utc.timestamp()) - } - - fn utc_date_time_from_timestamp(&self, timestamp: i64) -> chrono::DateTime { - let native = NaiveDateTime::from_timestamp(timestamp, 0); - let native2 = NaiveDateTime::from_timestamp(timestamp, 0); - - if native > native2 {} - - self.utc_date_time_from_native(native) - } - - fn utc_date_time_from_native(&self, naive: chrono::NaiveDateTime) -> chrono::DateTime { - chrono::DateTime::::from_utc(naive, chrono::Utc) - } -} - -impl CellDisplayable for DateTypeOption { - fn display_data( - &self, - cell_data: CellData, - _decoded_field_type: &FieldType, - _field_rev: &FieldRevision, - ) -> FlowyResult { - let timestamp = cell_data.try_into_inner()?; - Ok(self.today_desc_from_timestamp(timestamp.0)) - } -} - -impl CellDataOperation for DateTypeOption { - fn decode_cell_data( - &self, - cell_data: CellData, - decoded_field_type: &FieldType, - field_rev: &FieldRevision, - ) -> FlowyResult { - // Return default data if the type_option_cell_data is not FieldType::DateTime. - // It happens when switching from one field to another. - // For example: - // FieldType::RichText -> FieldType::DateTime, it will display empty content on the screen. - if !decoded_field_type.is_date() { - return Ok(CellBytes::default()); - } - CellBytes::from(self.display_data(cell_data, decoded_field_type, field_rev)?) - } - - fn apply_changeset( - &self, - changeset: CellDataChangeset, - _cell_rev: Option, - ) -> Result { - let changeset = changeset.try_into_inner()?; - let cell_data = match changeset.date_timestamp() { - None => 0, - 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); - self.timestamp_from_utc_with_time(&utc, &time)? - } - _ => date_timestamp, - }, - }; - - Ok(cell_data.to_string()) - } -} - -pub struct DateTimestamp(i64); -impl AsRef for DateTimestamp { - fn as_ref(&self) -> &i64 { - &self.0 - } -} - -impl std::convert::From for i64 { - fn from(timestamp: DateTimestamp) -> Self { - timestamp.0 - } -} - -impl FromCellString for DateTimestamp { - fn from_cell_str(s: &str) -> FlowyResult - where - Self: Sized, - { - let num = s.parse::().unwrap_or(0); - Ok(DateTimestamp(num)) - } -} - -impl std::convert::From for DateTimestamp { - fn from(data: AnyCellData) -> Self { - let num = data.data.parse::().unwrap_or(0); - DateTimestamp(num) - } -} - -#[derive(Default)] -pub struct DateTypeOptionBuilder(DateTypeOption); -impl_into_box_type_option_builder!(DateTypeOptionBuilder); -impl_builder_from_json_str_and_from_bytes!(DateTypeOptionBuilder, DateTypeOption); - -impl DateTypeOptionBuilder { - pub fn date_format(mut self, date_format: DateFormat) -> Self { - self.0.date_format = date_format; - self - } - - pub fn time_format(mut self, time_format: TimeFormat) -> Self { - self.0.time_format = time_format; - self - } -} -impl TypeOptionBuilder for DateTypeOptionBuilder { - fn field_type(&self) -> FieldType { - FieldType::DateTime - } - - fn entry(&self) -> &dyn TypeOptionDataEntry { - &self.0 - } -} - -#[derive(Clone, Debug, Copy, EnumIter, Serialize, Deserialize, ProtoBuf_Enum)] -pub enum DateFormat { - Local = 0, - US = 1, - ISO = 2, - Friendly = 3, -} -impl std::default::Default for DateFormat { - fn default() -> Self { - DateFormat::Friendly - } -} - -impl std::convert::From for DateFormat { - fn from(value: i32) -> Self { - match value { - 0 => DateFormat::Local, - 1 => DateFormat::US, - 2 => DateFormat::ISO, - 3 => DateFormat::Friendly, - _ => { - tracing::error!("Unsupported date format, fallback to friendly"); - DateFormat::Friendly - } - } - } -} - -impl DateFormat { - pub fn value(&self) -> i32 { - *self as i32 - } - // https://docs.rs/chrono/0.4.19/chrono/format/strftime/index.html - pub fn format_str(&self) -> &'static str { - match self { - DateFormat::Local => "%Y/%m/%d", - DateFormat::US => "%Y/%m/%d", - DateFormat::ISO => "%Y-%m-%d", - DateFormat::Friendly => "%b %d,%Y", - } - } -} - -#[derive(Clone, Copy, PartialEq, Eq, EnumIter, Debug, Hash, Serialize, Deserialize, ProtoBuf_Enum)] -pub enum TimeFormat { - TwelveHour = 0, - TwentyFourHour = 1, -} - -impl std::convert::From for TimeFormat { - fn from(value: i32) -> Self { - match value { - 0 => TimeFormat::TwelveHour, - 1 => TimeFormat::TwentyFourHour, - _ => { - tracing::error!("Unsupported time format, fallback to TwentyFourHour"); - TimeFormat::TwentyFourHour - } - } - } -} - -impl TimeFormat { - pub fn value(&self) -> i32 { - *self as i32 - } - - // https://docs.rs/chrono/0.4.19/chrono/format/strftime/index.html - pub fn format_str(&self) -> &'static str { - match self { - TimeFormat::TwelveHour => "%I:%M %p", - TimeFormat::TwentyFourHour => "%R", - } - } -} - -impl std::default::Default for TimeFormat { - fn default() -> Self { - TimeFormat::TwentyFourHour - } -} - -#[derive(Clone, Debug, Default, ProtoBuf)] -pub struct DateCellData { - #[pb(index = 1)] - pub date: String, - - #[pb(index = 2)] - pub time: String, - - #[pb(index = 3)] - pub timestamp: i64, -} - -#[derive(Clone, Debug, Default, ProtoBuf)] -pub struct DateChangesetPayload { - #[pb(index = 1)] - pub cell_identifier: CellIdentifierPayload, - - #[pb(index = 2, one_of)] - pub date: Option, - - #[pb(index = 3, one_of)] - pub time: Option, -} - -pub struct DateChangesetParams { - pub cell_identifier: CellIdentifier, - pub date: Option, - pub time: Option, -} - -impl TryInto for DateChangesetPayload { - type Error = ErrorCode; - - fn try_into(self) -> Result { - let cell_identifier: CellIdentifier = self.cell_identifier.try_into()?; - Ok(DateChangesetParams { - cell_identifier, - date: self.date, - time: self.time, - }) - } -} - -impl std::convert::From for CellChangeset { - fn from(params: DateChangesetParams) -> Self { - let changeset = DateCellChangeset { - date: params.date, - time: params.time, - }; - let s = serde_json::to_string(&changeset).unwrap(); - CellChangeset { - grid_id: params.cell_identifier.grid_id, - row_id: params.cell_identifier.row_id, - field_id: params.cell_identifier.field_id, - content: Some(s), - } - } -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct DateCellChangeset { - pub date: Option, - pub time: Option, -} - -impl DateCellChangeset { - pub fn date_timestamp(&self) -> Option { - if let Some(date) = &self.date { - match date.parse::() { - Ok(date_timestamp) => Some(date_timestamp), - Err(_) => None, - } - } else { - None - } - } -} - -impl FromCellChangeset for DateCellChangeset { - fn from_changeset(changeset: String) -> FlowyResult - where - Self: Sized, - { - serde_json::from_str::(&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::{DateCellChangeset, DateCellData, DateFormat, DateTypeOption, TimeFormat}; - use flowy_grid_data_model::revision::FieldRevision; - use strum::IntoEnumIterator; - - #[test] - fn date_type_option_invalid_input_test() { - let type_option = DateTypeOption::default(); - let field_type = FieldType::DateTime; - let field_rev = FieldBuilder::from_field_type(&field_type).build(); - assert_changeset_result( - &type_option, - DateCellChangeset { - date: Some("1e".to_string()), - time: Some("23:00".to_owned()), - }, - &field_type, - &field_rev, - "", - ); - } - - #[test] - fn date_type_option_date_format_test() { - let mut type_option = DateTypeOption::default(); - let field_rev = FieldBuilder::from_field_type(&FieldType::DateTime).build(); - for date_format in DateFormat::iter() { - type_option.date_format = date_format; - match date_format { - DateFormat::Friendly => { - assert_decode_timestamp(1647251762, &type_option, &field_rev, "Mar 14,2022"); - } - DateFormat::US => { - assert_decode_timestamp(1647251762, &type_option, &field_rev, "2022/03/14"); - } - DateFormat::ISO => { - assert_decode_timestamp(1647251762, &type_option, &field_rev, "2022-03-14"); - } - DateFormat::Local => { - assert_decode_timestamp(1647251762, &type_option, &field_rev, "2022/03/14"); - } - } - } - } - - #[test] - fn date_type_option_time_format_test() { - let mut type_option = DateTypeOption::default(); - let field_type = FieldType::DateTime; - let field_rev = FieldBuilder::from_field_type(&field_type).build(); - - for time_format in TimeFormat::iter() { - type_option.time_format = time_format; - type_option.include_time = true; - match time_format { - TimeFormat::TwentyFourHour => { - assert_changeset_result( - &type_option, - DateCellChangeset { - date: Some(1653609600.to_string()), - time: None, - }, - &field_type, - &field_rev, - "May 27,2022", - ); - assert_changeset_result( - &type_option, - DateCellChangeset { - date: Some(1653609600.to_string()), - time: Some("23:00".to_owned()), - }, - &field_type, - &field_rev, - "May 27,2022 23:00", - ); - } - TimeFormat::TwelveHour => { - assert_changeset_result( - &type_option, - DateCellChangeset { - date: Some(1653609600.to_string()), - time: None, - }, - &field_type, - &field_rev, - "May 27,2022", - ); - // - assert_changeset_result( - &type_option, - DateCellChangeset { - date: Some(1653609600.to_string()), - time: Some("".to_owned()), - }, - &field_type, - &field_rev, - "May 27,2022", - ); - - assert_changeset_result( - &type_option, - DateCellChangeset { - date: Some(1653609600.to_string()), - time: Some("11:23 pm".to_owned()), - }, - &field_type, - &field_rev, - "May 27,2022 11:23 PM", - ); - } - } - } - } - - #[test] - fn date_type_option_apply_changeset_test() { - let mut type_option = DateTypeOption::new(); - let field_type = FieldType::DateTime; - let field_rev = FieldBuilder::from_field_type(&field_type).build(); - let date_timestamp = "1653609600".to_owned(); - - assert_changeset_result( - &type_option, - DateCellChangeset { - date: Some(date_timestamp.clone()), - time: None, - }, - &field_type, - &field_rev, - "May 27,2022", - ); - - type_option.include_time = true; - assert_changeset_result( - &type_option, - DateCellChangeset { - date: Some(date_timestamp.clone()), - time: None, - }, - &field_type, - &field_rev, - "May 27,2022", - ); - - assert_changeset_result( - &type_option, - DateCellChangeset { - date: Some(date_timestamp.clone()), - time: Some("1:00".to_owned()), - }, - &field_type, - &field_rev, - "May 27,2022 01:00", - ); - - type_option.time_format = TimeFormat::TwelveHour; - assert_changeset_result( - &type_option, - DateCellChangeset { - date: Some(date_timestamp), - time: Some("1:00 am".to_owned()), - }, - &field_type, - &field_rev, - "May 27,2022 01:00 AM", - ); - } - - #[test] - #[should_panic] - fn date_type_option_apply_changeset_error_test() { - let mut type_option = DateTypeOption::new(); - type_option.include_time = true; - let field_rev = FieldBuilder::from_field_type(&FieldType::DateTime).build(); - let date_timestamp = "1653609600".to_owned(); - - assert_changeset_result( - &type_option, - DateCellChangeset { - date: Some(date_timestamp.clone()), - time: Some("1:".to_owned()), - }, - &FieldType::DateTime, - &field_rev, - "May 27,2022 01:00", - ); - - assert_changeset_result( - &type_option, - DateCellChangeset { - date: Some(date_timestamp), - time: Some("1:00".to_owned()), - }, - &FieldType::DateTime, - &field_rev, - "May 27,2022 01:00", - ); - } - - #[test] - #[should_panic] - fn date_type_option_twelve_hours_to_twenty_four_hours() { - let mut type_option = DateTypeOption::new(); - type_option.include_time = true; - let field_rev = FieldBuilder::from_field_type(&FieldType::DateTime).build(); - let date_timestamp = "1653609600".to_owned(); - - assert_changeset_result( - &type_option, - DateCellChangeset { - date: Some(date_timestamp), - time: Some("1:00 am".to_owned()), - }, - &FieldType::DateTime, - &field_rev, - "May 27,2022 01:00", - ); - } - - fn assert_changeset_result( - type_option: &DateTypeOption, - 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(), - decode_cell_data(encoded_data, type_option, field_rev) - ); - } - - fn assert_decode_timestamp( - timestamp: i64, - type_option: &DateTypeOption, - field_rev: &FieldRevision, - expected: &str, - ) { - 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(), - decode_cell_data(encoded_data, type_option, field_rev) - ); - } - - fn decode_cell_data(encoded_data: String, type_option: &DateTypeOption, field_rev: &FieldRevision) -> String { - let decoded_data = type_option - .decode_cell_data(encoded_data.into(), &FieldType::DateTime, field_rev) - .unwrap() - .parse::() - .unwrap(); - - if type_option.include_time { - format!("{}{}", decoded_data.date, decoded_data.time) - } else { - decoded_data.date - } - } -} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option.rs new file mode 100644 index 0000000000..63ed4ee2a9 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option.rs @@ -0,0 +1,199 @@ +use crate::entities::{FieldType}; + +use crate::impl_type_option; +use crate::services::cell::{ + AnyCellData, CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable, FromCellChangeset, + FromCellString, +}; +use crate::services::field::{ + BoxTypeOptionBuilder, DateCellChangeset, DateCellData, DateFormat, DateTimestamp, TimeFormat, TypeOptionBuilder, +}; +use bytes::Bytes; +use chrono::format::strftime::StrftimeItems; +use chrono::{NaiveDateTime, Timelike}; +use flowy_derive::{ProtoBuf}; +use flowy_error::{ErrorCode, FlowyError, FlowyResult}; +use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry}; +use serde::{Deserialize, Serialize}; + + +// Date +#[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)] +pub struct DateTypeOption { + #[pb(index = 1)] + pub date_format: DateFormat, + + #[pb(index = 2)] + pub time_format: TimeFormat, + + #[pb(index = 3)] + pub include_time: bool, +} +impl_type_option!(DateTypeOption, FieldType::DateTime); + +impl DateTypeOption { + #[allow(dead_code)] + pub fn new() -> Self { + Self::default() + } + + fn today_desc_from_timestamp>(&self, timestamp: T) -> DateCellData { + let timestamp = *timestamp.as_ref(); + let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0); + self.date_from_native(native) + } + + fn date_from_native(&self, native: chrono::NaiveDateTime) -> DateCellData { + if native.timestamp() == 0 { + return DateCellData::default(); + } + + let time = native.time(); + let has_time = time.hour() != 0 || time.second() != 0; + + let utc = self.utc_date_time_from_native(native); + let fmt = self.date_format.format_str(); + let date = format!("{}", utc.format_with_items(StrftimeItems::new(fmt))); + + let mut time = "".to_string(); + if has_time { + let fmt = format!("{} {}", self.date_format.format_str(), self.time_format.format_str()); + time = format!("{}", utc.format_with_items(StrftimeItems::new(&fmt))).replace(&date, ""); + } + + let timestamp = native.timestamp(); + DateCellData { date, time, timestamp } + } + + fn date_fmt(&self, time: &Option) -> String { + if self.include_time { + match time.as_ref() { + None => self.date_format.format_str().to_string(), + Some(time_str) => { + if time_str.is_empty() { + self.date_format.format_str().to_string() + } else { + format!("{} {}", self.date_format.format_str(), self.time_format.format_str()) + } + } + } + } else { + self.date_format.format_str().to_string() + } + } + + fn timestamp_from_utc_with_time( + &self, + utc: &chrono::DateTime, + time: &Option, + ) -> FlowyResult { + if let Some(time_str) = time.as_ref() { + if !time_str.is_empty() { + let date_str = format!( + "{}{}", + utc.format_with_items(StrftimeItems::new(self.date_format.format_str())), + &time_str + ); + + return match NaiveDateTime::parse_from_str(&date_str, &self.date_fmt(time)) { + Ok(native) => { + let utc = self.utc_date_time_from_native(native); + Ok(utc.timestamp()) + } + Err(_e) => { + let msg = format!("Parse {} failed", date_str); + Err(FlowyError::new(ErrorCode::InvalidDateTimeFormat, &msg)) + } + }; + } + } + + Ok(utc.timestamp()) + } + + fn utc_date_time_from_timestamp(&self, timestamp: i64) -> chrono::DateTime { + let native = NaiveDateTime::from_timestamp(timestamp, 0); + self.utc_date_time_from_native(native) + } + + fn utc_date_time_from_native(&self, naive: chrono::NaiveDateTime) -> chrono::DateTime { + chrono::DateTime::::from_utc(naive, chrono::Utc) + } +} + +impl CellDisplayable for DateTypeOption { + fn display_data( + &self, + cell_data: CellData, + _decoded_field_type: &FieldType, + _field_rev: &FieldRevision, + ) -> FlowyResult { + let timestamp = cell_data.try_into_inner()?; + CellBytes::from(self.today_desc_from_timestamp(timestamp)) + } +} + +impl CellDataOperation for DateTypeOption { + fn decode_cell_data( + &self, + cell_data: CellData, + decoded_field_type: &FieldType, + field_rev: &FieldRevision, + ) -> FlowyResult { + // Return default data if the type_option_cell_data is not FieldType::DateTime. + // It happens when switching from one field to another. + // For example: + // FieldType::RichText -> FieldType::DateTime, it will display empty content on the screen. + if !decoded_field_type.is_date() { + return Ok(CellBytes::default()); + } + self.display_data(cell_data, decoded_field_type, field_rev) + } + + fn apply_changeset( + &self, + changeset: CellDataChangeset, + _cell_rev: Option, + ) -> Result { + let changeset = changeset.try_into_inner()?; + let cell_data = match changeset.date_timestamp() { + None => 0, + 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); + self.timestamp_from_utc_with_time(&utc, &time)? + } + _ => date_timestamp, + }, + }; + + Ok(cell_data.to_string()) + } +} + +#[derive(Default)] +pub struct DateTypeOptionBuilder(DateTypeOption); +impl_into_box_type_option_builder!(DateTypeOptionBuilder); +impl_builder_from_json_str_and_from_bytes!(DateTypeOptionBuilder, DateTypeOption); + +impl DateTypeOptionBuilder { + pub fn date_format(mut self, date_format: DateFormat) -> Self { + self.0.date_format = date_format; + self + } + + pub fn time_format(mut self, time_format: TimeFormat) -> Self { + self.0.time_format = time_format; + self + } +} +impl TypeOptionBuilder for DateTypeOptionBuilder { + fn field_type(&self) -> FieldType { + FieldType::DateTime + } + + fn entry(&self) -> &dyn TypeOptionDataEntry { + &self.0 + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option_entities.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option_entities.rs new file mode 100644 index 0000000000..aa63eee9dd --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option_entities.rs @@ -0,0 +1,206 @@ +use crate::entities::CellChangeset; +use crate::entities::{CellIdentifier, CellIdentifierPayload}; +use crate::services::cell::{AnyCellData, FromCellChangeset, FromCellString}; + +use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; +use flowy_error::{internal_error, ErrorCode, FlowyResult}; + +use serde::{Deserialize, Serialize}; +use strum_macros::EnumIter; + +#[derive(Clone, Debug, Default, ProtoBuf)] +pub struct DateCellData { + #[pb(index = 1)] + pub date: String, + + #[pb(index = 2)] + pub time: String, + + #[pb(index = 3)] + pub timestamp: i64, +} + +#[derive(Clone, Debug, Default, ProtoBuf)] +pub struct DateChangesetPayload { + #[pb(index = 1)] + pub cell_identifier: CellIdentifierPayload, + + #[pb(index = 2, one_of)] + pub date: Option, + + #[pb(index = 3, one_of)] + pub time: Option, +} + +pub struct DateChangesetParams { + pub cell_identifier: CellIdentifier, + pub date: Option, + pub time: Option, +} + +impl TryInto for DateChangesetPayload { + type Error = ErrorCode; + + fn try_into(self) -> Result { + let cell_identifier: CellIdentifier = self.cell_identifier.try_into()?; + Ok(DateChangesetParams { + cell_identifier, + date: self.date, + time: self.time, + }) + } +} + +impl std::convert::From for CellChangeset { + fn from(params: DateChangesetParams) -> Self { + let changeset = DateCellChangeset { + date: params.date, + time: params.time, + }; + let s = serde_json::to_string(&changeset).unwrap(); + CellChangeset { + grid_id: params.cell_identifier.grid_id, + row_id: params.cell_identifier.row_id, + field_id: params.cell_identifier.field_id, + content: Some(s), + } + } +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct DateCellChangeset { + pub date: Option, + pub time: Option, +} + +impl DateCellChangeset { + pub fn date_timestamp(&self) -> Option { + if let Some(date) = &self.date { + match date.parse::() { + Ok(date_timestamp) => Some(date_timestamp), + Err(_) => None, + } + } else { + None + } + } +} + +impl FromCellChangeset for DateCellChangeset { + fn from_changeset(changeset: String) -> FlowyResult + where + Self: Sized, + { + serde_json::from_str::(&changeset).map_err(internal_error) + } +} +pub struct DateTimestamp(i64); +impl AsRef for DateTimestamp { + fn as_ref(&self) -> &i64 { + &self.0 + } +} + +impl std::convert::From for i64 { + fn from(timestamp: DateTimestamp) -> Self { + timestamp.0 + } +} + +impl FromCellString for DateTimestamp { + fn from_cell_str(s: &str) -> FlowyResult + where + Self: Sized, + { + let num = s.parse::().unwrap_or(0); + Ok(DateTimestamp(num)) + } +} + +impl std::convert::From for DateTimestamp { + fn from(data: AnyCellData) -> Self { + let num = data.data.parse::().unwrap_or(0); + DateTimestamp(num) + } +} +#[derive(Clone, Debug, Copy, EnumIter, Serialize, Deserialize, ProtoBuf_Enum)] +pub enum DateFormat { + Local = 0, + US = 1, + ISO = 2, + Friendly = 3, +} +impl std::default::Default for DateFormat { + fn default() -> Self { + DateFormat::Friendly + } +} + +impl std::convert::From for DateFormat { + fn from(value: i32) -> Self { + match value { + 0 => DateFormat::Local, + 1 => DateFormat::US, + 2 => DateFormat::ISO, + 3 => DateFormat::Friendly, + _ => { + tracing::error!("Unsupported date format, fallback to friendly"); + DateFormat::Friendly + } + } + } +} + +impl DateFormat { + pub fn value(&self) -> i32 { + *self as i32 + } + // https://docs.rs/chrono/0.4.19/chrono/format/strftime/index.html + pub fn format_str(&self) -> &'static str { + match self { + DateFormat::Local => "%Y/%m/%d", + DateFormat::US => "%Y/%m/%d", + DateFormat::ISO => "%Y-%m-%d", + DateFormat::Friendly => "%b %d,%Y", + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, EnumIter, Debug, Hash, Serialize, Deserialize, ProtoBuf_Enum)] +pub enum TimeFormat { + TwelveHour = 0, + TwentyFourHour = 1, +} + +impl std::convert::From for TimeFormat { + fn from(value: i32) -> Self { + match value { + 0 => TimeFormat::TwelveHour, + 1 => TimeFormat::TwentyFourHour, + _ => { + tracing::error!("Unsupported time format, fallback to TwentyFourHour"); + TimeFormat::TwentyFourHour + } + } + } +} + +impl TimeFormat { + pub fn value(&self) -> i32 { + *self as i32 + } + + // https://docs.rs/chrono/0.4.19/chrono/format/strftime/index.html + pub fn format_str(&self) -> &'static str { + match self { + TimeFormat::TwelveHour => "%I:%M %p", + TimeFormat::TwentyFourHour => "%R", + } + } +} + +impl std::default::Default for TimeFormat { + fn default() -> Self { + TimeFormat::TwentyFourHour + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/mod.rs new file mode 100644 index 0000000000..9785924a8c --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/mod.rs @@ -0,0 +1,6 @@ +mod date_option; +mod date_option_entities; +mod tests; + +pub use date_option::*; +pub use date_option_entities::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/tests.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/tests.rs new file mode 100644 index 0000000000..22e6f08b49 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/tests.rs @@ -0,0 +1,272 @@ +#[cfg(test)] +mod tests { + use crate::entities::FieldType; + use crate::services::cell::{CellDataChangeset, CellDataOperation}; + use crate::services::field::FieldBuilder; + use crate::services::field::{DateCellChangeset, DateCellData, DateFormat, DateTypeOption, TimeFormat}; + use flowy_grid_data_model::revision::FieldRevision; + use strum::IntoEnumIterator; + + #[test] + fn date_type_option_invalid_input_test() { + let type_option = DateTypeOption::default(); + let field_type = FieldType::DateTime; + let field_rev = FieldBuilder::from_field_type(&field_type).build(); + assert_changeset_result( + &type_option, + DateCellChangeset { + date: Some("1e".to_string()), + time: Some("23:00".to_owned()), + }, + &field_type, + &field_rev, + "", + ); + } + + #[test] + fn date_type_option_date_format_test() { + let mut type_option = DateTypeOption::default(); + let field_rev = FieldBuilder::from_field_type(&FieldType::DateTime).build(); + for date_format in DateFormat::iter() { + type_option.date_format = date_format; + match date_format { + DateFormat::Friendly => { + assert_decode_timestamp(1647251762, &type_option, &field_rev, "Mar 14,2022"); + } + DateFormat::US => { + assert_decode_timestamp(1647251762, &type_option, &field_rev, "2022/03/14"); + } + DateFormat::ISO => { + assert_decode_timestamp(1647251762, &type_option, &field_rev, "2022-03-14"); + } + DateFormat::Local => { + assert_decode_timestamp(1647251762, &type_option, &field_rev, "2022/03/14"); + } + } + } + } + + #[test] + fn date_type_option_time_format_test() { + let mut type_option = DateTypeOption::default(); + let field_type = FieldType::DateTime; + let field_rev = FieldBuilder::from_field_type(&field_type).build(); + + for time_format in TimeFormat::iter() { + type_option.time_format = time_format; + type_option.include_time = true; + match time_format { + TimeFormat::TwentyFourHour => { + assert_changeset_result( + &type_option, + DateCellChangeset { + date: Some(1653609600.to_string()), + time: None, + }, + &field_type, + &field_rev, + "May 27,2022", + ); + assert_changeset_result( + &type_option, + DateCellChangeset { + date: Some(1653609600.to_string()), + time: Some("23:00".to_owned()), + }, + &field_type, + &field_rev, + "May 27,2022 23:00", + ); + } + TimeFormat::TwelveHour => { + assert_changeset_result( + &type_option, + DateCellChangeset { + date: Some(1653609600.to_string()), + time: None, + }, + &field_type, + &field_rev, + "May 27,2022", + ); + // + assert_changeset_result( + &type_option, + DateCellChangeset { + date: Some(1653609600.to_string()), + time: Some("".to_owned()), + }, + &field_type, + &field_rev, + "May 27,2022", + ); + + assert_changeset_result( + &type_option, + DateCellChangeset { + date: Some(1653609600.to_string()), + time: Some("11:23 pm".to_owned()), + }, + &field_type, + &field_rev, + "May 27,2022 11:23 PM", + ); + } + } + } + } + + #[test] + fn date_type_option_apply_changeset_test() { + let mut type_option = DateTypeOption::new(); + let field_type = FieldType::DateTime; + let field_rev = FieldBuilder::from_field_type(&field_type).build(); + let date_timestamp = "1653609600".to_owned(); + + assert_changeset_result( + &type_option, + DateCellChangeset { + date: Some(date_timestamp.clone()), + time: None, + }, + &field_type, + &field_rev, + "May 27,2022", + ); + + type_option.include_time = true; + assert_changeset_result( + &type_option, + DateCellChangeset { + date: Some(date_timestamp.clone()), + time: None, + }, + &field_type, + &field_rev, + "May 27,2022", + ); + + assert_changeset_result( + &type_option, + DateCellChangeset { + date: Some(date_timestamp.clone()), + time: Some("1:00".to_owned()), + }, + &field_type, + &field_rev, + "May 27,2022 01:00", + ); + + type_option.time_format = TimeFormat::TwelveHour; + assert_changeset_result( + &type_option, + DateCellChangeset { + date: Some(date_timestamp), + time: Some("1:00 am".to_owned()), + }, + &field_type, + &field_rev, + "May 27,2022 01:00 AM", + ); + } + + #[test] + #[should_panic] + fn date_type_option_apply_changeset_error_test() { + let mut type_option = DateTypeOption::new(); + type_option.include_time = true; + let field_rev = FieldBuilder::from_field_type(&FieldType::DateTime).build(); + let date_timestamp = "1653609600".to_owned(); + + assert_changeset_result( + &type_option, + DateCellChangeset { + date: Some(date_timestamp.clone()), + time: Some("1:".to_owned()), + }, + &FieldType::DateTime, + &field_rev, + "May 27,2022 01:00", + ); + + assert_changeset_result( + &type_option, + DateCellChangeset { + date: Some(date_timestamp), + time: Some("1:00".to_owned()), + }, + &FieldType::DateTime, + &field_rev, + "May 27,2022 01:00", + ); + } + + #[test] + #[should_panic] + fn date_type_option_twelve_hours_to_twenty_four_hours() { + let mut type_option = DateTypeOption::new(); + type_option.include_time = true; + let field_rev = FieldBuilder::from_field_type(&FieldType::DateTime).build(); + let date_timestamp = "1653609600".to_owned(); + + assert_changeset_result( + &type_option, + DateCellChangeset { + date: Some(date_timestamp), + time: Some("1:00 am".to_owned()), + }, + &FieldType::DateTime, + &field_rev, + "May 27,2022 01:00", + ); + } + + fn assert_changeset_result( + type_option: &DateTypeOption, + 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(), + decode_cell_data(encoded_data, type_option, field_rev) + ); + } + + fn assert_decode_timestamp( + timestamp: i64, + type_option: &DateTypeOption, + field_rev: &FieldRevision, + expected: &str, + ) { + 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(), + decode_cell_data(encoded_data, type_option, field_rev) + ); + } + + fn decode_cell_data(encoded_data: String, type_option: &DateTypeOption, field_rev: &FieldRevision) -> String { + let decoded_data = type_option + .decode_cell_data(encoded_data.into(), &FieldType::DateTime, field_rev) + .unwrap() + .parse::() + .unwrap(); + + if type_option.include_time { + format!("{}{}", decoded_data.date, decoded_data.time) + } else { + decoded_data.date + } + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/mod.rs index 754e441876..32fd9ef0a4 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/mod.rs @@ -1,17 +1,14 @@ -mod checkbox_type_option; -mod date_type_option; -mod multi_select_type_option; -mod number_type_option; -mod single_select_type_option; -mod text_type_option; -mod url_type_option; +pub mod checkbox_type_option; +pub mod date_type_option; +pub mod number_type_option; +pub mod selection_type_option; +pub mod text_type_option; +pub mod url_type_option; mod util; pub use checkbox_type_option::*; pub use date_type_option::*; -pub use multi_select_type_option::*; -pub use multi_select_type_option::*; pub use number_type_option::*; -pub use single_select_type_option::*; +pub use selection_type_option::*; pub use text_type_option::*; pub use url_type_option::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/mod.rs index fffbad97bf..8b4f9b2896 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/mod.rs @@ -1,6 +1,9 @@ #![allow(clippy::module_inception)] mod format; -mod number_type_option; +mod number_option; +mod number_option_entities; +mod tests; pub use format::*; -pub use number_type_option::*; +pub use number_option::*; +pub use number_option_entities::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option.rs new file mode 100644 index 0000000000..001bb4a0bc --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option.rs @@ -0,0 +1,149 @@ +use crate::impl_type_option; + +use crate::entities::FieldType; +use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation}; +use crate::services::field::number_currency::Currency; +use crate::services::field::type_options::number_type_option::format::*; +use crate::services::field::{BoxTypeOptionBuilder, NumberCellData, TypeOptionBuilder}; +use bytes::Bytes; +use flowy_derive::ProtoBuf; +use flowy_error::{FlowyError, FlowyResult}; +use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry}; + +use rust_decimal::Decimal; +use rusty_money::Money; +use serde::{Deserialize, Serialize}; +use std::str::FromStr; + +#[derive(Default)] +pub struct NumberTypeOptionBuilder(NumberTypeOption); +impl_into_box_type_option_builder!(NumberTypeOptionBuilder); +impl_builder_from_json_str_and_from_bytes!(NumberTypeOptionBuilder, NumberTypeOption); + +impl NumberTypeOptionBuilder { + pub fn name(mut self, name: &str) -> Self { + self.0.name = name.to_string(); + self + } + + pub fn set_format(mut self, format: NumberFormat) -> Self { + self.0.set_format(format); + self + } + + pub fn scale(mut self, scale: u32) -> Self { + self.0.scale = scale; + self + } + + pub fn positive(mut self, positive: bool) -> Self { + self.0.sign_positive = positive; + self + } +} + +impl TypeOptionBuilder for NumberTypeOptionBuilder { + fn field_type(&self) -> FieldType { + FieldType::Number + } + + fn entry(&self) -> &dyn TypeOptionDataEntry { + &self.0 + } +} + +// Number +#[derive(Clone, Debug, Serialize, Deserialize, ProtoBuf)] +pub struct NumberTypeOption { + #[pb(index = 1)] + pub format: NumberFormat, + + #[pb(index = 2)] + pub scale: u32, + + #[pb(index = 3)] + pub symbol: String, + + #[pb(index = 4)] + pub sign_positive: bool, + + #[pb(index = 5)] + pub name: String, +} +impl_type_option!(NumberTypeOption, FieldType::Number); + +impl NumberTypeOption { + pub fn new() -> Self { + Self::default() + } + + pub(crate) fn format_cell_data(&self, s: &str) -> FlowyResult { + match self.format { + NumberFormat::Num | NumberFormat::Percent => match Decimal::from_str(s) { + Ok(value, ..) => Ok(NumberCellData::from_decimal(value)), + Err(_) => Ok(NumberCellData::new()), + }, + _ => NumberCellData::from_format_str(s, self.sign_positive, &self.format), + } + } + + pub fn set_format(&mut self, format: NumberFormat) { + self.format = format; + self.symbol = format.symbol(); + } +} + +pub(crate) fn strip_currency_symbol(s: T) -> String { + let mut s = s.to_string(); + for symbol in CURRENCY_SYMBOL.iter() { + if s.starts_with(symbol) { + s = s.strip_prefix(symbol).unwrap_or("").to_string(); + break; + } + } + s +} + +impl CellDataOperation for NumberTypeOption { + fn decode_cell_data( + &self, + cell_data: CellData, + decoded_field_type: &FieldType, + _field_rev: &FieldRevision, + ) -> FlowyResult { + if decoded_field_type.is_date() { + return Ok(CellBytes::default()); + } + + let cell_data: String = cell_data.try_into_inner()?; + match self.format_cell_data(&cell_data) { + Ok(num) => Ok(CellBytes::new(num.to_string())), + Err(_) => Ok(CellBytes::default()), + } + } + + fn apply_changeset( + &self, + changeset: CellDataChangeset, + _cell_rev: Option, + ) -> Result { + let changeset = changeset.try_into_inner()?; + let data = changeset.trim().to_string(); + let _ = self.format_cell_data(&data)?; + Ok(data) + } +} + +impl std::default::Default for NumberTypeOption { + fn default() -> Self { + let format = NumberFormat::default(); + let symbol = format.symbol(); + NumberTypeOption { + format, + scale: 0, + symbol, + sign_positive: true, + name: "Number".to_string(), + } + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option_entities.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option_entities.rs new file mode 100644 index 0000000000..e797165b03 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option_entities.rs @@ -0,0 +1,93 @@ +use crate::services::field::number_currency::Currency; +use crate::services::field::{strip_currency_symbol, NumberFormat, STRIP_SYMBOL}; +use flowy_error::{FlowyError, FlowyResult}; +use rust_decimal::Decimal; +use rusty_money::Money; +use std::str::FromStr; + +#[derive(Default)] +pub struct NumberCellData { + decimal: Option, + money: Option, +} + +impl NumberCellData { + pub fn new() -> Self { + Self { + decimal: Default::default(), + money: None, + } + } + + pub fn from_format_str(s: &str, sign_positive: bool, format: &NumberFormat) -> FlowyResult { + let mut num_str = strip_currency_symbol(s); + let currency = format.currency(); + if num_str.is_empty() { + return Ok(Self::default()); + } + match Decimal::from_str(&num_str) { + Ok(mut decimal) => { + decimal.set_sign_positive(sign_positive); + let money = Money::from_decimal(decimal, currency); + Ok(Self::from_money(money)) + } + Err(_) => match Money::from_str(&num_str, currency) { + Ok(money) => Ok(NumberCellData::from_money(money)), + Err(_) => { + num_str.retain(|c| !STRIP_SYMBOL.contains(&c.to_string())); + if num_str.chars().all(char::is_numeric) { + Self::from_format_str(&num_str, sign_positive, format) + } else { + Err(FlowyError::invalid_data().context("Should only contain numbers")) + } + } + }, + } + } + + pub fn from_decimal(decimal: Decimal) -> Self { + Self { + decimal: Some(decimal), + money: None, + } + } + + pub fn from_money(money: Money) -> Self { + Self { + decimal: Some(*money.amount()), + money: Some(money.to_string()), + } + } + + pub fn decimal(&self) -> &Option { + &self.decimal + } + + pub fn is_empty(&self) -> bool { + self.decimal.is_none() + } +} + +impl FromStr for NumberCellData { + type Err = rust_decimal::Error; + + fn from_str(s: &str) -> Result { + if s.is_empty() { + return Ok(Self::default()); + } + let decimal = Decimal::from_str(s)?; + Ok(Self::from_decimal(decimal)) + } +} + +impl ToString for NumberCellData { + fn to_string(&self) -> String { + match &self.money { + None => match self.decimal { + None => String::default(), + Some(decimal) => decimal.to_string(), + }, + Some(money) => money.to_string(), + } + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs deleted file mode 100644 index c1c3dbdae3..0000000000 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs +++ /dev/null @@ -1,376 +0,0 @@ -use crate::impl_type_option; - -use crate::entities::FieldType; -use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation}; -use crate::services::field::number_currency::Currency; -use crate::services::field::type_options::number_type_option::format::*; -use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; -use bytes::Bytes; -use flowy_derive::ProtoBuf; -use flowy_error::{FlowyError, FlowyResult}; -use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry}; - -use rust_decimal::Decimal; -use rusty_money::Money; -use serde::{Deserialize, Serialize}; -use std::str::FromStr; - -#[derive(Default)] -pub struct NumberTypeOptionBuilder(NumberTypeOption); -impl_into_box_type_option_builder!(NumberTypeOptionBuilder); -impl_builder_from_json_str_and_from_bytes!(NumberTypeOptionBuilder, NumberTypeOption); - -impl NumberTypeOptionBuilder { - pub fn name(mut self, name: &str) -> Self { - self.0.name = name.to_string(); - self - } - - pub fn set_format(mut self, format: NumberFormat) -> Self { - self.0.set_format(format); - self - } - - pub fn scale(mut self, scale: u32) -> Self { - self.0.scale = scale; - self - } - - pub fn positive(mut self, positive: bool) -> Self { - self.0.sign_positive = positive; - self - } -} - -impl TypeOptionBuilder for NumberTypeOptionBuilder { - fn field_type(&self) -> FieldType { - FieldType::Number - } - - fn entry(&self) -> &dyn TypeOptionDataEntry { - &self.0 - } -} - -// Number -#[derive(Clone, Debug, Serialize, Deserialize, ProtoBuf)] -pub struct NumberTypeOption { - #[pb(index = 1)] - pub format: NumberFormat, - - #[pb(index = 2)] - pub scale: u32, - - #[pb(index = 3)] - pub symbol: String, - - #[pb(index = 4)] - pub sign_positive: bool, - - #[pb(index = 5)] - pub name: String, -} -impl_type_option!(NumberTypeOption, FieldType::Number); - -impl NumberTypeOption { - pub fn new() -> Self { - Self::default() - } - - pub(crate) fn format_cell_data(&self, s: &str) -> FlowyResult { - match self.format { - NumberFormat::Num | NumberFormat::Percent => match Decimal::from_str(s) { - Ok(value, ..) => Ok(NumberCellData::from_decimal(value)), - Err(_) => Ok(NumberCellData::new()), - }, - _ => NumberCellData::from_format_str(s, self.sign_positive, &self.format), - } - } - - pub fn set_format(&mut self, format: NumberFormat) { - self.format = format; - self.symbol = format.symbol(); - } -} - -pub(crate) fn strip_currency_symbol(s: T) -> String { - let mut s = s.to_string(); - for symbol in CURRENCY_SYMBOL.iter() { - if s.starts_with(symbol) { - s = s.strip_prefix(symbol).unwrap_or("").to_string(); - break; - } - } - s -} - -impl CellDataOperation for NumberTypeOption { - fn decode_cell_data( - &self, - cell_data: CellData, - decoded_field_type: &FieldType, - _field_rev: &FieldRevision, - ) -> FlowyResult { - if decoded_field_type.is_date() { - return Ok(CellBytes::default()); - } - - let cell_data: String = cell_data.try_into_inner()?; - match self.format_cell_data(&cell_data) { - Ok(num) => Ok(CellBytes::new(num.to_string())), - Err(_) => Ok(CellBytes::default()), - } - } - - fn apply_changeset( - &self, - changeset: CellDataChangeset, - _cell_rev: Option, - ) -> Result { - let changeset = changeset.try_into_inner()?; - let data = changeset.trim().to_string(); - let _ = self.format_cell_data(&data)?; - Ok(data) - } -} - -impl std::default::Default for NumberTypeOption { - fn default() -> Self { - let format = NumberFormat::default(); - let symbol = format.symbol(); - NumberTypeOption { - format, - scale: 0, - symbol, - sign_positive: true, - name: "Number".to_string(), - } - } -} - -#[derive(Default)] -pub struct NumberCellData { - decimal: Option, - money: Option, -} - -impl NumberCellData { - pub fn new() -> Self { - Self { - decimal: Default::default(), - money: None, - } - } - - pub fn from_format_str(s: &str, sign_positive: bool, format: &NumberFormat) -> FlowyResult { - let mut num_str = strip_currency_symbol(s); - let currency = format.currency(); - if num_str.is_empty() { - return Ok(Self::default()); - } - match Decimal::from_str(&num_str) { - Ok(mut decimal) => { - decimal.set_sign_positive(sign_positive); - let money = Money::from_decimal(decimal, currency); - Ok(Self::from_money(money)) - } - Err(_) => match Money::from_str(&num_str, currency) { - Ok(money) => Ok(NumberCellData::from_money(money)), - Err(_) => { - num_str.retain(|c| !STRIP_SYMBOL.contains(&c.to_string())); - if num_str.chars().all(char::is_numeric) { - Self::from_format_str(&num_str, sign_positive, format) - } else { - Err(FlowyError::invalid_data().context("Should only contain numbers")) - } - } - }, - } - } - - pub fn from_decimal(decimal: Decimal) -> Self { - Self { - decimal: Some(decimal), - money: None, - } - } - - pub fn from_money(money: Money) -> Self { - Self { - decimal: Some(*money.amount()), - money: Some(money.to_string()), - } - } - - pub fn decimal(&self) -> &Option { - &self.decimal - } - - pub fn is_empty(&self) -> bool { - self.decimal.is_none() - } -} - -impl FromStr for NumberCellData { - type Err = rust_decimal::Error; - - fn from_str(s: &str) -> Result { - if s.is_empty() { - return Ok(Self::default()); - } - let decimal = Decimal::from_str(s)?; - Ok(Self::from_decimal(decimal)) - } -} - -impl ToString for NumberCellData { - fn to_string(&self) -> String { - match &self.money { - None => match self.decimal { - None => String::default(), - Some(decimal) => decimal.to_string(), - }, - Some(money) => money.to_string(), - } - } -} - -#[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 flowy_grid_data_model::revision::FieldRevision; - use strum::IntoEnumIterator; - - #[test] - fn number_type_option_invalid_input_test() { - let type_option = NumberTypeOption::default(); - let field_type = FieldType::Number; - let field_rev = FieldBuilder::from_field_type(&field_type).build(); - assert_equal(&type_option, "", "", &field_type, &field_rev); - assert_equal(&type_option, "abc", "", &field_type, &field_rev); - } - - #[test] - fn number_type_option_strip_symbol_test() { - let mut type_option = NumberTypeOption::new(); - type_option.format = NumberFormat::USD; - assert_eq!(strip_currency_symbol("$18,443"), "18,443".to_owned()); - - type_option.format = NumberFormat::Yuan; - assert_eq!(strip_currency_symbol("$0.2"), "0.2".to_owned()); - } - - #[test] - fn number_type_option_format_number_test() { - let mut type_option = NumberTypeOption::default(); - let field_type = FieldType::Number; - let field_rev = FieldBuilder::from_field_type(&field_type).build(); - - for format in NumberFormat::iter() { - type_option.format = format; - match format { - NumberFormat::Num => { - assert_equal(&type_option, "18443", "18443", &field_type, &field_rev); - } - NumberFormat::USD => { - assert_equal(&type_option, "18443", "$18,443", &field_type, &field_rev); - } - NumberFormat::Yen => { - assert_equal(&type_option, "18443", "Â¥18,443", &field_type, &field_rev); - } - NumberFormat::Yuan => { - assert_equal(&type_option, "18443", "CNÂ¥18,443", &field_type, &field_rev); - } - NumberFormat::EUR => { - assert_equal(&type_option, "18443", "€18.443", &field_type, &field_rev); - } - _ => {} - } - } - } - - #[test] - fn number_type_option_format_str_test() { - let mut type_option = NumberTypeOption::default(); - let field_type = FieldType::Number; - let field_rev = FieldBuilder::from_field_type(&field_type).build(); - - for format in NumberFormat::iter() { - type_option.format = format; - match format { - NumberFormat::Num => { - assert_equal(&type_option, "18443", "18443", &field_type, &field_rev); - assert_equal(&type_option, "0.2", "0.2", &field_type, &field_rev); - } - NumberFormat::USD => { - assert_equal(&type_option, "$18,44", "$1,844", &field_type, &field_rev); - assert_equal(&type_option, "$0.2", "$0.2", &field_type, &field_rev); - assert_equal(&type_option, "", "", &field_type, &field_rev); - assert_equal(&type_option, "abc", "", &field_type, &field_rev); - } - NumberFormat::Yen => { - assert_equal(&type_option, "Â¥18,44", "Â¥1,844", &field_type, &field_rev); - assert_equal(&type_option, "Â¥1844", "Â¥1,844", &field_type, &field_rev); - } - NumberFormat::Yuan => { - assert_equal(&type_option, "CNÂ¥18,44", "CNÂ¥1,844", &field_type, &field_rev); - assert_equal(&type_option, "CNÂ¥1844", "CNÂ¥1,844", &field_type, &field_rev); - } - NumberFormat::EUR => { - assert_equal(&type_option, "€18.44", "€18,44", &field_type, &field_rev); - assert_equal(&type_option, "€0.5", "€0,5", &field_type, &field_rev); - assert_equal(&type_option, "€1844", "€1.844", &field_type, &field_rev); - } - _ => {} - } - } - } - - #[test] - fn number_description_sign_test() { - let mut type_option = NumberTypeOption { - sign_positive: false, - ..Default::default() - }; - let field_type = FieldType::Number; - let field_rev = FieldBuilder::from_field_type(&field_type).build(); - - for format in NumberFormat::iter() { - type_option.format = format; - match format { - NumberFormat::Num => { - assert_equal(&type_option, "18443", "18443", &field_type, &field_rev); - } - NumberFormat::USD => { - assert_equal(&type_option, "18443", "-$18,443", &field_type, &field_rev); - } - NumberFormat::Yen => { - assert_equal(&type_option, "18443", "-Â¥18,443", &field_type, &field_rev); - } - NumberFormat::EUR => { - assert_equal(&type_option, "18443", "-€18.443", &field_type, &field_rev); - } - _ => {} - } - } - } - - fn assert_equal( - type_option: &NumberTypeOption, - cell_data: &str, - expected_str: &str, - field_type: &FieldType, - field_rev: &FieldRevision, - ) { - assert_eq!( - type_option - .decode_cell_data(cell_data.to_owned().into(), field_type, field_rev) - .unwrap() - .to_string(), - expected_str.to_owned() - ); - } -} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/tests.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/tests.rs new file mode 100644 index 0000000000..6ea1e8f302 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/tests.rs @@ -0,0 +1,139 @@ +#[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 flowy_grid_data_model::revision::FieldRevision; + use strum::IntoEnumIterator; + + #[test] + fn number_type_option_invalid_input_test() { + let type_option = NumberTypeOption::default(); + let field_type = FieldType::Number; + let field_rev = FieldBuilder::from_field_type(&field_type).build(); + assert_equal(&type_option, "", "", &field_type, &field_rev); + assert_equal(&type_option, "abc", "", &field_type, &field_rev); + } + + #[test] + fn number_type_option_strip_symbol_test() { + let mut type_option = NumberTypeOption::new(); + type_option.format = NumberFormat::USD; + assert_eq!(strip_currency_symbol("$18,443"), "18,443".to_owned()); + + type_option.format = NumberFormat::Yuan; + assert_eq!(strip_currency_symbol("$0.2"), "0.2".to_owned()); + } + + #[test] + fn number_type_option_format_number_test() { + let mut type_option = NumberTypeOption::default(); + let field_type = FieldType::Number; + let field_rev = FieldBuilder::from_field_type(&field_type).build(); + + for format in NumberFormat::iter() { + type_option.format = format; + match format { + NumberFormat::Num => { + assert_equal(&type_option, "18443", "18443", &field_type, &field_rev); + } + NumberFormat::USD => { + assert_equal(&type_option, "18443", "$18,443", &field_type, &field_rev); + } + NumberFormat::Yen => { + assert_equal(&type_option, "18443", "Â¥18,443", &field_type, &field_rev); + } + NumberFormat::Yuan => { + assert_equal(&type_option, "18443", "CNÂ¥18,443", &field_type, &field_rev); + } + NumberFormat::EUR => { + assert_equal(&type_option, "18443", "€18.443", &field_type, &field_rev); + } + _ => {} + } + } + } + + #[test] + fn number_type_option_format_str_test() { + let mut type_option = NumberTypeOption::default(); + let field_type = FieldType::Number; + let field_rev = FieldBuilder::from_field_type(&field_type).build(); + + for format in NumberFormat::iter() { + type_option.format = format; + match format { + NumberFormat::Num => { + assert_equal(&type_option, "18443", "18443", &field_type, &field_rev); + assert_equal(&type_option, "0.2", "0.2", &field_type, &field_rev); + } + NumberFormat::USD => { + assert_equal(&type_option, "$18,44", "$1,844", &field_type, &field_rev); + assert_equal(&type_option, "$0.2", "$0.2", &field_type, &field_rev); + assert_equal(&type_option, "", "", &field_type, &field_rev); + assert_equal(&type_option, "abc", "", &field_type, &field_rev); + } + NumberFormat::Yen => { + assert_equal(&type_option, "Â¥18,44", "Â¥1,844", &field_type, &field_rev); + assert_equal(&type_option, "Â¥1844", "Â¥1,844", &field_type, &field_rev); + } + NumberFormat::Yuan => { + assert_equal(&type_option, "CNÂ¥18,44", "CNÂ¥1,844", &field_type, &field_rev); + assert_equal(&type_option, "CNÂ¥1844", "CNÂ¥1,844", &field_type, &field_rev); + } + NumberFormat::EUR => { + assert_equal(&type_option, "€18.44", "€18,44", &field_type, &field_rev); + assert_equal(&type_option, "€0.5", "€0,5", &field_type, &field_rev); + assert_equal(&type_option, "€1844", "€1.844", &field_type, &field_rev); + } + _ => {} + } + } + } + + #[test] + fn number_description_sign_test() { + let mut type_option = NumberTypeOption { + sign_positive: false, + ..Default::default() + }; + let field_type = FieldType::Number; + let field_rev = FieldBuilder::from_field_type(&field_type).build(); + + for format in NumberFormat::iter() { + type_option.format = format; + match format { + NumberFormat::Num => { + assert_equal(&type_option, "18443", "18443", &field_type, &field_rev); + } + NumberFormat::USD => { + assert_equal(&type_option, "18443", "-$18,443", &field_type, &field_rev); + } + NumberFormat::Yen => { + assert_equal(&type_option, "18443", "-Â¥18,443", &field_type, &field_rev); + } + NumberFormat::EUR => { + assert_equal(&type_option, "18443", "-€18.443", &field_type, &field_rev); + } + _ => {} + } + } + } + + fn assert_equal( + type_option: &NumberTypeOption, + cell_data: &str, + expected_str: &str, + field_type: &FieldType, + field_rev: &FieldRevision, + ) { + assert_eq!( + type_option + .decode_cell_data(cell_data.to_owned().into(), field_type, field_rev) + .unwrap() + .to_string(), + expected_str.to_owned() + ); + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/mod.rs new file mode 100644 index 0000000000..45d2b96e83 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/mod.rs @@ -0,0 +1,7 @@ +mod multi_select_type_option; +mod select_option; +mod single_select_type_option; + +pub use multi_select_type_option::*; +pub use select_option::*; +pub use single_select_type_option::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/multi_select_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs similarity index 93% rename from frontend/rust-lib/flowy-grid/src/services/field/type_options/multi_select_type_option.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs index 902170307b..050cf196f3 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/multi_select_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs @@ -1,19 +1,15 @@ use crate::entities::FieldType; - use crate::impl_type_option; use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable}; -use crate::services::field::select_option::{ - make_selected_select_options, SelectOption, SelectOptionCellChangeset, SelectOptionCellData, SelectOptionIds, - SelectOptionOperation, SELECTION_IDS_SEPARATOR, -}; use crate::services::field::type_options::util::get_cell_data; -use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; +use crate::services::field::{ + make_selected_select_options, BoxTypeOptionBuilder, SelectOption, SelectOptionCellChangeset, SelectOptionCellData, + SelectOptionIds, SelectOptionOperation, TypeOptionBuilder, SELECTION_IDS_SEPARATOR, +}; use bytes::Bytes; use flowy_derive::ProtoBuf; use flowy_error::{FlowyError, FlowyResult}; - use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry}; - use serde::{Deserialize, Serialize}; // Multiple select @@ -56,8 +52,7 @@ impl CellDataOperation for MultiSele return Ok(CellBytes::default()); } - let cell_data = self.display_data(cell_data, decoded_field_type, field_rev)?; - CellBytes::from(cell_data) + self.display_data(cell_data, decoded_field_type, field_rev) } fn apply_changeset( @@ -121,7 +116,7 @@ impl TypeOptionBuilder for MultiSelectTypeOptionBuilder { mod tests { use crate::entities::FieldType; use crate::services::cell::CellDataOperation; - use crate::services::field::select_option::*; + use crate::services::field::type_options::selection_type_option::*; use crate::services::field::FieldBuilder; use crate::services::field::{MultiSelectTypeOption, MultiSelectTypeOptionBuilder}; use flowy_grid_data_model::revision::FieldRevision; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/select_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_option.rs similarity index 97% rename from frontend/rust-lib/flowy-grid/src/services/field/select_option.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_option.rs index d429899587..dcea1e209d 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/select_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_option.rs @@ -1,5 +1,5 @@ use crate::entities::{CellChangeset, CellIdentifier, CellIdentifierPayload, FieldType}; -use crate::services::cell::{AnyCellData, CellData, CellDisplayable, FromCellChangeset, FromCellString}; +use crate::services::cell::{AnyCellData, CellBytes, CellData, CellDisplayable, FromCellChangeset, FromCellString}; use crate::services::field::{MultiSelectTypeOption, SingleSelectTypeOption}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult}; @@ -106,7 +106,7 @@ pub trait SelectOptionOperation: TypeOptionDataEntry + Send + Sync { fn mut_options(&mut self) -> &mut Vec; } -impl CellDisplayable for T +impl CellDisplayable for T where T: SelectOptionOperation, { @@ -115,8 +115,8 @@ where cell_data: CellData, _decoded_field_type: &FieldType, _field_rev: &FieldRevision, - ) -> FlowyResult { - Ok(self.selected_select_option(cell_data)) + ) -> FlowyResult { + CellBytes::from(self.selected_select_option(cell_data)) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/single_select_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/single_select_type_option.rs similarity index 96% rename from frontend/rust-lib/flowy-grid/src/services/field/type_options/single_select_type_option.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/single_select_type_option.rs index 35a2ac4853..f92fbd73d0 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/single_select_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/single_select_type_option.rs @@ -1,7 +1,7 @@ use crate::entities::FieldType; use crate::impl_type_option; use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable}; -use crate::services::field::select_option::{ +use crate::services::field::{ make_selected_select_options, SelectOption, SelectOptionCellChangeset, SelectOptionCellData, SelectOptionIds, SelectOptionOperation, }; @@ -54,8 +54,7 @@ impl CellDataOperation for SingleSel return Ok(CellBytes::default()); } - let cell_data = self.display_data(cell_data, decoded_field_type, field_rev)?; - CellBytes::from(cell_data) + self.display_data(cell_data, decoded_field_type, field_rev) } fn apply_changeset( @@ -103,7 +102,7 @@ impl TypeOptionBuilder for SingleSelectTypeOptionBuilder { 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 flowy_grid_data_model::revision::FieldRevision; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/mod.rs new file mode 100644 index 0000000000..9f41c61fdd --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/mod.rs @@ -0,0 +1,2 @@ +mod text_option; +pub use text_option::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_option.rs similarity index 95% rename from frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_option.rs index 4d4b4e65fc..e2d77ddfde 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_option.rs @@ -32,15 +32,15 @@ pub struct RichTextTypeOption { } impl_type_option!(RichTextTypeOption, FieldType::RichText); -impl CellDisplayable for RichTextTypeOption { +impl CellDisplayable for RichTextTypeOption { fn display_data( &self, cell_data: CellData, _decoded_field_type: &FieldType, _field_rev: &FieldRevision, - ) -> FlowyResult { + ) -> FlowyResult { let cell_str: String = cell_data.try_into_inner()?; - Ok(cell_str) + Ok(CellBytes::new(cell_str)) } } @@ -58,8 +58,7 @@ impl CellDataOperation for RichTextTypeOption { { try_decode_cell_data(cell_data, field_rev, decoded_field_type, decoded_field_type) } else { - let content = self.display_data(cell_data, decoded_field_type, field_rev)?; - Ok(CellBytes::new(content)) + self.display_data(cell_data, decoded_field_type, field_rev) } } @@ -96,7 +95,7 @@ impl std::convert::TryFrom for TextCellData { 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::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs deleted file mode 100644 index 18144e3f2a..0000000000 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs +++ /dev/null @@ -1,206 +0,0 @@ -use crate::entities::FieldType; -use crate::impl_type_option; -use crate::services::cell::{ - AnyCellData, CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable, FromCellString, -}; -use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; -use bytes::Bytes; -use fancy_regex::Regex; -use flowy_derive::ProtoBuf; -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}; - -#[derive(Default)] -pub struct URLTypeOptionBuilder(URLTypeOption); -impl_into_box_type_option_builder!(URLTypeOptionBuilder); -impl_builder_from_json_str_and_from_bytes!(URLTypeOptionBuilder, URLTypeOption); - -impl TypeOptionBuilder for URLTypeOptionBuilder { - fn field_type(&self) -> FieldType { - FieldType::URL - } - - fn entry(&self) -> &dyn TypeOptionDataEntry { - &self.0 - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default, ProtoBuf)] -pub struct URLTypeOption { - #[pb(index = 1)] - data: String, //It's not used yet. -} -impl_type_option!(URLTypeOption, FieldType::URL); - -impl CellDisplayable for URLTypeOption { - fn display_data( - &self, - cell_data: CellData, - _decoded_field_type: &FieldType, - _field_rev: &FieldRevision, - ) -> FlowyResult { - let cell_data: URLCellData = cell_data.try_into_inner()?; - Ok(cell_data) - } -} - -impl CellDataOperation for URLTypeOption { - fn decode_cell_data( - &self, - cell_data: CellData, - decoded_field_type: &FieldType, - field_rev: &FieldRevision, - ) -> FlowyResult { - if !decoded_field_type.is_url() { - return Ok(CellBytes::default()); - } - let cell_data = self.display_data(cell_data, decoded_field_type, field_rev)?; - CellBytes::from(cell_data) - } - - fn apply_changeset( - &self, - changeset: CellDataChangeset, - _cell_rev: Option, - ) -> Result { - 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_json() - } -} - -fn auto_append_scheme(s: &str) -> String { - // Only support https scheme by now - match url::Url::parse(s) { - Ok(url) => { - if url.scheme() == "https" { - url.into() - } else { - format!("https://{}", s) - } - } - Err(_) => { - format!("https://{}", s) - } - } -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)] -pub struct URLCellData { - #[pb(index = 1)] - pub url: String, - - #[pb(index = 2)] - pub content: String, -} - -impl URLCellData { - pub fn new(s: &str) -> Self { - Self { - url: "".to_string(), - content: s.to_string(), - } - } - - fn to_json(&self) -> FlowyResult { - serde_json::to_string(self).map_err(internal_error) - } -} - -impl FromCellString for URLCellData { - fn from_cell_str(s: &str) -> FlowyResult { - serde_json::from_str::(s).map_err(internal_error) - } -} - -impl std::convert::TryFrom for URLCellData { - type Error = FlowyError; - - fn try_from(data: AnyCellData) -> Result { - serde_json::from_str::(&data.data).map_err(internal_error) - } -} - -lazy_static! { - static ref URL_REGEX: Regex = Regex::new( - "[(http(s)?):\\/\\/(www\\.)?a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)" - ) - .unwrap(); -} - -#[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 flowy_grid_data_model::revision::FieldRevision; - - #[test] - fn url_type_option_test_no_url() { - let type_option = URLTypeOption::default(); - let field_type = FieldType::URL; - let field_rev = FieldBuilder::from_field_type(&field_type).build(); - assert_changeset(&type_option, "123", &field_type, &field_rev, "123", ""); - } - - #[test] - fn url_type_option_test_contains_url() { - let type_option = URLTypeOption::default(); - let field_type = FieldType::URL; - let field_rev = FieldBuilder::from_field_type(&field_type).build(); - assert_changeset( - &type_option, - "AppFlowy website - https://www.appflowy.io", - &field_type, - &field_rev, - "AppFlowy website - https://www.appflowy.io", - "https://www.appflowy.io/", - ); - - assert_changeset( - &type_option, - "AppFlowy website appflowy.io", - &field_type, - &field_rev, - "AppFlowy website appflowy.io", - "https://appflowy.io", - ); - } - - fn assert_changeset( - type_option: &URLTypeOption, - cell_data: &str, - field_type: &FieldType, - field_rev: &FieldRevision, - expected: &str, - expected_url: &str, - ) { - 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>>( - encoded_data: T, - type_option: &URLTypeOption, - field_rev: &FieldRevision, - field_type: &FieldType, - ) -> URLCellData { - type_option - .decode_cell_data(encoded_data.into(), field_type, field_rev) - .unwrap() - .parse::() - .unwrap() - } -} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/mod.rs new file mode 100644 index 0000000000..ee0e0848dd --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/mod.rs @@ -0,0 +1,6 @@ +mod tests; +mod url_option; +mod url_option_entities; + +pub use url_option::*; +pub use url_option_entities::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/tests.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/tests.rs new file mode 100644 index 0000000000..91940fb83c --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/tests.rs @@ -0,0 +1,67 @@ +#[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 flowy_grid_data_model::revision::FieldRevision; + + #[test] + fn url_type_option_test_no_url() { + let type_option = URLTypeOption::default(); + let field_type = FieldType::URL; + let field_rev = FieldBuilder::from_field_type(&field_type).build(); + assert_changeset(&type_option, "123", &field_type, &field_rev, "123", ""); + } + + #[test] + fn url_type_option_test_contains_url() { + let type_option = URLTypeOption::default(); + let field_type = FieldType::URL; + let field_rev = FieldBuilder::from_field_type(&field_type).build(); + assert_changeset( + &type_option, + "AppFlowy website - https://www.appflowy.io", + &field_type, + &field_rev, + "AppFlowy website - https://www.appflowy.io", + "https://www.appflowy.io/", + ); + + assert_changeset( + &type_option, + "AppFlowy website appflowy.io", + &field_type, + &field_rev, + "AppFlowy website appflowy.io", + "https://appflowy.io", + ); + } + + fn assert_changeset( + type_option: &URLTypeOption, + cell_data: &str, + field_type: &FieldType, + field_rev: &FieldRevision, + expected: &str, + expected_url: &str, + ) { + 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>>( + encoded_data: T, + type_option: &URLTypeOption, + field_rev: &FieldRevision, + field_type: &FieldType, + ) -> URLCellData { + type_option + .decode_cell_data(encoded_data.into(), field_type, field_rev) + .unwrap() + .parse::() + .unwrap() + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option.rs new file mode 100644 index 0000000000..d5fe0f287b --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option.rs @@ -0,0 +1,101 @@ +use crate::entities::FieldType; +use crate::impl_type_option; +use crate::services::cell::{ + AnyCellData, CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable, FromCellString, +}; +use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder, URLCellData}; +use bytes::Bytes; +use fancy_regex::Regex; +use flowy_derive::ProtoBuf; +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}; + +#[derive(Default)] +pub struct URLTypeOptionBuilder(URLTypeOption); +impl_into_box_type_option_builder!(URLTypeOptionBuilder); +impl_builder_from_json_str_and_from_bytes!(URLTypeOptionBuilder, URLTypeOption); + +impl TypeOptionBuilder for URLTypeOptionBuilder { + fn field_type(&self) -> FieldType { + FieldType::URL + } + + fn entry(&self) -> &dyn TypeOptionDataEntry { + &self.0 + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default, ProtoBuf)] +pub struct URLTypeOption { + #[pb(index = 1)] + data: String, //It's not used yet. +} +impl_type_option!(URLTypeOption, FieldType::URL); + +impl CellDisplayable for URLTypeOption { + fn display_data( + &self, + cell_data: CellData, + _decoded_field_type: &FieldType, + _field_rev: &FieldRevision, + ) -> FlowyResult { + let cell_data: URLCellData = cell_data.try_into_inner()?; + CellBytes::from(cell_data) + } +} + +impl CellDataOperation for URLTypeOption { + fn decode_cell_data( + &self, + cell_data: CellData, + decoded_field_type: &FieldType, + field_rev: &FieldRevision, + ) -> FlowyResult { + if !decoded_field_type.is_url() { + return Ok(CellBytes::default()); + } + self.display_data(cell_data, decoded_field_type, field_rev) + } + + fn apply_changeset( + &self, + changeset: CellDataChangeset, + _cell_rev: Option, + ) -> Result { + 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_json() + } +} + +fn auto_append_scheme(s: &str) -> String { + // Only support https scheme by now + match url::Url::parse(s) { + Ok(url) => { + if url.scheme() == "https" { + url.into() + } else { + format!("https://{}", s) + } + } + Err(_) => { + format!("https://{}", s) + } + } +} + +lazy_static! { + static ref URL_REGEX: Regex = Regex::new( + "[(http(s)?):\\/\\/(www\\.)?a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)" + ) + .unwrap(); +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option_entities.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option_entities.rs new file mode 100644 index 0000000000..b78335349e --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option_entities.rs @@ -0,0 +1,40 @@ +use crate::services::cell::{AnyCellData, FromCellString}; +use flowy_derive::ProtoBuf; +use flowy_error::{internal_error, FlowyError, FlowyResult}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)] +pub struct URLCellData { + #[pb(index = 1)] + pub url: String, + + #[pb(index = 2)] + pub content: String, +} + +impl URLCellData { + pub fn new(s: &str) -> Self { + Self { + url: "".to_string(), + content: s.to_string(), + } + } + + pub(crate) fn to_json(&self) -> FlowyResult { + serde_json::to_string(self).map_err(internal_error) + } +} + +impl FromCellString for URLCellData { + fn from_cell_str(s: &str) -> FlowyResult { + serde_json::from_str::(s).map_err(internal_error) + } +} + +impl std::convert::TryFrom for URLCellData { + type Error = FlowyError; + + fn try_from(data: AnyCellData) -> Result { + serde_json::from_str::(&data.data).map_err(internal_error) + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/util/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/util/mod.rs index c7aa386fe9..dfadd1c5a0 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/util/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/util/mod.rs @@ -1,4 +1,3 @@ mod cell_data_util; -pub use crate::services::field::select_option::*; pub use cell_data_util::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/filter/impls/select_option_filter.rs b/frontend/rust-lib/flowy-grid/src/services/filter/impls/select_option_filter.rs index 3ef3a77b84..82d5ab6a0b 100644 --- a/frontend/rust-lib/flowy-grid/src/services/filter/impls/select_option_filter.rs +++ b/frontend/rust-lib/flowy-grid/src/services/filter/impls/select_option_filter.rs @@ -2,8 +2,8 @@ use crate::entities::{GridSelectOptionFilter, SelectOptionCondition}; use crate::services::cell::{AnyCellData, CellFilterOperation}; -use crate::services::field::select_option::{SelectOptionOperation, SelectedSelectOptions}; use crate::services::field::{MultiSelectTypeOption, SingleSelectTypeOption}; +use crate::services::field::{SelectOptionOperation, SelectedSelectOptions}; use flowy_error::FlowyResult; impl GridSelectOptionFilter { @@ -64,7 +64,7 @@ impl CellFilterOperation for SingleSelectTypeOption { mod tests { #![allow(clippy::all)] use crate::entities::{GridSelectOptionFilter, SelectOptionCondition}; - use crate::services::field::select_option::{SelectOption, SelectedSelectOptions}; + use crate::services::field::selection_type_option::{SelectOption, SelectedSelectOptions}; #[test] fn select_option_filter_is_test() { diff --git a/frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs b/frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs index cc86fa3d9d..fb8c2550e4 100644 --- a/frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs +++ b/frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs @@ -1,5 +1,5 @@ use crate::services::cell::apply_cell_data_changeset; -use crate::services::field::select_option::SelectOptionCellChangeset; +use crate::services::field::SelectOptionCellChangeset; use flowy_error::{FlowyError, FlowyResult}; use flowy_grid_data_model::revision::{gen_row_id, CellRevision, FieldRevision, RowRevision, DEFAULT_ROW_HEIGHT}; use indexmap::IndexMap; diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs index baf1bf6ae0..07db75161a 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs @@ -1,5 +1,5 @@ use flowy_grid::entities::FieldType; -use flowy_grid::services::field::select_option::{SelectOption, SELECTION_IDS_SEPARATOR}; +use flowy_grid::services::field::selection_type_option::{SelectOption, SELECTION_IDS_SEPARATOR}; use flowy_grid::services::field::{DateCellChangeset, MultiSelectTypeOption, SingleSelectTypeOption}; use flowy_grid::services::row::RowRevisionBuilder; use flowy_grid_data_model::revision::{FieldRevision, RowRevision}; diff --git a/frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs b/frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs index 844a230036..ccdb8ea629 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs @@ -2,7 +2,7 @@ use crate::grid::cell_test::script::CellScript::*; use crate::grid::cell_test::script::GridCellTest; use crate::grid::field_test::util::make_date_cell_string; use flowy_grid::entities::{CellChangeset, FieldType}; -use flowy_grid::services::field::select_option::SelectOptionCellChangeset; +use flowy_grid::services::field::selection_type_option::SelectOptionCellChangeset; use flowy_grid::services::field::{MultiSelectTypeOption, SingleSelectTypeOption}; #[tokio::test] diff --git a/frontend/rust-lib/flowy-grid/tests/grid/field_test/test.rs b/frontend/rust-lib/flowy-grid/tests/grid/field_test/test.rs index 624979e940..c53ba2a2fc 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/field_test/test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/field_test/test.rs @@ -1,7 +1,7 @@ use crate::grid::field_test::script::FieldScript::*; use crate::grid::field_test::script::GridFieldTest; use crate::grid::field_test::util::*; -use flowy_grid::services::field::select_option::SelectOption; +use flowy_grid::services::field::selection_type_option::SelectOption; use flowy_grid::services::field::SingleSelectTypeOption; use flowy_grid_data_model::revision::TypeOptionDataEntry; use flowy_sync::entities::grid::FieldChangesetParams; diff --git a/frontend/rust-lib/flowy-grid/tests/grid/field_test/util.rs b/frontend/rust-lib/flowy-grid/tests/grid/field_test/util.rs index d9055f93ea..42c04e41f9 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/field_test/util.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/field_test/util.rs @@ -1,5 +1,5 @@ use flowy_grid::entities::*; -use flowy_grid::services::field::select_option::SelectOption; +use flowy_grid::services::field::selection_type_option::SelectOption; use flowy_grid::services::field::*; use flowy_grid_data_model::revision::*; diff --git a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs index 85c81c4058..d5429623a3 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs @@ -3,7 +3,7 @@ #![allow(unused_imports)] use bytes::Bytes; use flowy_grid::entities::*; -use flowy_grid::services::field::select_option::SelectOption; +use flowy_grid::services::field::SelectOption; use flowy_grid::services::field::*; use flowy_grid::services::grid_editor::{GridPadBuilder, GridRevisionEditor}; use flowy_grid::services::row::{CreateRowRevisionPayload, RowRevisionBuilder}; From 602aab45e269a2dce1713adb91427ff116993263 Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 13 Jul 2022 17:25:03 +0800 Subject: [PATCH 4/7] chore: unit test for cell content --- .../src/services/cell/any_cell_data.rs | 29 ++++++- .../src/services/cell/cell_operation.rs | 6 +- .../checkbox_type_option/checkbox_option.rs | 3 +- .../checkbox_option_entities.rs | 56 ++++++------ .../{tests.rs => checkbox_tests.rs} | 0 .../type_options/checkbox_type_option/mod.rs | 2 +- .../date_type_option/date_option.rs | 14 ++- .../date_type_option/date_option_entities.rs | 18 ++-- .../{tests.rs => date_tests.rs} | 6 +- .../type_options/date_type_option/mod.rs | 2 +- .../type_options/number_type_option/mod.rs | 2 +- .../number_type_option/number_option.rs | 6 +- .../number_option_entities.rs | 36 +++++--- .../{tests.rs => number_tests.rs} | 0 .../multi_select_type_option.rs | 2 +- .../selection_type_option/select_option.rs | 38 ++++---- .../single_select_type_option.rs | 4 +- .../text_type_option/text_option.rs | 31 +++++-- .../field/type_options/url_type_option/mod.rs | 2 +- .../url_type_option/url_option.rs | 16 ++-- .../url_type_option/url_option_entities.rs | 22 ++--- .../{tests.rs => url_tests.rs} | 4 +- .../services/filter/impls/checkbox_filter.rs | 5 +- .../src/services/filter/impls/date_filter.rs | 5 +- .../services/filter/impls/number_filter.rs | 8 +- .../src/services/filter/impls/text_filter.rs | 5 +- .../src/services/filter/impls/url_filter.rs | 5 +- .../flowy-grid/src/services/grid_editor.rs | 14 +-- .../tests/grid/block_test/row_test.rs | 46 ++++++++-- .../tests/grid/block_test/script.rs | 86 ++++++++++++++++--- .../flowy-grid/tests/grid/block_test/util.rs | 23 +++-- .../grid/filter_test/text_filter_test.rs | 10 +-- .../flowy-grid/tests/grid/grid_editor.rs | 7 +- 33 files changed, 338 insertions(+), 175 deletions(-) rename frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/{tests.rs => checkbox_tests.rs} (100%) rename frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/{tests.rs => date_tests.rs} (97%) rename frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/{tests.rs => number_tests.rs} (100%) rename frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/{tests.rs => url_tests.rs} (95%) diff --git a/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs b/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs index 1b85f3d1a9..8ebffcbedc 100644 --- a/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs +++ b/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs @@ -1,9 +1,11 @@ use crate::entities::FieldType; +use crate::services::cell::{CellData, FromCellString}; 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. @@ -46,6 +48,15 @@ impl std::convert::TryFrom for AnyCellData { } } +impl std::convert::From for CellData +where + T: FromCellString, +{ + fn from(any_call_data: AnyCellData) -> Self { + CellData::from(any_call_data.data) + } +} + impl AnyCellData { pub fn new(content: String, field_type: FieldType) -> Self { AnyCellData { @@ -102,6 +113,11 @@ impl AnyCellData { #[derive(Default)] pub struct CellBytes(pub Bytes); +pub trait CellBytesParser { + type Object; + fn parse(&self, bytes: &Bytes) -> FlowyResult; +} + impl CellBytes { pub fn new>(data: T) -> Self { let bytes = Bytes::from(data.as_ref().to_vec()); @@ -116,12 +132,19 @@ impl CellBytes { Ok(Self(bytes)) } - pub fn parse<'a, T: TryFrom<&'a [u8]>>(&'a self) -> FlowyResult + pub fn with_parser

(&self, parser: P) -> FlowyResult where - >::Error: std::fmt::Debug, + P: CellBytesParser, { - T::try_from(self.0.as_ref()).map_err(internal_error) + parser.parse(&self.0) } + + // pub fn parse<'a, T: TryFrom<&'a [u8]>>(&'a self) -> FlowyResult + // where + // >::Error: std::fmt::Debug, + // { + // T::try_from(self.0.as_ref()).map_err(internal_error) + // } } impl ToString for CellBytes { diff --git a/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs b/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs index 4bbeafa814..b4729c6925 100644 --- a/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs +++ b/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs @@ -163,9 +163,9 @@ where } } -impl std::convert::From for CellData { - fn from(s: String) -> Self { - CellData(Some(s)) +impl std::convert::From for CellData { + fn from(val: T) -> Self { + CellData(Some(val)) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option.rs index b6e1d39410..155965f409 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option.rs @@ -7,6 +7,7 @@ use flowy_derive::ProtoBuf; use flowy_error::{FlowyError, FlowyResult}; use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry}; use serde::{Deserialize, Serialize}; +use std::str::FromStr; #[derive(Default)] pub struct CheckboxTypeOptionBuilder(CheckboxTypeOption); @@ -69,7 +70,7 @@ impl CellDataOperation for CheckboxTypeOption { _cell_rev: Option, ) -> Result { let changeset = changeset.try_into_inner()?; - let cell_data = CheckboxCellData::from_str(&changeset); + let cell_data = CheckboxCellData::from_str(&changeset)?; Ok(cell_data.to_string()) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option_entities.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option_entities.rs index 99f9866d41..5a8bf1e7a7 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option_entities.rs @@ -1,5 +1,7 @@ -use crate::services::cell::{AnyCellData, FromCellString}; +use crate::services::cell::{CellBytesParser, FromCellString}; +use bytes::Bytes; use flowy_error::{FlowyError, FlowyResult}; +use std::str::FromStr; pub const YES: &str = "Yes"; pub const NO: &str = "No"; @@ -7,7 +9,21 @@ pub const NO: &str = "No"; pub struct CheckboxCellData(pub String); impl CheckboxCellData { - pub fn from_str(s: &str) -> Self { + pub fn is_check(&self) -> bool { + self.0 == YES + } +} + +impl AsRef<[u8]> for CheckboxCellData { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl FromStr for CheckboxCellData { + type Err = FlowyError; + + fn from_str(s: &str) -> Result { let lower_case_str: &str = &s.to_lowercase(); let val = match lower_case_str { "1" => Some(true), @@ -20,29 +36,11 @@ impl CheckboxCellData { }; match val { - Some(true) => Self(YES.to_string()), - Some(false) => Self(NO.to_string()), - None => Self("".to_string()), + Some(true) => Ok(Self(YES.to_string())), + Some(false) => Ok(Self(NO.to_string())), + None => Ok(Self("".to_string())), } } - - pub fn is_check(&self) -> bool { - &self.0 == YES - } -} - -impl AsRef<[u8]> for CheckboxCellData { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -impl std::convert::TryFrom for CheckboxCellData { - type Error = FlowyError; - - fn try_from(value: AnyCellData) -> Result { - Ok(Self::from_str(&value.data)) - } } impl FromCellString for CheckboxCellData { @@ -50,7 +48,7 @@ impl FromCellString for CheckboxCellData { where Self: Sized, { - Ok(Self::from_str(s)) + Self::from_str(s) } } @@ -59,3 +57,13 @@ impl ToString for CheckboxCellData { self.0.clone() } } +pub struct CheckboxCellDataParser; +impl CellBytesParser for CheckboxCellDataParser { + type Object = CheckboxCellData; + fn parse(&self, bytes: &Bytes) -> FlowyResult { + match String::from_utf8(bytes.to_vec()) { + Ok(s) => CheckboxCellData::from_str(&s), + Err(_) => Ok(CheckboxCellData("".to_string())), + } + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/tests.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_tests.rs similarity index 100% rename from frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/tests.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_tests.rs diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/mod.rs index 705b9bcdb0..35b6500a0d 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/mod.rs @@ -1,6 +1,6 @@ mod checkbox_option; mod checkbox_option_entities; -mod tests; +mod checkbox_tests; pub use checkbox_option::*; pub use checkbox_option_entities::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option.rs index 63ed4ee2a9..783b9faa7c 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option.rs @@ -1,22 +1,17 @@ -use crate::entities::{FieldType}; - +use crate::entities::FieldType; use crate::impl_type_option; -use crate::services::cell::{ - AnyCellData, CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable, FromCellChangeset, - FromCellString, -}; +use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable}; use crate::services::field::{ BoxTypeOptionBuilder, DateCellChangeset, DateCellData, DateFormat, DateTimestamp, TimeFormat, TypeOptionBuilder, }; use bytes::Bytes; use chrono::format::strftime::StrftimeItems; use chrono::{NaiveDateTime, Timelike}; -use flowy_derive::{ProtoBuf}; +use flowy_derive::ProtoBuf; use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry}; use serde::{Deserialize, Serialize}; - // Date #[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)] pub struct DateTypeOption { @@ -129,7 +124,8 @@ impl CellDisplayable for DateTypeOption { _field_rev: &FieldRevision, ) -> FlowyResult { let timestamp = cell_data.try_into_inner()?; - CellBytes::from(self.today_desc_from_timestamp(timestamp)) + let date_cell_data = self.today_desc_from_timestamp(timestamp); + CellBytes::from(date_cell_data) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option_entities.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option_entities.rs index aa63eee9dd..b73c89a9b2 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option_entities.rs @@ -1,6 +1,7 @@ use crate::entities::CellChangeset; use crate::entities::{CellIdentifier, CellIdentifierPayload}; -use crate::services::cell::{AnyCellData, FromCellChangeset, FromCellString}; +use crate::services::cell::{CellBytesParser, FromCellChangeset, FromCellString}; +use bytes::Bytes; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_error::{internal_error, ErrorCode, FlowyResult}; @@ -117,12 +118,6 @@ impl FromCellString for DateTimestamp { } } -impl std::convert::From for DateTimestamp { - fn from(data: AnyCellData) -> Self { - let num = data.data.parse::().unwrap_or(0); - DateTimestamp(num) - } -} #[derive(Clone, Debug, Copy, EnumIter, Serialize, Deserialize, ProtoBuf_Enum)] pub enum DateFormat { Local = 0, @@ -204,3 +199,12 @@ impl std::default::Default for TimeFormat { TimeFormat::TwentyFourHour } } + +pub struct DateCellDataParser(); +impl CellBytesParser for DateCellDataParser { + type Object = DateCellData; + + fn parse(&self, bytes: &Bytes) -> FlowyResult { + DateCellData::try_from(bytes.as_ref()).map_err(internal_error) + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/tests.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_tests.rs similarity index 97% rename from frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/tests.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_tests.rs index 22e6f08b49..0c81268f77 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/tests.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_tests.rs @@ -2,8 +2,8 @@ mod tests { use crate::entities::FieldType; use crate::services::cell::{CellDataChangeset, CellDataOperation}; - use crate::services::field::FieldBuilder; - use crate::services::field::{DateCellChangeset, DateCellData, DateFormat, DateTypeOption, TimeFormat}; + use crate::services::field::*; + // use crate::services::field::{DateCellChangeset, DateCellData, DateFormat, DateTypeOption, TimeFormat}; use flowy_grid_data_model::revision::FieldRevision; use strum::IntoEnumIterator; @@ -260,7 +260,7 @@ mod tests { let decoded_data = type_option .decode_cell_data(encoded_data.into(), &FieldType::DateTime, field_rev) .unwrap() - .parse::() + .with_parser(DateCellDataParser()) .unwrap(); if type_option.include_time { diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/mod.rs index 9785924a8c..fe00a14889 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/mod.rs @@ -1,6 +1,6 @@ mod date_option; mod date_option_entities; -mod tests; +mod date_tests; pub use date_option::*; pub use date_option_entities::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/mod.rs index 8b4f9b2896..32a91961c9 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/mod.rs @@ -2,7 +2,7 @@ mod format; mod number_option; mod number_option_entities; -mod tests; +mod number_tests; pub use format::*; pub use number_option::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option.rs index 001bb4a0bc..26d1d64248 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option.rs @@ -1,8 +1,6 @@ -use crate::impl_type_option; - use crate::entities::FieldType; +use crate::impl_type_option; use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation}; -use crate::services::field::number_currency::Currency; use crate::services::field::type_options::number_type_option::format::*; use crate::services::field::{BoxTypeOptionBuilder, NumberCellData, TypeOptionBuilder}; use bytes::Bytes; @@ -11,7 +9,7 @@ use flowy_error::{FlowyError, FlowyResult}; use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry}; use rust_decimal::Decimal; -use rusty_money::Money; + use serde::{Deserialize, Serialize}; use std::str::FromStr; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option_entities.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option_entities.rs index e797165b03..7e47556fe5 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option_entities.rs @@ -1,6 +1,8 @@ +use crate::services::cell::CellBytesParser; use crate::services::field::number_currency::Currency; use crate::services::field::{strip_currency_symbol, NumberFormat, STRIP_SYMBOL}; -use flowy_error::{FlowyError, FlowyResult}; +use bytes::Bytes; +use flowy_error::{internal_error, FlowyError, FlowyResult}; use rust_decimal::Decimal; use rusty_money::Money; use std::str::FromStr; @@ -68,17 +70,17 @@ impl NumberCellData { } } -impl FromStr for NumberCellData { - type Err = rust_decimal::Error; - - fn from_str(s: &str) -> Result { - if s.is_empty() { - return Ok(Self::default()); - } - let decimal = Decimal::from_str(s)?; - Ok(Self::from_decimal(decimal)) - } -} +// impl FromStr for NumberCellData { +// type Err = FlowyError; +// +// fn from_str(s: &str) -> Result { +// if s.is_empty() { +// return Ok(Self::default()); +// } +// let decimal = Decimal::from_str(s).map_err(internal_error)?; +// Ok(Self::from_decimal(decimal)) +// } +// } impl ToString for NumberCellData { fn to_string(&self) -> String { @@ -91,3 +93,13 @@ impl ToString for NumberCellData { } } } +pub struct NumberCellDataParser(pub NumberFormat); +impl CellBytesParser for NumberCellDataParser { + type Object = NumberCellData; + fn parse(&self, bytes: &Bytes) -> FlowyResult { + match String::from_utf8(bytes.to_vec()) { + Ok(s) => NumberCellData::from_format_str(&s, true, &self.0), + Err(_) => Ok(NumberCellData::default()), + } + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/tests.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_tests.rs similarity index 100% rename from frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/tests.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_tests.rs diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs index 050cf196f3..759114ca5a 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs @@ -180,7 +180,7 @@ mod tests { type_option .decode_cell_data(cell_data.into(), &field_type, field_rev) .unwrap() - .parse::() + .with_parser(SelectOptionCellDataParser()) .unwrap() .select_options, ); diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_option.rs index dcea1e209d..c06bf25957 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_option.rs @@ -1,8 +1,9 @@ use crate::entities::{CellChangeset, CellIdentifier, CellIdentifierPayload, FieldType}; -use crate::services::cell::{AnyCellData, CellBytes, CellData, CellDisplayable, FromCellChangeset, FromCellString}; +use crate::services::cell::{CellBytes, CellBytesParser, CellData, CellDisplayable, FromCellChangeset, FromCellString}; use crate::services::field::{MultiSelectTypeOption, SingleSelectTypeOption}; +use bytes::Bytes; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; -use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult}; +use flowy_error::{internal_error, ErrorCode, FlowyResult}; use flowy_grid_data_model::parser::NotEmptyStr; use flowy_grid_data_model::revision::{FieldRevision, TypeOptionDataEntry}; use nanoid::nanoid; @@ -160,20 +161,6 @@ impl SelectOptionIds { } } -impl std::convert::TryFrom for SelectOptionIds { - type Error = FlowyError; - - fn try_from(value: AnyCellData) -> Result { - Ok(Self::from(value.data)) - } -} - -impl std::convert::From for CellData { - fn from(any_cell_data: AnyCellData) -> Self { - any_cell_data.data.into() - } -} - impl FromCellString for SelectOptionIds { fn from_cell_str(s: &str) -> FlowyResult where @@ -215,6 +202,25 @@ impl std::ops::DerefMut for SelectOptionIds { &mut self.0 } } +pub struct SelectOptionIdsParser(); +impl CellBytesParser for SelectOptionIdsParser { + type Object = SelectOptionIds; + fn parse(&self, bytes: &Bytes) -> FlowyResult { + match String::from_utf8(bytes.to_vec()) { + Ok(s) => Ok(SelectOptionIds::from(s)), + Err(_) => Ok(SelectOptionIds::from("".to_owned())), + } + } +} + +pub struct SelectOptionCellDataParser(); +impl CellBytesParser for SelectOptionCellDataParser { + type Object = SelectOptionCellData; + + fn parse(&self, bytes: &Bytes) -> FlowyResult { + SelectOptionCellData::try_from(bytes.as_ref()).map_err(internal_error) + } +} #[derive(Clone, Debug, Default, ProtoBuf)] pub struct SelectOptionCellChangesetPayload { diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/single_select_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/single_select_type_option.rs index f92fbd73d0..8aae4bf677 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/single_select_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/single_select_type_option.rs @@ -102,7 +102,7 @@ impl TypeOptionBuilder for SingleSelectTypeOptionBuilder { mod tests { use crate::entities::FieldType; use crate::services::cell::CellDataOperation; - + use crate::services::field::type_options::*; use crate::services::field::FieldBuilder; use flowy_grid_data_model::revision::FieldRevision; @@ -162,7 +162,7 @@ mod tests { type_option .decode_cell_data(cell_data.into(), &field_type, field_rev) .unwrap() - .parse::() + .with_parser(SelectOptionCellDataParser()) .unwrap() .select_options, ); diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_option.rs index e2d77ddfde..2716206dfd 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_option.rs @@ -1,7 +1,8 @@ use crate::entities::FieldType; use crate::impl_type_option; use crate::services::cell::{ - try_decode_cell_data, AnyCellData, CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable, + try_decode_cell_data, CellBytes, CellBytesParser, CellData, CellDataChangeset, CellDataOperation, CellDisplayable, + FromCellString, }; use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; use bytes::Bytes; @@ -83,11 +84,23 @@ impl AsRef for TextCellData { } } -impl std::convert::TryFrom for TextCellData { - type Error = FlowyError; +impl FromCellString for TextCellData { + fn from_cell_str(s: &str) -> FlowyResult + where + Self: Sized, + { + Ok(TextCellData(s.to_owned())) + } +} - fn try_from(value: AnyCellData) -> Result { - Ok(TextCellData(value.data)) +pub struct TextCellDataParser(); +impl CellBytesParser for TextCellDataParser { + type Object = TextCellData; + fn parse(&self, bytes: &Bytes) -> FlowyResult { + match String::from_utf8(bytes.to_vec()) { + Ok(s) => Ok(TextCellData(s)), + Err(_) => Ok(TextCellData("".to_owned())), + } } } @@ -95,7 +108,7 @@ impl std::convert::TryFrom for TextCellData { mod tests { use crate::entities::FieldType; use crate::services::cell::CellDataOperation; - + use crate::services::field::FieldBuilder; use crate::services::field::*; @@ -111,7 +124,7 @@ mod tests { type_option .decode_cell_data(1647251762.to_string().into(), &field_type, &date_time_field_rev) .unwrap() - .parse::() + .with_parser(DateCellDataParser()) .unwrap() .date, "Mar 14,2022".to_owned() @@ -131,7 +144,7 @@ mod tests { &single_select_field_rev ) .unwrap() - .parse::() + .with_parser(SelectOptionCellDataParser()) .unwrap() .select_options, vec![done_option], @@ -154,7 +167,7 @@ mod tests { type_option .decode_cell_data(cell_data.into(), &FieldType::MultiSelect, &multi_select_field_rev) .unwrap() - .parse::() + .with_parser(SelectOptionCellDataParser()) .unwrap() .select_options, vec![google_option, facebook_option] diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/mod.rs index ee0e0848dd..6c8dc96f88 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/mod.rs @@ -1,6 +1,6 @@ -mod tests; mod url_option; mod url_option_entities; +mod url_tests; pub use url_option::*; pub use url_option_entities::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option.rs index d5fe0f287b..0d4ceb5caf 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option.rs @@ -1,13 +1,11 @@ use crate::entities::FieldType; use crate::impl_type_option; -use crate::services::cell::{ - AnyCellData, CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable, FromCellString, -}; +use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable}; use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder, URLCellData}; use bytes::Bytes; use fancy_regex::Regex; use flowy_derive::ProtoBuf; -use flowy_error::{internal_error, FlowyError, FlowyResult}; +use flowy_error::{FlowyError, FlowyResult}; use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry}; use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; @@ -64,16 +62,12 @@ impl CellDataOperation for URLTypeOption { changeset: CellDataChangeset, _cell_rev: Option, ) -> Result { - let changeset = changeset.try_into_inner()?; + let content = changeset.try_into_inner()?; let mut url = "".to_string(); - if let Ok(Some(m)) = URL_REGEX.find(&changeset) { + if let Ok(Some(m)) = URL_REGEX.find(&content) { url = auto_append_scheme(m.as_str()); } - URLCellData { - url, - content: changeset, - } - .to_json() + URLCellData { url, content }.to_json() } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option_entities.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option_entities.rs index b78335349e..ddb84cf9a7 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option_entities.rs @@ -1,6 +1,7 @@ -use crate::services::cell::{AnyCellData, FromCellString}; +use crate::services::cell::{CellBytesParser, FromCellString}; +use bytes::Bytes; use flowy_derive::ProtoBuf; -use flowy_error::{internal_error, FlowyError, FlowyResult}; +use flowy_error::{internal_error, FlowyResult}; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)] @@ -25,16 +26,17 @@ impl URLCellData { } } +pub struct URLCellDataParser(); +impl CellBytesParser for URLCellDataParser { + type Object = URLCellData; + + fn parse(&self, bytes: &Bytes) -> FlowyResult { + URLCellData::try_from(bytes.as_ref()).map_err(internal_error) + } +} + impl FromCellString for URLCellData { fn from_cell_str(s: &str) -> FlowyResult { serde_json::from_str::(s).map_err(internal_error) } } - -impl std::convert::TryFrom for URLCellData { - type Error = FlowyError; - - fn try_from(data: AnyCellData) -> Result { - serde_json::from_str::(&data.data).map_err(internal_error) - } -} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/tests.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_tests.rs similarity index 95% rename from frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/tests.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_tests.rs index 91940fb83c..6f74bdb4d4 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/tests.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_tests.rs @@ -2,7 +2,7 @@ mod tests { use crate::entities::FieldType; use crate::services::cell::{CellData, CellDataOperation}; - use crate::services::field::FieldBuilder; + use crate::services::field::{FieldBuilder, URLCellDataParser}; use crate::services::field::{URLCellData, URLTypeOption}; use flowy_grid_data_model::revision::FieldRevision; @@ -61,7 +61,7 @@ mod tests { type_option .decode_cell_data(encoded_data.into(), field_type, field_rev) .unwrap() - .parse::() + .with_parser(URLCellDataParser()) .unwrap() } } diff --git a/frontend/rust-lib/flowy-grid/src/services/filter/impls/checkbox_filter.rs b/frontend/rust-lib/flowy-grid/src/services/filter/impls/checkbox_filter.rs index c5cf95d34b..ca67d7aeaf 100644 --- a/frontend/rust-lib/flowy-grid/src/services/filter/impls/checkbox_filter.rs +++ b/frontend/rust-lib/flowy-grid/src/services/filter/impls/checkbox_filter.rs @@ -1,5 +1,5 @@ use crate::entities::{CheckboxCondition, GridCheckboxFilter}; -use crate::services::cell::{AnyCellData, CellFilterOperation}; +use crate::services::cell::{AnyCellData, CellData, CellFilterOperation}; use crate::services::field::{CheckboxCellData, CheckboxTypeOption}; use flowy_error::FlowyResult; @@ -18,7 +18,8 @@ impl CellFilterOperation for CheckboxTypeOption { if !any_cell_data.is_checkbox() { return Ok(true); } - let checkbox_cell_data: CheckboxCellData = any_cell_data.try_into()?; + let cell_data: CellData = any_cell_data.into(); + let checkbox_cell_data = cell_data.try_into_inner()?; Ok(filter.is_visible(&checkbox_cell_data)) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/filter/impls/date_filter.rs b/frontend/rust-lib/flowy-grid/src/services/filter/impls/date_filter.rs index ae920c4edb..46e2571438 100644 --- a/frontend/rust-lib/flowy-grid/src/services/filter/impls/date_filter.rs +++ b/frontend/rust-lib/flowy-grid/src/services/filter/impls/date_filter.rs @@ -1,5 +1,5 @@ use crate::entities::{DateFilterCondition, GridDateFilter}; -use crate::services::cell::{AnyCellData, CellFilterOperation}; +use crate::services::cell::{AnyCellData, CellData, CellFilterOperation}; use crate::services::field::{DateTimestamp, DateTypeOption}; use flowy_error::FlowyResult; @@ -34,7 +34,8 @@ impl CellFilterOperation for DateTypeOption { if !any_cell_data.is_date() { return Ok(true); } - let timestamp: DateTimestamp = any_cell_data.into(); + let cell_data: CellData = any_cell_data.into(); + let timestamp = cell_data.try_into_inner()?; Ok(filter.is_visible(timestamp)) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/filter/impls/number_filter.rs b/frontend/rust-lib/flowy-grid/src/services/filter/impls/number_filter.rs index 7f3a5dd212..f44c1d2d62 100644 --- a/frontend/rust-lib/flowy-grid/src/services/filter/impls/number_filter.rs +++ b/frontend/rust-lib/flowy-grid/src/services/filter/impls/number_filter.rs @@ -47,9 +47,7 @@ impl CellFilterOperation for NumberTypeOption { #[cfg(test)] mod tests { use crate::entities::{GridNumberFilter, NumberFilterCondition}; - use crate::services::field::{NumberCellData, NumberFormat}; - use std::str::FromStr; #[test] fn number_filter_equal_test() { let number_filter = GridNumberFilter { @@ -58,7 +56,7 @@ mod tests { }; for (num_str, visible) in [("123", true), ("1234", false), ("", false)] { - let data = NumberCellData::from_str(num_str).unwrap(); + let data = NumberCellData::from_format_str(num_str, true, &NumberFormat::Num).unwrap(); assert_eq!(number_filter.is_visible(&data), visible); } @@ -75,7 +73,7 @@ mod tests { content: Some("12".to_owned()), }; for (num_str, visible) in [("123", true), ("10", false), ("30", true), ("", false)] { - let data = NumberCellData::from_str(num_str).unwrap(); + let data = NumberCellData::from_format_str(num_str, true, &NumberFormat::Num).unwrap(); assert_eq!(number_filter.is_visible(&data), visible); } } @@ -87,7 +85,7 @@ mod tests { content: Some("100".to_owned()), }; for (num_str, visible) in [("12", true), ("1234", false), ("30", true), ("", true)] { - let data = NumberCellData::from_str(num_str).unwrap(); + let data = NumberCellData::from_format_str(num_str, true, &NumberFormat::Num).unwrap(); assert_eq!(number_filter.is_visible(&data), visible); } } diff --git a/frontend/rust-lib/flowy-grid/src/services/filter/impls/text_filter.rs b/frontend/rust-lib/flowy-grid/src/services/filter/impls/text_filter.rs index 45165e35e2..25f3902ceb 100644 --- a/frontend/rust-lib/flowy-grid/src/services/filter/impls/text_filter.rs +++ b/frontend/rust-lib/flowy-grid/src/services/filter/impls/text_filter.rs @@ -1,5 +1,5 @@ use crate::entities::{GridTextFilter, TextFilterCondition}; -use crate::services::cell::{AnyCellData, CellFilterOperation}; +use crate::services::cell::{AnyCellData, CellData, CellFilterOperation}; use crate::services::field::{RichTextTypeOption, TextCellData}; use flowy_error::FlowyResult; @@ -30,7 +30,8 @@ impl CellFilterOperation for RichTextTypeOption { return Ok(true); } - let text_cell_data: TextCellData = any_cell_data.try_into()?; + let cell_data: CellData = any_cell_data.into(); + let text_cell_data = cell_data.try_into_inner()?; Ok(filter.is_visible(text_cell_data)) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/filter/impls/url_filter.rs b/frontend/rust-lib/flowy-grid/src/services/filter/impls/url_filter.rs index 50f9c815fc..15254d4713 100644 --- a/frontend/rust-lib/flowy-grid/src/services/filter/impls/url_filter.rs +++ b/frontend/rust-lib/flowy-grid/src/services/filter/impls/url_filter.rs @@ -1,5 +1,5 @@ use crate::entities::GridTextFilter; -use crate::services::cell::{AnyCellData, CellFilterOperation}; +use crate::services::cell::{AnyCellData, CellData, CellFilterOperation}; use crate::services::field::{TextCellData, URLTypeOption}; use flowy_error::FlowyResult; @@ -9,7 +9,8 @@ impl CellFilterOperation for URLTypeOption { return Ok(true); } - let text_cell_data: TextCellData = any_cell_data.try_into()?; + let cell_data: CellData = any_cell_data.into(); + let text_cell_data = cell_data.try_into_inner()?; Ok(filter.is_visible(&text_cell_data)) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs index 919e24f2bf..f9a618d145 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -3,7 +3,7 @@ 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::cell::{apply_cell_data_changeset, decode_any_cell_data, CellBytes}; 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; @@ -340,16 +340,16 @@ impl GridRevisionEditor { } pub async fn get_cell(&self, params: &CellIdentifier) -> Option { + let cell_bytes = self.get_cell_bytes(params).await?; + Some(Cell::new(¶ms.field_id, cell_bytes.to_vec())) + } + + pub async fn get_cell_bytes(&self, params: &CellIdentifier) -> Option { let field_rev = self.get_field_rev(¶ms.field_id).await?; let row_rev = self.block_manager.get_row_rev(¶ms.row_id).await.ok()??; let cell_rev = row_rev.cells.get(¶ms.field_id)?.clone(); - let data = decode_any_cell_data(cell_rev.data, &field_rev).to_vec(); - Some(Cell::new(¶ms.field_id, data)) - } - - pub async fn get_cell_display(&self, _params: &CellIdentifier) -> Option { - todo!() + Some(decode_any_cell_data(cell_rev.data, &field_rev)) } pub async fn get_cell_rev(&self, row_id: &str, field_id: &str) -> FlowyResult> { diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs index ec57893d9b..0278a40090 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs @@ -1,5 +1,6 @@ use crate::grid::block_test::script::GridRowTest; use crate::grid::block_test::script::RowScript::*; +use flowy_grid::entities::FieldType; use flowy_grid_data_model::revision::RowMetaChangeset; #[tokio::test] @@ -67,15 +68,48 @@ async fn grid_row_add_cells_test() { let mut test = GridRowTest::new().await; let mut builder = test.row_builder(); - builder.insert_text_cell("hello world"); - builder.insert_number_cell("18,443"); - builder.insert_date_cell("1647251762"); - builder.insert_single_select_cell(|options| options.first().unwrap()); + let text_field_id = builder.insert_text_cell("hello world"); + let number_field_id = builder.insert_number_cell("18,443"); + let date_field_id = builder.insert_date_cell("1647251762"); + let single_select_field_id = builder.insert_single_select_cell(|options| options.first().unwrap()); builder.insert_multi_select_cell(|options| options); builder.insert_checkbox_cell("false"); - builder.insert_url_cell("1"); + let url_field_id = builder.insert_url_cell("https://appflowy.io"); let row_rev = builder.build(); - let scripts = vec![CreateRow { row_rev }]; + let row_id = row_rev.id.clone(); + let scripts = vec![ + CreateRow { row_rev }, + AssertCell { + row_id: row_id.clone(), + field_id: text_field_id, + field_type: FieldType::RichText, + expected: "hello world".to_owned(), + }, + AssertCell { + row_id: row_id.clone(), + field_id: number_field_id, + field_type: FieldType::Number, + expected: "$18,443.00".to_owned(), + }, + AssertCell { + row_id: row_id.clone(), + field_id: single_select_field_id, + field_type: FieldType::SingleSelect, + expected: "Completed".to_owned(), + }, + AssertCell { + row_id: row_id.clone(), + field_id: date_field_id, + field_type: FieldType::DateTime, + expected: "2022/03/14".to_owned(), + }, + AssertCell { + row_id: row_id.clone(), + field_id: url_field_id, + field_type: FieldType::URL, + expected: "https://appflowy.io/".to_owned(), + }, + ]; test.run_scripts(scripts).await; } diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs index 642cd51670..872748fd23 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs @@ -1,7 +1,11 @@ use crate::grid::block_test::util::GridRowTestBuilder; use crate::grid::grid_editor::GridEditorTest; -use flowy_grid::entities::{CellIdentifier, RowInfo}; +use flowy_grid::entities::{CellIdentifier, FieldType, RowInfo}; +use flowy_grid::services::field::{ + DateCellDataParser, NumberCellDataParser, NumberFormat, NumberTypeOption, SelectOptionCellDataParser, + SelectOptionIdsParser, SelectOptionOperation, SingleSelectTypeOption, TextCellDataParser, URLCellDataParser, +}; use flowy_grid_data_model::revision::{ GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision, }; @@ -24,7 +28,8 @@ pub enum RowScript { AssertCell { row_id: String, field_id: String, - expected_display: Option, + field_type: FieldType, + expected: String, }, AssertRowCount(usize), CreateBlock { @@ -101,20 +106,15 @@ impl GridRowTest { RowScript::AssertCell { row_id, field_id, - expected_display, + field_type, + expected, } => { let id = CellIdentifier { grid_id: self.grid_id.clone(), field_id, row_id, }; - let display = self.editor.get_cell_display(&id).await; - match expected_display { - None => {} - Some(expected_display) => { - assert_eq!(display.unwrap(), expected_display); - } - } + self.compare_cell_content(id, field_type, expected).await; } RowScript::AssertRow { expected_row } => { let row = &*self @@ -153,6 +153,72 @@ impl GridRowTest { } } } + + async fn compare_cell_content(&self, cell_id: CellIdentifier, field_type: FieldType, expected: String) { + match field_type { + FieldType::RichText => { + let cell_data = self + .editor + .get_cell_bytes(&cell_id) + .await + .unwrap() + .with_parser(TextCellDataParser()) + .unwrap(); + + assert_eq!(cell_data.as_ref(), &expected); + } + FieldType::Number => { + let field_rev = self.editor.get_field_rev(&cell_id.field_id).await.unwrap(); + let number_type_option = field_rev + .get_type_option_entry::(FieldType::Number.into()) + .unwrap(); + let cell_data = self + .editor + .get_cell_bytes(&cell_id) + .await + .unwrap() + .with_parser(NumberCellDataParser(number_type_option.format.clone())) + .unwrap(); + assert_eq!(cell_data.to_string(), expected); + } + FieldType::DateTime => { + let cell_data = self + .editor + .get_cell_bytes(&cell_id) + .await + .unwrap() + .with_parser(DateCellDataParser()) + .unwrap(); + + assert_eq!(cell_data.date, expected); + } + FieldType::SingleSelect => { + let select_options = self + .editor + .get_cell_bytes(&cell_id) + .await + .unwrap() + .with_parser(SelectOptionCellDataParser()) + .unwrap(); + let select_option = select_options.select_options.first().unwrap(); + assert_eq!(select_option.name, expected); + } + FieldType::MultiSelect => {} + FieldType::Checkbox => {} + FieldType::URL => { + let cell_data = self + .editor + .get_cell_bytes(&cell_id) + .await + .unwrap() + .with_parser(URLCellDataParser()) + .unwrap(); + + assert_eq!(cell_data.content, expected); + assert_eq!(cell_data.url, expected); + } + } + } } impl std::ops::Deref for GridRowTest { diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs index 07db75161a..397c7100db 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs @@ -23,21 +23,24 @@ impl<'a> GridRowTestBuilder<'a> { } } - pub fn insert_text_cell(&mut self, data: &str) { + pub fn insert_text_cell(&mut self, data: &str) -> String { let text_field = self.field_rev_with_type(&FieldType::RichText); self.inner_builder .insert_cell(&text_field.id, data.to_string()) .unwrap(); + + text_field.id.clone() } - pub fn insert_number_cell(&mut self, data: &str) { + pub fn insert_number_cell(&mut self, data: &str) -> String { let number_field = self.field_rev_with_type(&FieldType::Number); self.inner_builder .insert_cell(&number_field.id, data.to_string()) .unwrap(); + number_field.id.clone() } - pub fn insert_date_cell(&mut self, data: &str) { + pub fn insert_date_cell(&mut self, data: &str) -> String { let value = serde_json::to_string(&DateCellChangeset { date: Some(data.to_string()), time: None, @@ -45,6 +48,7 @@ impl<'a> GridRowTestBuilder<'a> { .unwrap(); let date_field = self.field_rev_with_type(&FieldType::DateTime); self.inner_builder.insert_cell(&date_field.id, value).unwrap(); + date_field.id.clone() } pub fn insert_checkbox_cell(&mut self, data: &str) { @@ -54,14 +58,13 @@ impl<'a> GridRowTestBuilder<'a> { .unwrap(); } - pub fn insert_url_cell(&mut self, data: &str) { - let number_field = self.field_rev_with_type(&FieldType::URL); - self.inner_builder - .insert_cell(&number_field.id, data.to_string()) - .unwrap(); + pub fn insert_url_cell(&mut self, data: &str) -> String { + let url_field = self.field_rev_with_type(&FieldType::URL); + self.inner_builder.insert_cell(&url_field.id, data.to_string()).unwrap(); + url_field.id.clone() } - pub fn insert_single_select_cell(&mut self, f: F) + pub fn insert_single_select_cell(&mut self, f: F) -> String where F: Fn(&Vec) -> &SelectOption, { @@ -71,6 +74,8 @@ impl<'a> GridRowTestBuilder<'a> { self.inner_builder .insert_select_option_cell(&single_select_field.id, option.id.clone()) .unwrap(); + + single_select_field.id.clone() } pub fn insert_multi_select_cell(&mut self, f: F) diff --git a/frontend/rust-lib/flowy-grid/tests/grid/filter_test/text_filter_test.rs b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/text_filter_test.rs index 29614b21b2..e954ebe512 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/filter_test/text_filter_test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/text_filter_test.rs @@ -1,12 +1,12 @@ use crate::grid::filter_test::script::FilterScript::*; use crate::grid::filter_test::script::*; -use flowy_grid::entities::{CreateGridFilterPayload, TextFilterCondition}; +use flowy_grid::entities::{CreateGridFilterPayload, FieldType, TextFilterCondition}; use flowy_grid_data_model::revision::FieldRevision; #[tokio::test] async fn grid_filter_create_test() { let mut test = GridFilterTest::new().await; - let field_rev = test.text_field(); + let field_rev = test.get_field_rev(FieldType::RichText); let payload = CreateGridFilterPayload::new(field_rev, TextFilterCondition::TextIsEmpty, Some("abc".to_owned())); let scripts = vec![InsertGridTableFilter { payload }, AssertTableFilterCount { count: 1 }]; test.run_scripts(scripts).await; @@ -16,7 +16,7 @@ async fn grid_filter_create_test() { #[should_panic] async fn grid_filter_invalid_condition_panic_test() { let mut test = GridFilterTest::new().await; - let field_rev = test.text_field().clone(); + let field_rev = test.get_field_rev(FieldType::RichText).clone(); // 100 is not a valid condition, so this test should be panic. let payload = CreateGridFilterPayload::new(&field_rev, 100, Some("".to_owned())); @@ -27,7 +27,7 @@ async fn grid_filter_invalid_condition_panic_test() { #[tokio::test] async fn grid_filter_delete_test() { let mut test = GridFilterTest::new().await; - let field_rev = test.text_field().clone(); + let field_rev = test.get_field_rev(FieldType::RichText).clone(); let payload = create_filter(&field_rev, TextFilterCondition::TextIsEmpty, "abc"); let scripts = vec![InsertGridTableFilter { payload }, AssertTableFilterCount { count: 1 }]; test.run_scripts(scripts).await; @@ -36,7 +36,7 @@ async fn grid_filter_delete_test() { test.run_scripts(vec![ DeleteGridTableFilter { filter_id: filter.id, - field_rev, + field_rev: field_rev.as_ref().clone(), }, AssertTableFilterCount { count: 0 }, ]) diff --git a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs index d5429623a3..3366264936 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs @@ -64,7 +64,7 @@ impl GridEditorTest { } } - pub(crate) async fn get_row_revs(&self) -> Vec> { + pub async fn get_row_revs(&self) -> Vec> { self.editor .grid_block_snapshots(None) .await @@ -79,12 +79,12 @@ impl GridEditorTest { self.editor.get_grid_filter(&layout_type).await.unwrap() } - pub fn text_field(&self) -> &FieldRevision { + pub fn get_field_rev(&self, field_type: FieldType) -> &Arc { self.field_revs .iter() .filter(|field_rev| { let t_field_type: FieldType = field_rev.field_type_rev.into(); - t_field_type == FieldType::RichText + t_field_type == field_type }) .collect::>() .pop() @@ -129,7 +129,6 @@ fn make_test_grid() -> BuildGridContext { FieldType::SingleSelect => { // Single Select let single_select = SingleSelectTypeOptionBuilder::default() - .option(SelectOption::new("Live")) .option(SelectOption::new("Completed")) .option(SelectOption::new("Planned")) .option(SelectOption::new("Paused")); From d02acbae6eb0292188ed83149183e52dd5c29a20 Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 13 Jul 2022 22:56:34 +0800 Subject: [PATCH 5/7] chore: add more test --- .../grid/cell/cell_service/cell_service.dart | 4 +- .../application/grid/cell/date_cal_bloc.dart | 1 + .../application/grid/cell/date_cell_bloc.dart | 2 +- .../application/grid/cell/url_cell_bloc.dart | 2 +- .../grid/cell/url_cell_editor_bloc.dart | 2 +- .../grid/field/type_option/date_bloc.dart | 1 + .../src/widgets/header/type_option/date.dart | 2 +- ...kbox_option.rs => checkbox_type_option.rs} | 0 ...es.rs => checkbox_type_option_entities.rs} | 2 +- .../type_options/checkbox_type_option/mod.rs | 8 +- .../{date_option.rs => date_type_option.rs} | 0 ...tities.rs => date_type_option_entities.rs} | 0 .../type_options/date_type_option/mod.rs | 8 +- .../type_options/number_type_option/mod.rs | 8 +- ...number_option.rs => number_type_option.rs} | 0 ...ties.rs => number_type_option_entities.rs} | 2 +- .../type_options/text_type_option/mod.rs | 4 +- .../{text_option.rs => text_type_option.rs} | 0 .../field/type_options/url_type_option/mod.rs | 8 +- .../{url_option.rs => url_type_option.rs} | 0 ...ntities.rs => url_type_option_entities.rs} | 0 .../flowy-grid/src/services/grid_editor.rs | 6 +- .../src/services/row/row_builder.rs | 9 +- .../tests/grid/block_test/row_test.rs | 109 ++++++----- .../tests/grid/block_test/script.rs | 174 ++++++++++++++++-- .../flowy-grid/tests/grid/block_test/util.rs | 39 +++- .../flowy-grid/tests/grid/grid_editor.rs | 20 +- .../src/client_grid/grid_builder.rs | 4 + 28 files changed, 311 insertions(+), 104 deletions(-) rename frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/{checkbox_option.rs => checkbox_type_option.rs} (100%) rename frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/{checkbox_option_entities.rs => checkbox_type_option_entities.rs} (97%) rename frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/{date_option.rs => date_type_option.rs} (100%) rename frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/{date_option_entities.rs => date_type_option_entities.rs} (100%) rename frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/{number_option.rs => number_type_option.rs} (100%) rename frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/{number_option_entities.rs => number_type_option_entities.rs} (98%) rename frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/{text_option.rs => text_type_option.rs} (100%) rename frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/{url_option.rs => url_type_option.rs} (100%) rename frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/{url_option_entities.rs => url_type_option_entities.rs} (100%) diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart index 5e406c0c26..bc5c683414 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart @@ -7,10 +7,10 @@ import 'package:flowy_sdk/dispatch/dispatch.dart'; import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart'; -import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart'; -import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option_entities.pb.dart'; import 'package:flutter/foundation.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart'; diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart index 28f8bf1b10..c5cfe397a3 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart @@ -5,6 +5,7 @@ import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:table_calendar/table_calendar.dart'; diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart index 00780143cc..7c03354134 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart @@ -1,4 +1,4 @@ -import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart index e1fe39c3bf..6129f90f71 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart @@ -1,4 +1,4 @@ -import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option_entities.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_editor_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_editor_bloc.dart index 6e4990943f..dcb643d535 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_editor_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_editor_bloc.dart @@ -1,4 +1,4 @@ -import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option_entities.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/date_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/date_bloc.dart index 8784422c54..556b33d40f 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/date_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/date_bloc.dart @@ -1,5 +1,6 @@ import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/date.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/date.dart index f4031dc9ce..b9b2f8e89d 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/date.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/date.dart @@ -9,7 +9,7 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/button.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; -import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option.rs similarity index 100% rename from frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option.rs diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option_entities.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs similarity index 97% rename from frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option_entities.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs index 5a8bf1e7a7..54cde9b310 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs @@ -57,7 +57,7 @@ impl ToString for CheckboxCellData { self.0.clone() } } -pub struct CheckboxCellDataParser; +pub struct CheckboxCellDataParser(); impl CellBytesParser for CheckboxCellDataParser { type Object = CheckboxCellData; fn parse(&self, bytes: &Bytes) -> FlowyResult { diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/mod.rs index 35b6500a0d..61013fac08 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/mod.rs @@ -1,6 +1,6 @@ -mod checkbox_option; -mod checkbox_option_entities; mod checkbox_tests; +mod checkbox_type_option; +mod checkbox_type_option_entities; -pub use checkbox_option::*; -pub use checkbox_option_entities::*; +pub use checkbox_type_option::*; +pub use checkbox_type_option_entities::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option.rs similarity index 100% rename from frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option.rs diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option_entities.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option_entities.rs similarity index 100% rename from frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option_entities.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option_entities.rs diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/mod.rs index fe00a14889..8ca49fdd15 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/mod.rs @@ -1,6 +1,6 @@ -mod date_option; -mod date_option_entities; mod date_tests; +mod date_type_option; +mod date_type_option_entities; -pub use date_option::*; -pub use date_option_entities::*; +pub use date_type_option::*; +pub use date_type_option_entities::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/mod.rs index 32a91961c9..4b2bcc1ecd 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/mod.rs @@ -1,9 +1,9 @@ #![allow(clippy::module_inception)] mod format; -mod number_option; -mod number_option_entities; mod number_tests; +mod number_type_option; +mod number_type_option_entities; pub use format::*; -pub use number_option::*; -pub use number_option_entities::*; +pub use number_type_option::*; +pub use number_type_option_entities::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs similarity index 100% rename from frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option_entities.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option_entities.rs similarity index 98% rename from frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option_entities.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option_entities.rs index 7e47556fe5..6297114a07 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option_entities.rs @@ -2,7 +2,7 @@ use crate::services::cell::CellBytesParser; use crate::services::field::number_currency::Currency; use crate::services::field::{strip_currency_symbol, NumberFormat, STRIP_SYMBOL}; use bytes::Bytes; -use flowy_error::{internal_error, FlowyError, FlowyResult}; +use flowy_error::{FlowyError, FlowyResult}; use rust_decimal::Decimal; use rusty_money::Money; use std::str::FromStr; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/mod.rs index 9f41c61fdd..3c1d2287cd 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/mod.rs @@ -1,2 +1,2 @@ -mod text_option; -pub use text_option::*; +mod text_type_option; +pub use text_type_option::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_type_option.rs similarity index 100% rename from frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_option.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_type_option.rs diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/mod.rs index 6c8dc96f88..e3b2ae16bd 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/mod.rs @@ -1,6 +1,6 @@ -mod url_option; -mod url_option_entities; mod url_tests; +mod url_type_option; +mod url_type_option_entities; -pub use url_option::*; -pub use url_option_entities::*; +pub use url_type_option::*; +pub use url_type_option_entities::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option.rs similarity index 100% rename from frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option.rs diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option_entities.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option_entities.rs similarity index 100% rename from frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option_entities.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option_entities.rs diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs index f9a618d145..70804f1404 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -270,10 +270,14 @@ impl GridRevisionEditor { pub async fn create_row(&self, start_row_id: Option) -> FlowyResult { let field_revs = self.grid_pad.read().await.get_field_revs(None)?; + let field_revs_ref = field_revs + .iter() + .map(|field_rev| field_rev.as_ref()) + .collect::>(); let block_id = self.block_id().await?; // insert empty row below the row whose id is upper_row_id - let row_rev = RowRevisionBuilder::new(&field_revs).build(&block_id); + let row_rev = RowRevisionBuilder::new(&field_revs_ref).build(&block_id); let row_order = RowInfo::from(&row_rev); // insert the row diff --git a/frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs b/frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs index fb8c2550e4..6ccb553fc1 100644 --- a/frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs +++ b/frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs @@ -4,19 +4,18 @@ use flowy_error::{FlowyError, FlowyResult}; use flowy_grid_data_model::revision::{gen_row_id, CellRevision, FieldRevision, RowRevision, DEFAULT_ROW_HEIGHT}; use indexmap::IndexMap; use std::collections::HashMap; -use std::sync::Arc; pub struct RowRevisionBuilder<'a> { - field_rev_map: HashMap<&'a String, &'a Arc>, + field_rev_map: HashMap<&'a String, &'a FieldRevision>, payload: CreateRowRevisionPayload, } impl<'a> RowRevisionBuilder<'a> { - pub fn new(fields: &'a [Arc]) -> Self { + pub fn new(fields: &'a [&'a FieldRevision]) -> Self { let field_rev_map = fields .iter() - .map(|field| (&field.id, field)) - .collect::>>(); + .map(|field| (&field.id, *field)) + .collect::>(); let payload = CreateRowRevisionPayload { row_id: gen_row_id(), diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs index 0278a40090..02e7ccc050 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs @@ -1,6 +1,8 @@ -use crate::grid::block_test::script::GridRowTest; use crate::grid::block_test::script::RowScript::*; +use crate::grid::block_test::script::{CreateRowScriptBuilder, GridRowTest}; +use crate::grid::grid_editor::{COMPLETED, FACEBOOK, GOOGLE, PAUSED, TWITTER}; use flowy_grid::entities::FieldType; +use flowy_grid::services::field::{NO, SELECTION_IDS_SEPARATOR}; use flowy_grid_data_model::revision::RowMetaChangeset; #[tokio::test] @@ -66,50 +68,65 @@ async fn grid_delete_row() { #[tokio::test] async fn grid_row_add_cells_test() { let mut test = GridRowTest::new().await; - let mut builder = test.row_builder(); + let mut builder = CreateRowScriptBuilder::new(&test); + builder.insert(FieldType::RichText, "hello world", "hello world"); + builder.insert(FieldType::DateTime, "1647251762", "2022/03/14"); + builder.insert(FieldType::Number, "18,443", "$18,443.00"); + builder.insert(FieldType::Checkbox, "false", NO); + builder.insert(FieldType::URL, "https://appflowy.io", "https://appflowy.io"); + builder.insert_single_select_cell(|mut options| options.remove(0), COMPLETED); + builder.insert_multi_select_cell( + |options| options, + &vec![GOOGLE, FACEBOOK, TWITTER].join(SELECTION_IDS_SEPARATOR), + ); - let text_field_id = builder.insert_text_cell("hello world"); - let number_field_id = builder.insert_number_cell("18,443"); - let date_field_id = builder.insert_date_cell("1647251762"); - let single_select_field_id = builder.insert_single_select_cell(|options| options.first().unwrap()); - builder.insert_multi_select_cell(|options| options); - builder.insert_checkbox_cell("false"); - let url_field_id = builder.insert_url_cell("https://appflowy.io"); - - let row_rev = builder.build(); - let row_id = row_rev.id.clone(); - let scripts = vec![ - CreateRow { row_rev }, - AssertCell { - row_id: row_id.clone(), - field_id: text_field_id, - field_type: FieldType::RichText, - expected: "hello world".to_owned(), - }, - AssertCell { - row_id: row_id.clone(), - field_id: number_field_id, - field_type: FieldType::Number, - expected: "$18,443.00".to_owned(), - }, - AssertCell { - row_id: row_id.clone(), - field_id: single_select_field_id, - field_type: FieldType::SingleSelect, - expected: "Completed".to_owned(), - }, - AssertCell { - row_id: row_id.clone(), - field_id: date_field_id, - field_type: FieldType::DateTime, - expected: "2022/03/14".to_owned(), - }, - AssertCell { - row_id: row_id.clone(), - field_id: url_field_id, - field_type: FieldType::URL, - expected: "https://appflowy.io/".to_owned(), - }, - ]; - test.run_scripts(scripts).await; + test.run_scripts(builder.build()).await; +} + +#[tokio::test] +async fn grid_row_insert_number_test() { + let mut test = GridRowTest::new().await; + for (val, expected) in &[("1647251762", "2022/03/14"), ("2022/03/14", ""), ("", "")] { + let mut builder = CreateRowScriptBuilder::new(&test); + builder.insert(FieldType::DateTime, val, expected); + test.run_scripts(builder.build()).await; + } +} + +#[tokio::test] +async fn grid_row_insert_date_test() { + let mut test = GridRowTest::new().await; + for (val, expected) in &[ + ("18,443", "$18,443.00"), + ("0", "$0.00"), + ("100000", "$100,000.00"), + ("$100,000.00", "$100,000.00"), + ("", ""), + ] { + let mut builder = CreateRowScriptBuilder::new(&test); + builder.insert(FieldType::Number, val, expected); + test.run_scripts(builder.build()).await; + } +} +#[tokio::test] +async fn grid_row_insert_single_select_test() { + let mut test = GridRowTest::new().await; + let mut builder = CreateRowScriptBuilder::new(&test); + builder.insert_single_select_cell(|mut options| options.pop().unwrap(), PAUSED); + test.run_scripts(builder.build()).await; +} + +#[tokio::test] +async fn grid_row_insert_multi_select_test() { + let mut test = GridRowTest::new().await; + let mut builder = CreateRowScriptBuilder::new(&test); + builder.insert_multi_select_cell( + |mut options| { + options.remove(0); + options + }, + &vec![FACEBOOK, TWITTER].join(SELECTION_IDS_SEPARATOR), + ); + + test.run_scripts(builder.build()).await; } diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs index 872748fd23..366d1e6c80 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs @@ -1,15 +1,18 @@ +use crate::grid::block_test::script::RowScript::{AssertCell, CreateRow}; use crate::grid::block_test::util::GridRowTestBuilder; use crate::grid::grid_editor::GridEditorTest; use flowy_grid::entities::{CellIdentifier, FieldType, RowInfo}; - use flowy_grid::services::field::{ - DateCellDataParser, NumberCellDataParser, NumberFormat, NumberTypeOption, SelectOptionCellDataParser, - SelectOptionIdsParser, SelectOptionOperation, SingleSelectTypeOption, TextCellDataParser, URLCellDataParser, + CheckboxCellDataParser, DateCellDataParser, MultiSelectTypeOption, NumberCellDataParser, NumberTypeOption, + SelectOption, SelectOptionCellDataParser, SelectOptionIdsParser, SingleSelectTypeOption, TextCellDataParser, + URLCellDataParser, SELECTION_IDS_SEPARATOR, }; use flowy_grid_data_model::revision::{ - GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision, + FieldRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision, }; +use std::collections::HashMap; use std::sync::Arc; +use strum::IntoEnumIterator; pub enum RowScript { CreateEmptyRow, @@ -71,7 +74,19 @@ impl GridRowTest { } pub fn row_builder(&self) -> GridRowTestBuilder { - GridRowTestBuilder::new(self.block_id(), &self.field_revs) + let field_revs_ref = self + .field_revs + .iter() + .map(|field_rev| field_rev.as_ref()) + .collect::>(); + GridRowTestBuilder::new( + self.block_id(), + &self + .field_revs + .iter() + .map(|field_rev| field_rev.as_ref()) + .collect::>(), + ) } pub async fn run_script(&mut self, script: RowScript) { @@ -177,7 +192,7 @@ impl GridRowTest { .get_cell_bytes(&cell_id) .await .unwrap() - .with_parser(NumberCellDataParser(number_type_option.format.clone())) + .with_parser(NumberCellDataParser(number_type_option.format)) .unwrap(); assert_eq!(cell_data.to_string(), expected); } @@ -193,18 +208,45 @@ impl GridRowTest { assert_eq!(cell_data.date, expected); } FieldType::SingleSelect => { - let select_options = self + let cell_data = self .editor .get_cell_bytes(&cell_id) .await .unwrap() .with_parser(SelectOptionCellDataParser()) .unwrap(); - let select_option = select_options.select_options.first().unwrap(); + let select_option = cell_data.select_options.first().unwrap(); assert_eq!(select_option.name, expected); } - FieldType::MultiSelect => {} - FieldType::Checkbox => {} + FieldType::MultiSelect => { + let cell_data = self + .editor + .get_cell_bytes(&cell_id) + .await + .unwrap() + .with_parser(SelectOptionCellDataParser()) + .unwrap(); + + let s = cell_data + .select_options + .into_iter() + .map(|option| option.name) + .collect::>() + .join(SELECTION_IDS_SEPARATOR); + + assert_eq!(s, expected); + } + + FieldType::Checkbox => { + let cell_data = self + .editor + .get_cell_bytes(&cell_id) + .await + .unwrap() + .with_parser(CheckboxCellDataParser()) + .unwrap(); + assert_eq!(cell_data.to_string(), expected); + } FieldType::URL => { let cell_data = self .editor @@ -215,7 +257,7 @@ impl GridRowTest { .unwrap(); assert_eq!(cell_data.content, expected); - assert_eq!(cell_data.url, expected); + // assert_eq!(cell_data.url, expected); } } } @@ -234,3 +276,113 @@ impl std::ops::DerefMut for GridRowTest { &mut self.inner } } + +pub struct CreateRowScriptBuilder<'a> { + builder: GridRowTestBuilder<'a>, + data_by_field_type: HashMap, + output_by_field_type: HashMap, +} + +impl<'a> CreateRowScriptBuilder<'a> { + pub fn new(test: &'a GridRowTest) -> Self { + Self { + builder: test.row_builder(), + data_by_field_type: HashMap::new(), + output_by_field_type: HashMap::new(), + } + } + + pub fn insert(&mut self, field_type: FieldType, input: &str, expected: &str) { + self.data_by_field_type.insert( + field_type, + CellTestData { + input: input.to_string(), + expected: expected.to_owned(), + }, + ); + } + + pub fn insert_single_select_cell(&mut self, f: F, expected: &str) + where + F: Fn(Vec) -> SelectOption, + { + let field_id = self.builder.insert_single_select_cell(f); + self.output_by_field_type.insert( + FieldType::SingleSelect, + CellTestOutput { + field_id, + expected: expected.to_owned(), + }, + ); + } + + pub fn insert_multi_select_cell(&mut self, f: F, expected: &str) + where + F: Fn(Vec) -> Vec, + { + let field_id = self.builder.insert_multi_select_cell(f); + self.output_by_field_type.insert( + FieldType::MultiSelect, + CellTestOutput { + field_id, + expected: expected.to_owned(), + }, + ); + } + + pub fn build(mut self) -> Vec { + let mut scripts = vec![]; + let output_by_field_type = &mut self.output_by_field_type; + + for field_type in FieldType::iter() { + let field_type: FieldType = field_type; + if let Some(data) = self.data_by_field_type.get(&field_type) { + let field_id = match field_type { + FieldType::RichText => self.builder.insert_text_cell(&data.input), + FieldType::Number => self.builder.insert_number_cell(&data.input), + FieldType::DateTime => self.builder.insert_date_cell(&data.input), + FieldType::Checkbox => self.builder.insert_checkbox_cell(&data.input), + FieldType::URL => self.builder.insert_url_cell(&data.input), + _ => "".to_owned(), + }; + + if !field_id.is_empty() { + output_by_field_type.insert( + field_type, + CellTestOutput { + field_id, + expected: data.expected.clone(), + }, + ); + } + } + } + + let row_rev = self.builder.build(); + let row_id = row_rev.id.clone(); + scripts.push(CreateRow { row_rev }); + + for field_type in FieldType::iter() { + if let Some(data) = output_by_field_type.get(&field_type) { + let script = AssertCell { + row_id: row_id.clone(), + field_id: data.field_id.clone(), + field_type, + expected: data.expected.clone(), + }; + scripts.push(script); + } + } + scripts + } +} + +pub struct CellTestData { + pub input: String, + pub expected: String, +} + +struct CellTestOutput { + field_id: String, + expected: String, +} diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs index 397c7100db..ca1e45465e 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs @@ -1,19 +1,21 @@ use flowy_grid::entities::FieldType; -use flowy_grid::services::field::selection_type_option::{SelectOption, SELECTION_IDS_SEPARATOR}; -use flowy_grid::services::field::{DateCellChangeset, MultiSelectTypeOption, SingleSelectTypeOption}; + +use flowy_grid::services::field::{ + DateCellChangeset, MultiSelectTypeOption, SelectOption, SingleSelectTypeOption, SELECTION_IDS_SEPARATOR, +}; use flowy_grid::services::row::RowRevisionBuilder; use flowy_grid_data_model::revision::{FieldRevision, RowRevision}; -use std::sync::Arc; + use strum::EnumCount; pub struct GridRowTestBuilder<'a> { block_id: String, - field_revs: &'a [Arc], + field_revs: &'a [&'a FieldRevision], inner_builder: RowRevisionBuilder<'a>, } impl<'a> GridRowTestBuilder<'a> { - pub fn new(block_id: &str, field_revs: &'a [Arc]) -> Self { + pub fn new(block_id: &str, field_revs: &'a [&'a FieldRevision]) -> Self { assert_eq!(field_revs.len(), FieldType::COUNT); let inner_builder = RowRevisionBuilder::new(field_revs); Self { @@ -51,11 +53,13 @@ impl<'a> GridRowTestBuilder<'a> { date_field.id.clone() } - pub fn insert_checkbox_cell(&mut self, data: &str) { - let number_field = self.field_rev_with_type(&FieldType::Checkbox); + pub fn insert_checkbox_cell(&mut self, data: &str) -> String { + let checkbox_field = self.field_rev_with_type(&FieldType::Checkbox); self.inner_builder - .insert_cell(&number_field.id, data.to_string()) + .insert_cell(&checkbox_field.id, data.to_string()) .unwrap(); + + checkbox_field.id.clone() } pub fn insert_url_cell(&mut self, data: &str) -> String { @@ -64,6 +68,7 @@ impl<'a> GridRowTestBuilder<'a> { url_field.id.clone() } + #[allow(dead_code)] pub fn insert_single_select_cell(&mut self, f: F) -> String where F: Fn(&Vec) -> &SelectOption, @@ -78,7 +83,7 @@ impl<'a> GridRowTestBuilder<'a> { single_select_field.id.clone() } - pub fn insert_multi_select_cell(&mut self, f: F) + pub fn insert_multi_select_cell(&mut self, f: F) -> String where F: Fn(&Vec) -> &Vec, { @@ -93,6 +98,8 @@ impl<'a> GridRowTestBuilder<'a> { self.inner_builder .insert_select_option_cell(&multi_select_field.id, ops_ids) .unwrap(); + + multi_select_field.id.clone() } pub fn field_rev_with_type(&self, field_type: &FieldType) -> FieldRevision { @@ -111,3 +118,17 @@ impl<'a> GridRowTestBuilder<'a> { self.inner_builder.build(&self.block_id) } } + +impl<'a> std::ops::Deref for GridRowTestBuilder<'a> { + type Target = RowRevisionBuilder<'a>; + + fn deref(&self) -> &Self::Target { + &self.inner_builder + } +} + +impl<'a> std::ops::DerefMut for GridRowTestBuilder<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner_builder + } +} diff --git a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs index 3366264936..8d6a2043d0 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs @@ -96,6 +96,14 @@ impl GridEditorTest { } } +pub const GOOGLE: &str = "Google"; +pub const FACEBOOK: &str = "Facebook"; +pub const TWITTER: &str = "Twitter"; + +pub const COMPLETED: &str = "Completed"; +pub const PLANNED: &str = "Planned"; +pub const PAUSED: &str = "Paused"; + // This grid is assumed to contain all the Fields. fn make_test_grid() -> BuildGridContext { let mut grid_builder = GridBuilder::new(); @@ -129,18 +137,18 @@ fn make_test_grid() -> BuildGridContext { FieldType::SingleSelect => { // Single Select let single_select = SingleSelectTypeOptionBuilder::default() - .option(SelectOption::new("Completed")) - .option(SelectOption::new("Planned")) - .option(SelectOption::new("Paused")); + .option(SelectOption::new(COMPLETED)) + .option(SelectOption::new(PLANNED)) + .option(SelectOption::new(PAUSED)); let single_select_field = FieldBuilder::new(single_select).name("Status").visibility(true).build(); grid_builder.add_field(single_select_field); } FieldType::MultiSelect => { // MultiSelect let multi_select = MultiSelectTypeOptionBuilder::default() - .option(SelectOption::new("Google")) - .option(SelectOption::new("Facebook")) - .option(SelectOption::new("Twitter")); + .option(SelectOption::new(GOOGLE)) + .option(SelectOption::new(FACEBOOK)) + .option(SelectOption::new(TWITTER)); let multi_select_field = FieldBuilder::new(multi_select) .name("Platform") .visibility(true) diff --git a/shared-lib/flowy-sync/src/client_grid/grid_builder.rs b/shared-lib/flowy-sync/src/client_grid/grid_builder.rs index 1dffa6c0c6..b0cc2b7bb0 100644 --- a/shared-lib/flowy-sync/src/client_grid/grid_builder.rs +++ b/shared-lib/flowy-sync/src/client_grid/grid_builder.rs @@ -45,6 +45,10 @@ impl GridBuilder { self.add_row(row); } + // pub fn field_revs(&self) -> Vec { + // self.build_context.field_revs + // } + pub fn build(self) -> BuildGridContext { self.build_context } From ab80e9b0ed8ddb51cfc26f42a682e3be1e9eba09 Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 14 Jul 2022 09:29:05 +0800 Subject: [PATCH 6/7] chore: add 5 rows in test grid --- .../type_options/checkbox_type_option/mod.rs | 1 + .../type_options/date_type_option/mod.rs | 1 + .../type_options/text_type_option/mod.rs | 1 + .../field/type_options/url_type_option/mod.rs | 1 + .../flowy-grid/src/services/grid_editor.rs | 8 +- .../src/services/row/row_builder.rs | 9 +- .../tests/grid/block_test/row_test.rs | 17 ++-- .../tests/grid/block_test/script.rs | 22 +---- .../flowy-grid/tests/grid/block_test/util.rs | 16 +-- .../flowy-grid/tests/grid/grid_editor.rs | 98 +++++++++++++++---- .../src/revision/grid_rev.rs | 4 +- .../src/client_grid/grid_builder.rs | 14 ++- 12 files changed, 124 insertions(+), 68 deletions(-) diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/mod.rs index 61013fac08..ebe5d1a6a8 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/mod.rs @@ -1,3 +1,4 @@ +#![allow(clippy::module_inception)] mod checkbox_tests; mod checkbox_type_option; mod checkbox_type_option_entities; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/mod.rs index 8ca49fdd15..395f2c9104 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/mod.rs @@ -1,3 +1,4 @@ +#![allow(clippy::module_inception)] mod date_tests; mod date_type_option; mod date_type_option_entities; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/mod.rs index 3c1d2287cd..c7e518f103 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/mod.rs @@ -1,2 +1,3 @@ +#![allow(clippy::module_inception)] mod text_type_option; pub use text_type_option::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/mod.rs index e3b2ae16bd..8f6cb884df 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/mod.rs @@ -1,3 +1,4 @@ +#![allow(clippy::module_inception)] mod url_tests; mod url_type_option; mod url_type_option_entities; diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs index 70804f1404..413f4812de 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -270,14 +270,10 @@ impl GridRevisionEditor { pub async fn create_row(&self, start_row_id: Option) -> FlowyResult { let field_revs = self.grid_pad.read().await.get_field_revs(None)?; - let field_revs_ref = field_revs - .iter() - .map(|field_rev| field_rev.as_ref()) - .collect::>(); let block_id = self.block_id().await?; // insert empty row below the row whose id is upper_row_id - let row_rev = RowRevisionBuilder::new(&field_revs_ref).build(&block_id); + let row_rev = RowRevisionBuilder::new(&field_revs).build(&block_id); let row_order = RowInfo::from(&row_rev); // insert the row @@ -556,7 +552,7 @@ impl GridRevisionEditor { drop(grid_pad); Ok(BuildGridContext { - field_revs: duplicated_fields, + field_revs: duplicated_fields.into_iter().map(Arc::new).collect(), blocks: duplicated_blocks, blocks_meta_data, }) diff --git a/frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs b/frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs index 6ccb553fc1..43153f1311 100644 --- a/frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs +++ b/frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs @@ -4,18 +4,19 @@ use flowy_error::{FlowyError, FlowyResult}; use flowy_grid_data_model::revision::{gen_row_id, CellRevision, FieldRevision, RowRevision, DEFAULT_ROW_HEIGHT}; use indexmap::IndexMap; use std::collections::HashMap; +use std::sync::Arc; pub struct RowRevisionBuilder<'a> { - field_rev_map: HashMap<&'a String, &'a FieldRevision>, + field_rev_map: HashMap<&'a String, Arc>, payload: CreateRowRevisionPayload, } impl<'a> RowRevisionBuilder<'a> { - pub fn new(fields: &'a [&'a FieldRevision]) -> Self { + pub fn new(fields: &'a [Arc]) -> Self { let field_rev_map = fields .iter() - .map(|field| (&field.id, *field)) - .collect::>(); + .map(|field| (&field.id, field.clone())) + .collect::>>(); let payload = CreateRowRevisionPayload { row_id: gen_row_id(), diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs index 02e7ccc050..6dafb2973f 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs @@ -79,8 +79,8 @@ async fn grid_row_add_cells_test() { |options| options, &vec![GOOGLE, FACEBOOK, TWITTER].join(SELECTION_IDS_SEPARATOR), ); - - test.run_scripts(builder.build()).await; + let scripts = builder.build(); + test.run_scripts(scripts).await; } #[tokio::test] @@ -89,7 +89,8 @@ async fn grid_row_insert_number_test() { for (val, expected) in &[("1647251762", "2022/03/14"), ("2022/03/14", ""), ("", "")] { let mut builder = CreateRowScriptBuilder::new(&test); builder.insert(FieldType::DateTime, val, expected); - test.run_scripts(builder.build()).await; + let scripts = builder.build(); + test.run_scripts(scripts).await; } } @@ -105,7 +106,8 @@ async fn grid_row_insert_date_test() { ] { let mut builder = CreateRowScriptBuilder::new(&test); builder.insert(FieldType::Number, val, expected); - test.run_scripts(builder.build()).await; + let scripts = builder.build(); + test.run_scripts(scripts).await; } } #[tokio::test] @@ -113,7 +115,8 @@ async fn grid_row_insert_single_select_test() { let mut test = GridRowTest::new().await; let mut builder = CreateRowScriptBuilder::new(&test); builder.insert_single_select_cell(|mut options| options.pop().unwrap(), PAUSED); - test.run_scripts(builder.build()).await; + let scripts = builder.build(); + test.run_scripts(scripts).await; } #[tokio::test] @@ -127,6 +130,6 @@ async fn grid_row_insert_multi_select_test() { }, &vec![FACEBOOK, TWITTER].join(SELECTION_IDS_SEPARATOR), ); - - test.run_scripts(builder.build()).await; + let scripts = builder.build(); + test.run_scripts(scripts).await; } diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs index 366d1e6c80..015dcfee88 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs @@ -2,13 +2,9 @@ use crate::grid::block_test::script::RowScript::{AssertCell, CreateRow}; use crate::grid::block_test::util::GridRowTestBuilder; use crate::grid::grid_editor::GridEditorTest; use flowy_grid::entities::{CellIdentifier, FieldType, RowInfo}; -use flowy_grid::services::field::{ - CheckboxCellDataParser, DateCellDataParser, MultiSelectTypeOption, NumberCellDataParser, NumberTypeOption, - SelectOption, SelectOptionCellDataParser, SelectOptionIdsParser, SingleSelectTypeOption, TextCellDataParser, - URLCellDataParser, SELECTION_IDS_SEPARATOR, -}; +use flowy_grid::services::field::*; use flowy_grid_data_model::revision::{ - FieldRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision, + GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision, }; use std::collections::HashMap; use std::sync::Arc; @@ -74,19 +70,7 @@ impl GridRowTest { } pub fn row_builder(&self) -> GridRowTestBuilder { - let field_revs_ref = self - .field_revs - .iter() - .map(|field_rev| field_rev.as_ref()) - .collect::>(); - GridRowTestBuilder::new( - self.block_id(), - &self - .field_revs - .iter() - .map(|field_rev| field_rev.as_ref()) - .collect::>(), - ) + GridRowTestBuilder::new(self.block_id(), &self.field_revs) } pub async fn run_script(&mut self, script: RowScript) { diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs index ca1e45465e..7aade54a08 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs @@ -1,4 +1,5 @@ use flowy_grid::entities::FieldType; +use std::sync::Arc; use flowy_grid::services::field::{ DateCellChangeset, MultiSelectTypeOption, SelectOption, SingleSelectTypeOption, SELECTION_IDS_SEPARATOR, @@ -10,12 +11,12 @@ use strum::EnumCount; pub struct GridRowTestBuilder<'a> { block_id: String, - field_revs: &'a [&'a FieldRevision], + field_revs: &'a [Arc], inner_builder: RowRevisionBuilder<'a>, } impl<'a> GridRowTestBuilder<'a> { - pub fn new(block_id: &str, field_revs: &'a [&'a FieldRevision]) -> Self { + pub fn new(block_id: &str, field_revs: &'a [Arc]) -> Self { assert_eq!(field_revs.len(), FieldType::COUNT); let inner_builder = RowRevisionBuilder::new(field_revs); Self { @@ -68,16 +69,15 @@ impl<'a> GridRowTestBuilder<'a> { url_field.id.clone() } - #[allow(dead_code)] pub fn insert_single_select_cell(&mut self, f: F) -> String where - F: Fn(&Vec) -> &SelectOption, + F: Fn(Vec) -> SelectOption, { let single_select_field = self.field_rev_with_type(&FieldType::SingleSelect); let type_option = SingleSelectTypeOption::from(&single_select_field); - let option = f(&type_option.options); + let option = f(type_option.options); self.inner_builder - .insert_select_option_cell(&single_select_field.id, option.id.clone()) + .insert_select_option_cell(&single_select_field.id, option.id) .unwrap(); single_select_field.id.clone() @@ -85,11 +85,11 @@ impl<'a> GridRowTestBuilder<'a> { pub fn insert_multi_select_cell(&mut self, f: F) -> String where - F: Fn(&Vec) -> &Vec, + F: Fn(Vec) -> Vec, { let multi_select_field = self.field_rev_with_type(&FieldType::MultiSelect); let type_option = MultiSelectTypeOption::from(&multi_select_field); - let options = f(&type_option.options); + let options = f(type_option.options); let ops_ids = options .iter() .map(|option| option.id.clone()) diff --git a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs index 8d6a2043d0..2070bd6fce 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs @@ -1,6 +1,7 @@ #![allow(clippy::all)] #![allow(dead_code)] #![allow(unused_imports)] +use crate::grid::block_test::util::GridRowTestBuilder; use bytes::Bytes; use flowy_grid::entities::*; use flowy_grid::services::field::SelectOption; @@ -158,7 +159,7 @@ fn make_test_grid() -> BuildGridContext { FieldType::Checkbox => { // Checkbox let checkbox = CheckboxTypeOptionBuilder::default(); - let checkbox_field = FieldBuilder::new(checkbox).name("is done").visibility(true).build(); + let checkbox_field = FieldBuilder::new(checkbox).name("is urgent").visibility(true).build(); grid_builder.add_field(checkbox_field); } FieldType::URL => { @@ -171,24 +172,87 @@ fn make_test_grid() -> BuildGridContext { } // We have many assumptions base on the number of the rows, so do not change the number of the loop. - for _i in 0..10 { - for field_type in FieldType::iter() { - let field_type: FieldType = field_type; - // let mut row_builder = RowRevisionBuilder::new() - match field_type { - FieldType::RichText => {} - FieldType::Number => {} - FieldType::DateTime => {} - FieldType::SingleSelect => {} - FieldType::MultiSelect => {} - FieldType::Checkbox => {} - FieldType::URL => {} + for i in 0..5 { + let block_id = grid_builder.block_id().to_owned(); + let field_revs = grid_builder.field_revs(); + let mut row_builder = GridRowTestBuilder::new(&block_id, field_revs); + match i { + 0 => { + for field_type in FieldType::iter() { + match field_type { + FieldType::RichText => row_builder.insert_text_cell("A"), + FieldType::Number => row_builder.insert_number_cell("1"), + FieldType::DateTime => row_builder.insert_date_cell("1647251762"), + FieldType::SingleSelect => { + row_builder.insert_single_select_cell(|mut options| options.remove(0)) + } + FieldType::Checkbox => row_builder.insert_checkbox_cell("true"), + _ => "".to_owned(), + }; + } } + 1 => { + for field_type in FieldType::iter() { + match field_type { + FieldType::RichText => row_builder.insert_text_cell("B"), + FieldType::Number => row_builder.insert_number_cell("2"), + FieldType::DateTime => row_builder.insert_date_cell("1647251762"), + FieldType::SingleSelect => { + row_builder.insert_single_select_cell(|mut options| options.remove(0)) + } + FieldType::Checkbox => row_builder.insert_checkbox_cell("true"), + _ => "".to_owned(), + }; + } + } + 2 => { + for field_type in FieldType::iter() { + match field_type { + FieldType::RichText => row_builder.insert_text_cell("C"), + FieldType::Number => row_builder.insert_number_cell("3"), + FieldType::DateTime => row_builder.insert_date_cell("1647251762"), + FieldType::SingleSelect => { + row_builder.insert_single_select_cell(|mut options| options.remove(1)) + } + FieldType::Checkbox => row_builder.insert_checkbox_cell("false"), + _ => "".to_owned(), + }; + } + } + 3 => { + for field_type in FieldType::iter() { + match field_type { + FieldType::RichText => row_builder.insert_text_cell("D"), + FieldType::Number => row_builder.insert_number_cell("4"), + FieldType::DateTime => row_builder.insert_date_cell("1647251762"), + FieldType::SingleSelect => { + row_builder.insert_single_select_cell(|mut options| options.remove(1)) + } + FieldType::Checkbox => row_builder.insert_checkbox_cell("false"), + _ => "".to_owned(), + }; + } + } + 4 => { + for field_type in FieldType::iter() { + match field_type { + FieldType::RichText => row_builder.insert_text_cell("E"), + FieldType::Number => row_builder.insert_number_cell("5"), + FieldType::DateTime => row_builder.insert_date_cell("1647251762"), + FieldType::SingleSelect => { + row_builder.insert_single_select_cell(|mut options| options.remove(2)) + } + + FieldType::Checkbox => row_builder.insert_checkbox_cell("false"), + _ => "".to_owned(), + }; + } + } + _ => {} } + + let row_rev = row_builder.build(); + grid_builder.add_row(row_rev); } - // assert_eq!(row_revs.len(), 10); - // .add_empty_row() - // .add_empty_row() - // .add_empty_row() grid_builder.build() } diff --git a/shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs b/shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs index edceb25d9c..671f71563f 100644 --- a/shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs +++ b/shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs @@ -53,7 +53,7 @@ impl GridRevision { pub fn from_build_context(grid_id: &str, context: BuildGridContext) -> Self { Self { grid_id: grid_id.to_owned(), - fields: context.field_revs.into_iter().map(Arc::new).collect(), + fields: context.field_revs, blocks: context.blocks.into_iter().map(Arc::new).collect(), setting: Default::default(), } @@ -245,7 +245,7 @@ impl CellRevision { #[derive(Clone, Default, Deserialize, Serialize)] pub struct BuildGridContext { - pub field_revs: Vec, + pub field_revs: Vec>, pub blocks: Vec, pub blocks_meta_data: Vec, } diff --git a/shared-lib/flowy-sync/src/client_grid/grid_builder.rs b/shared-lib/flowy-sync/src/client_grid/grid_builder.rs index b0cc2b7bb0..ea5d5a9332 100644 --- a/shared-lib/flowy-sync/src/client_grid/grid_builder.rs +++ b/shared-lib/flowy-sync/src/client_grid/grid_builder.rs @@ -30,7 +30,7 @@ impl GridBuilder { Self::default() } pub fn add_field(&mut self, field: FieldRevision) { - self.build_context.field_revs.push(field); + self.build_context.field_revs.push(Arc::new(field)); } pub fn add_row(&mut self, row_rev: RowRevision) { @@ -41,13 +41,17 @@ impl GridBuilder { } pub fn add_empty_row(&mut self) { - let row = RowRevision::new(&self.build_context.blocks.first().unwrap().block_id); + let row = RowRevision::new(self.block_id()); self.add_row(row); } - // pub fn field_revs(&self) -> Vec { - // self.build_context.field_revs - // } + pub fn field_revs(&self) -> &Vec> { + &self.build_context.field_revs + } + + pub fn block_id(&self) -> &str { + &self.build_context.blocks.first().unwrap().block_id + } pub fn build(self) -> BuildGridContext { self.build_context From 5ea3213c4e12123999ef14c96f5087eb6f9c667e Mon Sep 17 00:00:00 2001 From: appflowy Date: Sun, 17 Jul 2022 12:57:25 +0800 Subject: [PATCH 7/7] chore: fix tests --- .../checkbox_type_option/checkbox_tests.rs | 2 +- .../checkbox_type_option_entities.rs | 2 +- .../src/services/filter/impls/checkbox_filter.rs | 5 +++-- .../flowy-grid/tests/grid/block_test/row_test.rs | 14 +++++++------- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_tests.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_tests.rs index 9e041cc415..1a60a0a5a8 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_tests.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_tests.rs @@ -25,6 +25,6 @@ mod tests { assert_eq!(decode_any_cell_data(data, &field_rev,).to_string(), NO); let data = apply_cell_data_changeset("12", None, &field_rev).unwrap(); - assert_eq!(decode_any_cell_data(data, &field_rev,).to_string(), NO); + assert_eq!(decode_any_cell_data(data, &field_rev,).to_string(), ""); } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs index 54cde9b310..233c481eb6 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs @@ -6,7 +6,7 @@ use std::str::FromStr; pub const YES: &str = "Yes"; pub const NO: &str = "No"; -pub struct CheckboxCellData(pub String); +pub struct CheckboxCellData(String); impl CheckboxCellData { pub fn is_check(&self) -> bool { diff --git a/frontend/rust-lib/flowy-grid/src/services/filter/impls/checkbox_filter.rs b/frontend/rust-lib/flowy-grid/src/services/filter/impls/checkbox_filter.rs index ca67d7aeaf..24e21bbeb7 100644 --- a/frontend/rust-lib/flowy-grid/src/services/filter/impls/checkbox_filter.rs +++ b/frontend/rust-lib/flowy-grid/src/services/filter/impls/checkbox_filter.rs @@ -28,6 +28,7 @@ impl CellFilterOperation for CheckboxTypeOption { mod tests { use crate::entities::{CheckboxCondition, GridCheckboxFilter}; use crate::services::field::CheckboxCellData; + use std::str::FromStr; #[test] fn checkbox_filter_is_check_test() { @@ -35,7 +36,7 @@ mod tests { condition: CheckboxCondition::IsChecked, }; for (value, visible) in [("true", true), ("yes", true), ("false", false), ("no", false)] { - let data = CheckboxCellData(value.to_owned()); + let data = CheckboxCellData::from_str(value).unwrap(); assert_eq!(checkbox_filter.is_visible(&data), visible); } } @@ -46,7 +47,7 @@ mod tests { condition: CheckboxCondition::IsUnChecked, }; for (value, visible) in [("false", true), ("no", true), ("true", false), ("yes", false)] { - let data = CheckboxCellData(value.to_owned()); + let data = CheckboxCellData::from_str(value).unwrap(); assert_eq!(checkbox_filter.is_visible(&data), visible); } } diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs index 6dafb2973f..7b64a44b48 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs @@ -9,13 +9,13 @@ use flowy_grid_data_model::revision::RowMetaChangeset; async fn grid_create_row_count_test() { let mut test = GridRowTest::new().await; let scripts = vec![ - AssertRowCount(3), + AssertRowCount(5), CreateEmptyRow, CreateEmptyRow, CreateRow { row_rev: test.row_builder().build(), }, - AssertRowCount(6), + AssertRowCount(8), ]; test.run_scripts(scripts).await; } @@ -31,11 +31,11 @@ async fn grid_update_row() { cell_by_field_id: Default::default(), }; - let scripts = vec![AssertRowCount(3), CreateRow { row_rev }, UpdateRow { changeset }]; + let scripts = vec![AssertRowCount(5), CreateRow { row_rev }, UpdateRow { changeset }]; test.run_scripts(scripts).await; let expected_row = test.last_row().unwrap(); - let scripts = vec![AssertRow { expected_row }, AssertRowCount(4)]; + let scripts = vec![AssertRow { expected_row }, AssertRowCount(6)]; test.run_scripts(scripts).await; } @@ -46,19 +46,19 @@ async fn grid_delete_row() { let row_2 = test.row_builder().build(); let row_ids = vec![row_1.id.clone(), row_2.id.clone()]; let scripts = vec![ - AssertRowCount(3), + AssertRowCount(5), CreateRow { row_rev: row_1 }, CreateRow { row_rev: row_2 }, AssertBlockCount(1), AssertBlock { block_index: 0, - row_count: 5, + row_count: 7, start_row_index: 0, }, DeleteRows { row_ids }, AssertBlock { block_index: 0, - row_count: 3, + row_count: 5, start_row_index: 0, }, ];