refactor: use BoxAny for dynamically-typed cell changesets (#4655)

* refactor: use BoxAny for dynamically-typed cell changesets

* fix: rust-lib tests and clippy
This commit is contained in:
Richard Shiue 2024-02-22 07:12:52 +08:00 committed by GitHub
parent c500cd1287
commit a4a2a4088b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 161 additions and 268 deletions

View File

@ -26,7 +26,7 @@ final class DateCellBackendService {
String? endTime,
String? reminderId,
}) {
final payload = DateChangesetPB.create()
final payload = DateCellChangesetPB.create()
..cellId = cellId
..includeTime = includeTime
..isRange = isRange;
@ -53,7 +53,7 @@ final class DateCellBackendService {
}
Future<Either<Unit, FlowyError>> clear() {
final payload = DateChangesetPB.create()
final payload = DateCellChangesetPB.create()
..cellId = cellId
..clearFlag = true;

View File

@ -3,7 +3,7 @@ import {
CellChangesetPB,
SelectOptionCellChangesetPB,
ChecklistCellDataChangesetPB,
DateChangesetPB,
DateCellChangesetPB,
FieldType,
} from '../../../../services/backend';
import {
@ -115,7 +115,7 @@ export async function updateDateCell(
isRange?: boolean;
}
): Promise<void> {
const payload = DateChangesetPB.fromObject({
const payload = DateCellChangesetPB.fromObject({
cell_id: {
view_id: viewId,
row_id: rowId,

View File

@ -298,7 +298,7 @@ impl EventIntegrationTest {
.error()
}
pub async fn update_date_cell(&self, changeset: DateChangesetPB) -> Option<FlowyError> {
pub async fn update_date_cell(&self, changeset: DateCellChangesetPB) -> Option<FlowyError> {
EventBuilder::new(self.clone())
.event(DatabaseEvent::UpdateDateCell)
.payload(changeset)

View File

@ -6,8 +6,8 @@ use event_integration::event_builder::EventBuilder;
use event_integration::EventIntegrationTest;
use flowy_database2::entities::{
CellChangesetPB, CellIdPB, CheckboxCellDataPB, ChecklistCellDataChangesetPB, DatabaseLayoutPB,
DatabaseSettingChangesetPB, DatabaseViewIdPB, DateChangesetPB, FieldType, OrderObjectPositionPB,
SelectOptionCellDataPB, UpdateRowMetaChangesetPB,
DatabaseSettingChangesetPB, DatabaseViewIdPB, DateCellChangesetPB, FieldType,
OrderObjectPositionPB, SelectOptionCellDataPB, UpdateRowMetaChangesetPB,
};
use lib_infra::util::timestamp;
@ -529,7 +529,7 @@ async fn update_date_cell_event_test() {
// Insert data into the date cell of the first row.
let timestamp = 1686300557;
let error = test
.update_date_cell(DateChangesetPB {
.update_date_cell(DateCellChangesetPB {
cell_id: cell_path,
date: Some(timestamp),
..Default::default()
@ -565,7 +565,7 @@ async fn update_date_cell_event_with_empty_time_str_test() {
// Insert empty timestamp string
let error = test
.update_date_cell(DateChangesetPB {
.update_date_cell(DateCellChangesetPB {
cell_id: cell_path,
date: None,
..Default::default()
@ -763,7 +763,7 @@ async fn create_calendar_event_test() {
// Insert data into the date cell of the first row.
let error = test
.update_date_cell(DateChangesetPB {
.update_date_cell(DateCellChangesetPB {
cell_id: CellIdPB {
view_id: calendar_view.id.clone(),
field_id: date_field.id.clone(),

View File

@ -38,7 +38,7 @@ pub struct DateCellDataPB {
}
#[derive(Clone, Debug, Default, ProtoBuf)]
pub struct DateChangesetPB {
pub struct DateCellChangesetPB {
#[pb(index = 1)]
pub cell_id: CellIdPB,

View File

@ -2,6 +2,7 @@ use std::sync::{Arc, Weak};
use collab_database::database::gen_row_id;
use collab_database::rows::RowId;
use lib_infra::box_any::BoxAny;
use tokio::sync::oneshot;
use flowy_error::{FlowyError, FlowyResult};
@ -11,9 +12,8 @@ use lib_infra::util::timestamp;
use crate::entities::*;
use crate::manager::DatabaseManager;
use crate::services::cell::CellBuilder;
use crate::services::field::checklist_type_option::ChecklistCellChangeset;
use crate::services::field::{
type_option_data_from_pb, DateCellChangeset, SelectOptionCellChangeset,
type_option_data_from_pb, ChecklistCellChangeset, DateCellChangeset, SelectOptionCellChangeset,
};
use crate::services::field_settings::FieldSettingsChangesetParams;
use crate::services::group::GroupChangeset;
@ -454,7 +454,7 @@ pub(crate) async fn update_cell_handler(
&params.view_id,
RowId::from(params.row_id),
&params.field_id,
params.cell_changeset.clone(),
BoxAny::new(params.cell_changeset),
)
.await?;
Ok(())
@ -551,7 +551,7 @@ pub(crate) async fn update_select_option_cell_handler(
&params.cell_identifier.view_id,
params.cell_identifier.row_id,
&params.cell_identifier.field_id,
changeset,
BoxAny::new(changeset),
)
.await?;
Ok(())
@ -572,14 +572,19 @@ pub(crate) async fn update_checklist_cell_handler(
update_options: params.update_options,
};
database_editor
.update_cell_with_changeset(&params.view_id, params.row_id, &params.field_id, changeset)
.update_cell_with_changeset(
&params.view_id,
params.row_id,
&params.field_id,
BoxAny::new(changeset),
)
.await?;
Ok(())
}
#[tracing::instrument(level = "trace", skip_all, err)]
pub(crate) async fn update_date_cell_handler(
data: AFPluginData<DateChangesetPB>,
data: AFPluginData<DateCellChangesetPB>,
manager: AFPluginState<Weak<DatabaseManager>>,
) -> Result<(), FlowyError> {
let manager = upgrade_manager(manager)?;
@ -602,7 +607,7 @@ pub(crate) async fn update_date_cell_handler(
&cell_id.view_id,
cell_id.row_id,
&cell_id.field_id,
cell_changeset,
BoxAny::new(cell_changeset),
)
.await?;
Ok(())
@ -831,7 +836,7 @@ pub(crate) async fn move_calendar_event_handler(
&cell_id.view_id,
cell_id.row_id,
&cell_id.field_id,
cell_changeset,
BoxAny::new(cell_changeset),
)
.await?;
Ok(())

View File

@ -256,10 +256,10 @@ pub enum DatabaseEvent {
#[event(input = "ChecklistCellDataChangesetPB")]
UpdateChecklistCell = 73,
/// [UpdateDateCell] event is used to update a date cell's data. [DateChangesetPB]
/// [UpdateDateCell] event is used to update a date cell's data. [DateCellChangesetPB]
/// contains the date and the time string. It can be cast to [CellChangesetPB] that
/// will be used by the `update_cell` function.
#[event(input = "DateChangesetPB")]
#[event(input = "DateCellChangesetPB")]
UpdateDateCell = 80,
/// [SetGroupByField] event is used to create a new grouping in a database

View File

@ -1,14 +1,13 @@
use std::collections::HashMap;
use std::fmt::Debug;
use collab_database::fields::Field;
use collab_database::rows::{get_field_type_from_cell, Cell, Cells};
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_error::{FlowyError, FlowyResult};
use lib_infra::box_any::BoxAny;
use crate::entities::{CheckboxCellDataPB, FieldType};
use crate::services::cell::{CellCache, CellProtobufBlob};
use crate::services::field::checklist_type_option::ChecklistCellChangeset;
use crate::services::field::*;
use crate::services::group::make_no_status_group;
@ -48,8 +47,9 @@ pub trait CellDataDecoder: TypeOption {
pub trait CellDataChangeset: TypeOption {
/// The changeset is able to parse into the concrete data struct if `TypeOption::CellChangeset`
/// implements the `FromCellChangesetString` trait.
/// For example,the SelectOptionCellChangeset,DateCellChangeset. etc.
/// implements the `FromCellChangesetString` trait e.g.
/// SelectOptionCellChangeset, DateCellChangeset
///
/// # Arguments
///
/// * `changeset`: the cell changeset that represents the changes of the cell.
@ -67,13 +67,12 @@ pub trait CellDataChangeset: TypeOption {
/// FieldType::SingleSelect => SelectOptionChangeset
///
/// cell_rev: It will be None if the cell does not contain any data.
pub fn apply_cell_changeset<C: ToCellChangeset>(
changeset: C,
pub fn apply_cell_changeset(
changeset: BoxAny,
cell: Option<Cell>,
field: &Field,
cell_data_cache: Option<CellCache>,
) -> Result<Cell, FlowyError> {
let changeset = changeset.to_cell_changeset_str();
let field_type = FieldType::from(field.field_type);
match TypeOptionCellExt::new_with_cell_data_cache(field, cell_data_cache)
.get_type_option_cell_data_handler(&field_type)
@ -155,6 +154,7 @@ pub fn try_decode_cell_to_cell_data<T: Default + 'static>(
.ok()?
.unbox_or_none::<T>()
}
/// Returns a string that represents the current field_type's cell data.
/// For example, The string of the Multi-Select cell will be a list of the option's name
/// separated by a comma.
@ -182,11 +182,11 @@ pub fn stringify_cell_data(
}
pub fn insert_text_cell(s: String, field: &Field) -> Cell {
apply_cell_changeset(s, None, field, None).unwrap()
apply_cell_changeset(BoxAny::new(s), None, field, None).unwrap()
}
pub fn insert_number_cell(num: i64, field: &Field) -> Cell {
apply_cell_changeset(num.to_string(), None, field, None).unwrap()
apply_cell_changeset(BoxAny::new(num.to_string()), None, field, None).unwrap()
}
pub fn insert_url_cell(url: String, field: &Field) -> Cell {
@ -200,7 +200,7 @@ pub fn insert_url_cell(url: String, field: &Field) -> Cell {
_ => url,
};
apply_cell_changeset(url, None, field, None).unwrap()
apply_cell_changeset(BoxAny::new(url), None, field, None).unwrap()
}
pub fn insert_checkbox_cell(is_checked: bool, field: &Field) -> Cell {
@ -209,38 +209,40 @@ pub fn insert_checkbox_cell(is_checked: bool, field: &Field) -> Cell {
} else {
UNCHECK.to_string()
};
apply_cell_changeset(s, None, field, None).unwrap()
apply_cell_changeset(BoxAny::new(s), None, field, None).unwrap()
}
pub fn insert_date_cell(timestamp: i64, include_time: Option<bool>, field: &Field) -> Cell {
let cell_data = serde_json::to_string(&DateCellChangeset {
pub fn insert_date_cell(
timestamp: i64,
time: Option<String>,
include_time: Option<bool>,
field: &Field,
) -> Cell {
let cell_data = DateCellChangeset {
date: Some(timestamp),
time,
include_time,
..Default::default()
})
.unwrap();
apply_cell_changeset(cell_data, None, field, None).unwrap()
};
apply_cell_changeset(BoxAny::new(cell_data), None, field, None).unwrap()
}
pub fn insert_select_option_cell(option_ids: Vec<String>, field: &Field) -> Cell {
let changeset =
SelectOptionCellChangeset::from_insert_options(option_ids).to_cell_changeset_str();
apply_cell_changeset(changeset, None, field, None).unwrap()
let changeset = SelectOptionCellChangeset::from_insert_options(option_ids);
apply_cell_changeset(BoxAny::new(changeset), None, field, None).unwrap()
}
pub fn insert_checklist_cell(insert_options: Vec<String>, field: &Field) -> Cell {
let changeset = ChecklistCellChangeset {
insert_options,
..Default::default()
}
.to_cell_changeset_str();
apply_cell_changeset(changeset, None, field, None).unwrap()
};
apply_cell_changeset(BoxAny::new(changeset), None, field, None).unwrap()
}
pub fn delete_select_option_cell(option_ids: Vec<String>, field: &Field) -> Cell {
let changeset =
SelectOptionCellChangeset::from_delete_options(option_ids).to_cell_changeset_str();
apply_cell_changeset(changeset, None, field, None).unwrap()
let changeset = SelectOptionCellChangeset::from_delete_options(option_ids);
apply_cell_changeset(BoxAny::new(changeset), None, field, None).unwrap()
}
/// Deserialize the String into cell specific data type.
@ -250,59 +252,6 @@ pub trait FromCellString {
Self: Sized;
}
/// 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<Self>
where
Self: Sized;
}
impl FromCellChangeset for String {
fn from_changeset(changeset: String) -> FlowyResult<Self>
where
Self: Sized,
{
Ok(changeset)
}
}
pub trait ToCellChangeset: Debug {
fn to_cell_changeset_str(&self) -> String;
}
impl ToCellChangeset for String {
fn to_cell_changeset_str(&self) -> String {
self.clone()
}
}
pub struct AnyCellChangeset<T>(pub Option<T>);
impl<T> AnyCellChangeset<T> {
pub fn try_into_inner(self) -> FlowyResult<T> {
match self.0 {
None => Err(ErrorCode::InvalidParams.into()),
Some(data) => Ok(data),
}
}
}
impl<T, C: ToString> std::convert::From<C> for AnyCellChangeset<T>
where
T: FromCellChangeset,
{
fn from(changeset: C) -> Self {
match T::from_changeset(changeset.to_string()) {
Ok(data) => AnyCellChangeset(Some(data)),
Err(e) => {
tracing::error!("Deserialize CellDataChangeset failed: {}", e);
AnyCellChangeset(None)
},
}
}
}
pub struct CellBuilder<'a> {
cells: Cells,
field_maps: HashMap<String, &'a Field>,
@ -331,7 +280,10 @@ impl<'a> CellBuilder<'a> {
},
FieldType::DateTime => {
if let Ok(timestamp) = cell_str.parse::<i64>() {
cells.insert(field_id, insert_date_cell(timestamp, Some(false), field));
cells.insert(
field_id,
insert_date_cell(timestamp, None, Some(false), field),
);
}
},
FieldType::LastEditedTime | FieldType::CreatedTime => {
@ -410,13 +362,19 @@ impl<'a> CellBuilder<'a> {
}
}
pub fn insert_date_cell(&mut self, field_id: &str, timestamp: i64) {
pub fn insert_date_cell(
&mut self,
field_id: &str,
timestamp: i64,
time: Option<String>,
include_time: Option<bool>,
) {
match self.field_maps.get(&field_id.to_owned()) {
None => tracing::warn!("Can't find the date field with id: {}", field_id),
Some(field) => {
self.cells.insert(
field_id.to_owned(),
insert_date_cell(timestamp, Some(false), field),
insert_date_cell(timestamp, time, include_time, field),
);
},
}

View File

@ -6,6 +6,7 @@ use collab_database::fields::{Field, TypeOptionData};
use collab_database::rows::{Cell, Cells, CreateRowParams, Row, RowCell, RowDetail, RowId};
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting, OrderObjectPosition};
use futures::StreamExt;
use lib_infra::box_any::BoxAny;
use tokio::sync::{broadcast, RwLock};
use tracing::{event, warn};
@ -17,17 +18,16 @@ use lib_infra::priority_task::TaskDispatcher;
use crate::entities::*;
use crate::notification::{send_notification, DatabaseNotification};
use crate::services::calculations::Calculation;
use crate::services::cell::{apply_cell_changeset, get_cell_protobuf, CellCache, ToCellChangeset};
use crate::services::cell::{apply_cell_changeset, get_cell_protobuf, CellCache};
use crate::services::database::util::database_view_setting_pb_from_view;
use crate::services::database::UpdatedRow;
use crate::services::database_view::{
DatabaseViewChanged, DatabaseViewEditor, DatabaseViewOperation, DatabaseViews, EditorByViewId,
};
use crate::services::field::checklist_type_option::ChecklistCellChangeset;
use crate::services::field::{
default_type_option_data_from_type, select_type_option_from_field, transform_type_option,
type_option_data_from_pb, SelectOptionCellChangeset, SelectOptionIds, TimestampCellData,
TypeOptionCellDataHandler, TypeOptionCellExt,
type_option_data_from_pb, ChecklistCellChangeset, SelectOptionCellChangeset, SelectOptionIds,
TimestampCellData, TypeOptionCellDataHandler, TypeOptionCellExt,
};
use crate::services::field_settings::{
default_field_settings_by_layout_map, FieldSettings, FieldSettingsChangesetParams,
@ -105,6 +105,7 @@ impl DatabaseEditor {
)
.await?,
);
Ok(Self {
database,
cell_cache,
@ -730,16 +731,13 @@ impl DatabaseEditor {
}
}
pub async fn update_cell_with_changeset<T>(
pub async fn update_cell_with_changeset(
&self,
view_id: &str,
row_id: RowId,
field_id: &str,
cell_changeset: T,
) -> FlowyResult<()>
where
T: ToCellChangeset,
{
cell_changeset: BoxAny,
) -> FlowyResult<()> {
let (field, cell) = {
let database = self.database.lock();
let field = match database.fields.get_field(field_id) {
@ -872,7 +870,7 @@ impl DatabaseEditor {
// Insert the options into the cell
self
.update_cell_with_changeset(view_id, row_id, field_id, cell_changeset)
.update_cell_with_changeset(view_id, row_id, field_id, BoxAny::new(cell_changeset))
.await?;
Ok(())
}
@ -911,7 +909,7 @@ impl DatabaseEditor {
.await?;
self
.update_cell_with_changeset(view_id, row_id, field_id, cell_changeset)
.update_cell_with_changeset(view_id, row_id, field_id, BoxAny::new(cell_changeset))
.await?;
Ok(())
}
@ -953,7 +951,7 @@ impl DatabaseEditor {
debug_assert!(FieldType::from(field.field_type).is_checklist());
self
.update_cell_with_changeset(view_id, row_id, field_id, changeset)
.update_cell_with_changeset(view_id, row_id, field_id, BoxAny::new(changeset))
.await?;
Ok(())
}

View File

@ -1,9 +1,7 @@
use crate::entities::FieldType;
use crate::services::cell::{FromCellChangeset, ToCellChangeset};
use crate::services::field::{SelectOption, TypeOptionCellData, CELL_DATA};
use collab::core::any_map::AnyMapExtension;
use collab_database::rows::{new_cell_builder, Cell};
use flowy_error::{internal_error, FlowyResult};
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
@ -76,7 +74,7 @@ impl From<ChecklistCellData> for Cell {
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[derive(Debug, Clone, Default)]
pub struct ChecklistCellChangeset {
/// List of option names that will be inserted
pub insert_options: Vec<String>,
@ -85,21 +83,6 @@ pub struct ChecklistCellChangeset {
pub update_options: Vec<SelectOption>,
}
impl FromCellChangeset for ChecklistCellChangeset {
fn from_changeset(changeset: String) -> FlowyResult<Self>
where
Self: Sized,
{
serde_json::from_str::<ChecklistCellChangeset>(&changeset).map_err(internal_error)
}
}
impl ToCellChangeset for ChecklistCellChangeset {
fn to_cell_changeset_str(&self) -> String {
serde_json::to_string(self).unwrap_or_default()
}
}
#[cfg(test)]
mod tests {
#[test]

View File

@ -11,12 +11,10 @@ use strum_macros::EnumIter;
use flowy_error::{internal_error, FlowyResult};
use crate::entities::{DateCellDataPB, FieldType};
use crate::services::cell::{
CellProtobufBlobParser, DecodedCellData, FromCellChangeset, FromCellString, ToCellChangeset,
};
use crate::services::cell::{CellProtobufBlobParser, DecodedCellData, FromCellString};
use crate::services::field::{TypeOptionCellData, CELL_DATA};
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[derive(Clone, Debug, Default)]
pub struct DateCellChangeset {
pub date: Option<i64>,
pub time: Option<String>,
@ -28,21 +26,6 @@ pub struct DateCellChangeset {
pub reminder_id: Option<String>,
}
impl FromCellChangeset for DateCellChangeset {
fn from_changeset(changeset: String) -> FlowyResult<Self>
where
Self: Sized,
{
serde_json::from_str::<DateCellChangeset>(&changeset).map_err(internal_error)
}
}
impl ToCellChangeset for DateCellChangeset {
fn to_cell_changeset_str(&self) -> String {
serde_json::to_string(self).unwrap_or_default()
}
}
#[derive(Default, Clone, Debug, Serialize)]
pub struct DateCellData {
pub timestamp: Option<i64>,

View File

@ -11,6 +11,7 @@ mod url_type_option;
mod util;
pub use checkbox_type_option::*;
pub use checklist_type_option::*;
pub use date_type_option::*;
pub use number_type_option::*;
pub use selection_type_option::*;

View File

@ -1,14 +1,11 @@
use bytes::Bytes;
use collab_database::fields::{Field, TypeOptionData};
use collab_database::rows::Cell;
use serde::{Deserialize, Serialize};
use flowy_error::{internal_error, ErrorCode, FlowyResult};
use crate::entities::{CheckboxCellDataPB, FieldType, SelectOptionCellDataPB};
use crate::services::cell::{
CellDataDecoder, CellProtobufBlobParser, DecodedCellData, FromCellChangeset, ToCellChangeset,
};
use crate::services::cell::{CellDataDecoder, CellProtobufBlobParser, DecodedCellData};
use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformHelper;
use crate::services::field::{
make_selected_options, MultiSelectTypeOption, SelectOption, SelectOptionCellData,
@ -231,27 +228,12 @@ impl CellProtobufBlobParser for SelectOptionCellDataParser {
}
}
#[derive(Clone, Serialize, Deserialize, Default, Debug)]
#[derive(Clone, Default, Debug)]
pub struct SelectOptionCellChangeset {
pub insert_option_ids: Vec<String>,
pub delete_option_ids: Vec<String>,
}
impl FromCellChangeset for SelectOptionCellChangeset {
fn from_changeset(changeset: String) -> FlowyResult<Self>
where
Self: Sized,
{
serde_json::from_str::<SelectOptionCellChangeset>(&changeset).map_err(internal_error)
}
}
impl ToCellChangeset for SelectOptionCellChangeset {
fn to_cell_changeset_str(&self) -> String {
serde_json::to_string(self).unwrap_or_default()
}
}
impl SelectOptionCellChangeset {
pub fn from_insert_option_id(option_id: &str) -> Self {
SelectOptionCellChangeset {

View File

@ -13,7 +13,7 @@ use crate::entities::{
MultiSelectTypeOptionPB, NumberTypeOptionPB, RichTextTypeOptionPB, SingleSelectTypeOptionPB,
TimestampTypeOptionPB, URLTypeOptionPB,
};
use crate::services::cell::{CellDataDecoder, FromCellChangeset, ToCellChangeset};
use crate::services::cell::CellDataDecoder;
use crate::services::field::checklist_type_option::ChecklistTypeOption;
use crate::services::field::{
CheckboxTypeOption, DateTypeOption, MultiSelectTypeOption, NumberTypeOption, RichTextTypeOption,
@ -43,11 +43,10 @@ pub trait TypeOption {
+ Debug
+ 'static;
/// Represents as the corresponding field type cell changeset.
/// The changeset must implements the `FromCellChangesetString` and the `ToCellChangesetString` trait.
/// These two traits are auto implemented for `String`.
/// Represents as the corresponding field type cell changeset. Must be able
/// to be placed into a `BoxAny`.
///
type CellChangeset: FromCellChangeset + ToCellChangeset;
type CellChangeset: Send + Sync + 'static;
/// For the moment, the protobuf type only be used in the FFI of `Dart`. If the decoded cell
/// struct is just a `String`, then use the `StrCellData` as its `CellProtobufType`.

View File

@ -11,7 +11,6 @@ use lib_infra::box_any::BoxAny;
use crate::entities::FieldType;
use crate::services::cell::{
CellCache, CellDataChangeset, CellDataDecoder, CellFilterCache, CellProtobufBlob,
FromCellChangeset,
};
use crate::services::field::checklist_type_option::ChecklistTypeOption;
use crate::services::field::{
@ -26,9 +25,11 @@ pub const CELL_DATA: &str = "data";
/// Each [FieldType] has its own [TypeOptionCellDataHandler].
/// A helper trait that used to erase the `Self` of `TypeOption` trait to make it become a Object-safe trait
/// Only object-safe traits can be made into trait objects.
/// > Object-safe traits are traits with methods that follow these two rules:
/// 1.the return type is not Self.
/// 2.there are no generic types parameters.
///
/// Object-safe traits are traits with methods that follow these two rules:
///
/// 1. the return type is not Self.
/// 2. there are no generic types parameters.
///
pub trait TypeOptionCellDataHandler: Send + Sync + 'static {
fn handle_cell_str(
@ -38,10 +39,9 @@ pub trait TypeOptionCellDataHandler: Send + Sync + 'static {
field_rev: &Field,
) -> FlowyResult<CellProtobufBlob>;
// TODO(nathan): replace cell_changeset with BoxAny to get rid of the serde process.
fn handle_cell_changeset(
&self,
cell_changeset: String,
cell_changeset: BoxAny,
old_cell: Option<Cell>,
field: &Field,
) -> FlowyResult<Cell>;
@ -238,11 +238,11 @@ where
fn handle_cell_changeset(
&self,
cell_changeset: String,
cell_changeset: BoxAny,
old_cell: Option<Cell>,
field: &Field,
) -> FlowyResult<Cell> {
let changeset = <Self as TypeOption>::CellChangeset::from_changeset(cell_changeset)?;
let changeset = cell_changeset.unbox_or_error::<<Self as TypeOption>::CellChangeset>()?;
let (cell, cell_data) = self.apply_changeset(changeset, old_cell)?;
self.set_decoded_cell_data(&cell, cell_data, field);
Ok(cell)

View File

@ -243,7 +243,7 @@ impl GroupController for DateGroupController {
None => tracing::warn!("Can not find the group: {}", group_id),
Some((_, _)) => {
let date = DateTime::parse_from_str(group_id, GROUP_ID_DATE_FORMAT).unwrap();
let cell = insert_date_cell(date.timestamp(), None, field);
let cell = insert_date_cell(date.timestamp(), None, Some(false), field);
cells.insert(field.id.clone(), cell);
},
}

View File

@ -176,7 +176,7 @@ pub fn make_inserted_cell(group_id: &str, field: &Field) -> Option<Cell> {
let date =
NaiveDateTime::parse_from_str(&format!("{} 00:00:00", group_id), "%Y/%m/%d %H:%M:%S")
.unwrap();
let cell = insert_date_cell(date.timestamp(), None, field);
let cell = insert_date_cell(date.timestamp(), None, Some(false), field);
Some(cell)
},
_ => {

View File

@ -1,12 +1,15 @@
use collab_database::rows::RowId;
use flowy_database2::entities::CellChangesetPB;
use lib_infra::box_any::BoxAny;
use crate::database::database_editor::DatabaseEditorTest;
pub enum CellScript {
UpdateCell {
changeset: CellChangesetPB,
view_id: String,
field_id: String,
row_id: RowId,
changeset: BoxAny,
is_err: bool,
},
}
@ -35,25 +38,26 @@ impl DatabaseCellTest {
match script {
CellScript::UpdateCell {
view_id,
field_id,
row_id,
changeset,
is_err: _,
} => {
self
.editor
.update_cell_with_changeset(
&self.view_id,
RowId::from(changeset.row_id),
&changeset.field_id,
changeset.cell_changeset,
)
.update_cell_with_changeset(&view_id, row_id, &field_id, changeset)
.await
.unwrap();
}, // CellScript::AssertGridRevisionPad => {
// sleep(Duration::from_millis(2 * REVISION_WRITE_INTERVAL_IN_MILLIS)).await;
// let mut grid_rev_manager = grid_manager.make_grid_rev_manager(&self.grid_id, pool.clone()).unwrap();
// let grid_pad = grid_rev_manager.load::<GridPadBuilder>(None).await.unwrap();
// println!("{}", grid_pad.delta_str());
// }
},
// CellScript::AssertGridRevisionPad => {
// sleep(Duration::from_millis(2 * REVISION_WRITE_INTERVAL_IN_MILLIS)).await;
// let mut grid_rev_manager = grid_manager
// .make_grid_rev_manager(&self.grid_id, pool.clone())
// .unwrap();
// let grid_pad = grid_rev_manager.load::<GridPadBuilder>(None).await.unwrap();
// println!("{}", grid_pad.delta_str());
// },
}
}
}

View File

@ -1,16 +1,14 @@
use std::time::Duration;
use flowy_database2::entities::{CellChangesetPB, FieldType};
use flowy_database2::services::cell::ToCellChangeset;
use flowy_database2::services::field::checklist_type_option::ChecklistCellChangeset;
use flowy_database2::entities::FieldType;
use flowy_database2::services::field::{
DateCellData, MultiSelectTypeOption, SelectOptionCellChangeset, SingleSelectTypeOption,
StrCellData, URLCellData,
ChecklistCellChangeset, DateCellChangeset, DateCellData, MultiSelectTypeOption,
SelectOptionCellChangeset, SingleSelectTypeOption, StrCellData, URLCellData,
};
use lib_infra::box_any::BoxAny;
use crate::database::cell_test::script::CellScript::UpdateCell;
use crate::database::cell_test::script::DatabaseCellTest;
use crate::database::field_test::util::make_date_cell_string;
#[tokio::test]
async fn grid_cell_update() {
@ -26,40 +24,42 @@ async fn grid_cell_update() {
continue;
}
let cell_changeset = match field_type {
FieldType::RichText => "".to_string(),
FieldType::Number => "123".to_string(),
FieldType::DateTime => make_date_cell_string(123),
FieldType::RichText => BoxAny::new("".to_string()),
FieldType::Number => BoxAny::new("123".to_string()),
FieldType::DateTime => BoxAny::new(DateCellChangeset {
date: Some(123),
..Default::default()
}),
FieldType::SingleSelect => {
let type_option = field
.get_type_option::<SingleSelectTypeOption>(field.field_type)
.unwrap();
SelectOptionCellChangeset::from_insert_option_id(&type_option.options.first().unwrap().id)
.to_cell_changeset_str()
BoxAny::new(SelectOptionCellChangeset::from_insert_option_id(
&type_option.options.first().unwrap().id,
))
},
FieldType::MultiSelect => {
let type_option = field
.get_type_option::<MultiSelectTypeOption>(field.field_type)
.unwrap();
SelectOptionCellChangeset::from_insert_option_id(&type_option.options.first().unwrap().id)
.to_cell_changeset_str()
BoxAny::new(SelectOptionCellChangeset::from_insert_option_id(
&type_option.options.first().unwrap().id,
))
},
FieldType::Checklist => ChecklistCellChangeset {
FieldType::Checklist => BoxAny::new(ChecklistCellChangeset {
insert_options: vec!["new option".to_string()],
..Default::default()
}
.to_cell_changeset_str(),
FieldType::Checkbox => "1".to_string(),
FieldType::URL => "1".to_string(),
_ => "".to_string(),
}),
FieldType::Checkbox => BoxAny::new("1".to_string()),
FieldType::URL => BoxAny::new("1".to_string()),
_ => BoxAny::new("".to_string()),
};
scripts.push(UpdateCell {
changeset: CellChangesetPB {
view_id: test.view_id.clone(),
row_id: row_detail.row.id.clone().into(),
field_id: field.id.clone(),
cell_changeset,
},
view_id: test.view_id.clone(),
field_id: field.id.clone(),
row_id: row_detail.row.id.clone(),
changeset: cell_changeset,
is_err: false,
});
}
@ -125,12 +125,10 @@ async fn update_updated_at_field_on_other_cell_update() {
let before_update_timestamp = chrono::offset::Utc::now().timestamp();
test
.run_script(UpdateCell {
changeset: CellChangesetPB {
view_id: test.view_id.clone(),
row_id: test.row_details[0].row.id.to_string(),
field_id: text_field.id.clone(),
cell_changeset: "change".to_string(),
},
view_id: test.view_id.clone(),
row_id: test.row_details[0].row.id.clone(),
field_id: text_field.id.clone(),
changeset: BoxAny::new("change".to_string()),
is_err: false,
})
.await;

View File

@ -5,19 +5,20 @@ use collab_database::database::{gen_database_view_id, timestamp};
use collab_database::fields::Field;
use collab_database::rows::{CreateRowParams, RowDetail, RowId};
use collab_database::views::OrderObjectPosition;
use lib_infra::box_any::BoxAny;
use strum::EnumCount;
use event_integration::folder_event::ViewTest;
use event_integration::EventIntegrationTest;
use flowy_database2::entities::{FieldType, FilterPB, RowMetaPB};
use flowy_database2::services::cell::{CellBuilder, ToCellChangeset};
use flowy_database2::services::cell::CellBuilder;
use flowy_database2::services::database::DatabaseEditor;
use flowy_database2::services::field::checklist_type_option::{
ChecklistCellChangeset, ChecklistTypeOption,
};
use flowy_database2::services::field::{
CheckboxTypeOption, DateCellChangeset, MultiSelectTypeOption, SelectOption,
SelectOptionCellChangeset, SingleSelectTypeOption,
CheckboxTypeOption, MultiSelectTypeOption, SelectOption, SelectOptionCellChangeset,
SingleSelectTypeOption,
};
use flowy_database2::services::share::csv::{CSVFormat, ImportResult};
use flowy_error::FlowyResult;
@ -180,11 +181,11 @@ impl DatabaseEditorTest {
.unwrap()
}
pub async fn update_cell<T: ToCellChangeset>(
pub async fn update_cell(
&mut self,
field_id: &str,
row_id: RowId,
cell_changeset: T,
cell_changeset: BoxAny,
) -> FlowyResult<()> {
let field = self
.editor
@ -212,7 +213,7 @@ impl DatabaseEditorTest {
.clone();
self
.update_cell(&field.id, row_id, content.to_string())
.update_cell(&field.id, row_id, BoxAny::new(content.to_string()))
.await
}
@ -258,7 +259,9 @@ impl DatabaseEditorTest {
.clone();
let cell_changeset = SelectOptionCellChangeset::from_insert_option_id(option_id);
self.update_cell(&field.id, row_id, cell_changeset).await
self
.update_cell(&field.id, row_id, BoxAny::new(cell_changeset))
.await
}
pub async fn import(&self, s: String, format: CSVFormat) -> ImportResult {
@ -315,20 +318,15 @@ impl<'a> TestRowBuilder<'a> {
pub fn insert_date_cell(
&mut self,
data: i64,
date: i64,
time: Option<String>,
include_time: Option<bool>,
field_type: &FieldType,
) -> String {
let value = serde_json::to_string(&DateCellChangeset {
date: Some(data),
time,
include_time,
..Default::default()
})
.unwrap();
let date_field = self.field_with_type(field_type);
self.cell_build.insert_text_cell(&date_field.id, value);
self
.cell_build
.insert_date_cell(&date_field.id, date, time, include_time);
date_field.id.clone()
}

View File

@ -3,8 +3,8 @@ use collab_database::views::OrderObjectPosition;
use flowy_database2::entities::{CreateFieldParams, FieldType};
use flowy_database2::services::field::{
type_option_to_pb, DateCellChangeset, DateFormat, DateTypeOption, FieldBuilder,
RichTextTypeOption, SelectOption, SingleSelectTypeOption, TimeFormat, TimestampTypeOption,
type_option_to_pb, DateFormat, DateTypeOption, FieldBuilder, RichTextTypeOption, SelectOption,
SingleSelectTypeOption, TimeFormat, TimestampTypeOption,
};
pub fn create_text_field(grid_id: &str) -> (CreateFieldParams, Field) {
@ -103,19 +103,3 @@ pub fn create_timestamp_field(grid_id: &str, field_type: FieldType) -> (CreateFi
};
(params, field)
}
// The grid will contains all existing field types and there are three empty rows in this grid.
pub fn make_date_cell_string(timestamp: i64) -> String {
serde_json::to_string(&DateCellChangeset {
date: Some(timestamp),
time: None,
end_date: None,
end_time: None,
include_time: Some(false),
is_range: Some(false),
clear_flag: None,
reminder_id: Some(String::new()),
})
.unwrap()
}

View File

@ -214,7 +214,7 @@ impl DatabaseGroupTest {
let cell = match field_type {
FieldType::URL => insert_url_cell(cell_data, &field),
FieldType::DateTime => {
insert_date_cell(cell_data.parse::<i64>().unwrap(), Some(true), &field)
insert_date_cell(cell_data.parse::<i64>().unwrap(), None, Some(true), &field)
},
_ => {
panic!("Unsupported group field type");