fix: unit test errors

This commit is contained in:
appflowy 2022-04-07 15:34:00 +08:00
parent d09a5bf42b
commit a3770a699c
29 changed files with 468 additions and 1511 deletions

View File

@ -4,7 +4,7 @@ import 'package:app_flowy/user/domain/auth_state.dart';
import 'package:app_flowy/user/presentation/router.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/errors.pb.dart';
import 'package:flowy_sdk/protobuf/error-code/error_code.pbenum.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

View File

@ -1,5 +1,5 @@
import 'package:app_flowy/user/application/user_listener.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/errors.pb.dart';
import 'package:flowy_sdk/protobuf/error-code/error_code.pbenum.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

View File

@ -72,13 +72,28 @@ class _MultiSelectCellState extends State<MultiSelectCell> {
@override
void initState() {
_cellBloc = getIt<SelectionCellBloc>(param1: widget.cellData);
_cellBloc = getIt<SelectionCellBloc>(param1: widget.cellData)..add(const SelectionCellEvent.initial());
super.initState();
}
@override
Widget build(BuildContext context) {
return Container();
return BlocProvider.value(
value: _cellBloc,
child: BlocBuilder<SelectionCellBloc, SelectionCellState>(
builder: (context, state) {
final children = state.selectedOptions.map((option) => SelectOptionTag(option: option)).toList();
return SizedBox.expand(
child: InkWell(
onTap: () {
SelectOptionEditor.show(context, state.cellData, state.options, state.selectedOptions);
},
child: Row(children: children),
),
);
},
),
);
}
@override

View File

@ -1,11 +0,0 @@
///
// Generated code. Do not modify.
// source: errors.proto
//
// @dart = 2.12
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
import 'dart:core' as $core;
export 'errors.pbenum.dart';

View File

@ -1,56 +0,0 @@
///
// Generated code. Do not modify.
// source: errors.proto
//
// @dart = 2.12
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
// ignore_for_file: UNDEFINED_SHOWN_NAME
import 'dart:core' as $core;
import 'package:protobuf/protobuf.dart' as $pb;
class ErrorCode extends $pb.ProtobufEnum {
static const ErrorCode WorkspaceNameInvalid = ErrorCode._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceNameInvalid');
static const ErrorCode WorkspaceIdInvalid = ErrorCode._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceIdInvalid');
static const ErrorCode AppColorStyleInvalid = ErrorCode._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppColorStyleInvalid');
static const ErrorCode WorkspaceDescTooLong = ErrorCode._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceDescTooLong');
static const ErrorCode WorkspaceNameTooLong = ErrorCode._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceNameTooLong');
static const ErrorCode AppIdInvalid = ErrorCode._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppIdInvalid');
static const ErrorCode AppNameInvalid = ErrorCode._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppNameInvalid');
static const ErrorCode ViewNameInvalid = ErrorCode._(20, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewNameInvalid');
static const ErrorCode ViewThumbnailInvalid = ErrorCode._(21, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewThumbnailInvalid');
static const ErrorCode ViewIdInvalid = ErrorCode._(22, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewIdInvalid');
static const ErrorCode ViewDescTooLong = ErrorCode._(23, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewDescTooLong');
static const ErrorCode ViewDataInvalid = ErrorCode._(24, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewDataInvalid');
static const ErrorCode ViewNameTooLong = ErrorCode._(25, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewNameTooLong');
static const ErrorCode UserUnauthorized = ErrorCode._(100, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserUnauthorized');
static const ErrorCode WsConnectError = ErrorCode._(200, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WsConnectError');
static const ErrorCode InternalError = ErrorCode._(1000, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'InternalError');
static const ErrorCode RecordNotFound = ErrorCode._(1001, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'RecordNotFound');
static const $core.List<ErrorCode> values = <ErrorCode> [
WorkspaceNameInvalid,
WorkspaceIdInvalid,
AppColorStyleInvalid,
WorkspaceDescTooLong,
WorkspaceNameTooLong,
AppIdInvalid,
AppNameInvalid,
ViewNameInvalid,
ViewThumbnailInvalid,
ViewIdInvalid,
ViewDescTooLong,
ViewDataInvalid,
ViewNameTooLong,
UserUnauthorized,
WsConnectError,
InternalError,
RecordNotFound,
];
static final $core.Map<$core.int, ErrorCode> _byValue = $pb.ProtobufEnum.initByValue(values);
static ErrorCode? valueOf($core.int value) => _byValue[value];
const ErrorCode._($core.int v, $core.String n) : super(v, n);
}

View File

@ -1,36 +0,0 @@
///
// Generated code. Do not modify.
// source: errors.proto
//
// @dart = 2.12
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
import 'dart:core' as $core;
import 'dart:convert' as $convert;
import 'dart:typed_data' as $typed_data;
@$core.Deprecated('Use errorCodeDescriptor instead')
const ErrorCode$json = const {
'1': 'ErrorCode',
'2': const [
const {'1': 'WorkspaceNameInvalid', '2': 0},
const {'1': 'WorkspaceIdInvalid', '2': 1},
const {'1': 'AppColorStyleInvalid', '2': 2},
const {'1': 'WorkspaceDescTooLong', '2': 3},
const {'1': 'WorkspaceNameTooLong', '2': 4},
const {'1': 'AppIdInvalid', '2': 10},
const {'1': 'AppNameInvalid', '2': 11},
const {'1': 'ViewNameInvalid', '2': 20},
const {'1': 'ViewThumbnailInvalid', '2': 21},
const {'1': 'ViewIdInvalid', '2': 22},
const {'1': 'ViewDescTooLong', '2': 23},
const {'1': 'ViewDataInvalid', '2': 24},
const {'1': 'ViewNameTooLong', '2': 25},
const {'1': 'UserUnauthorized', '2': 100},
const {'1': 'WsConnectError', '2': 200},
const {'1': 'InternalError', '2': 1000},
const {'1': 'RecordNotFound', '2': 1001},
],
};
/// Descriptor for `ErrorCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode('CglFcnJvckNvZGUSGAoUV29ya3NwYWNlTmFtZUludmFsaWQQABIWChJXb3Jrc3BhY2VJZEludmFsaWQQARIYChRBcHBDb2xvclN0eWxlSW52YWxpZBACEhgKFFdvcmtzcGFjZURlc2NUb29Mb25nEAMSGAoUV29ya3NwYWNlTmFtZVRvb0xvbmcQBBIQCgxBcHBJZEludmFsaWQQChISCg5BcHBOYW1lSW52YWxpZBALEhMKD1ZpZXdOYW1lSW52YWxpZBAUEhgKFFZpZXdUaHVtYm5haWxJbnZhbGlkEBUSEQoNVmlld0lkSW52YWxpZBAWEhMKD1ZpZXdEZXNjVG9vTG9uZxAXEhMKD1ZpZXdEYXRhSW52YWxpZBAYEhMKD1ZpZXdOYW1lVG9vTG9uZxAZEhQKEFVzZXJVbmF1dGhvcml6ZWQQZBITCg5Xc0Nvbm5lY3RFcnJvchDIARISCg1JbnRlcm5hbEVycm9yEOgHEhMKDlJlY29yZE5vdEZvdW5kEOkH');

View File

@ -1,9 +0,0 @@
///
// Generated code. Do not modify.
// source: errors.proto
//
// @dart = 2.12
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
export 'errors.pb.dart';

View File

@ -1,3 +1,3 @@
// Auto-generated, do not edit
// Auto-generated, do not edit
export './grid.pb.dart';
export './meta.pb.dart';

View File

@ -52,7 +52,7 @@ macro_rules! impl_type_option {
match serde_json::to_string(&self) {
Ok(s) => s,
Err(e) => {
tracing::error!("Field type data convert to AnyData fail, error: {:?}", e);
tracing::error!("Field type data serialize to json fail, error: {:?}", e);
serde_json::to_string(&$target::default()).unwrap()
}
}

View File

@ -23,6 +23,7 @@ pub struct GridManager {
editor_map: Arc<GridEditorMap>,
grid_user: Arc<dyn GridUser>,
block_index_persistence: Arc<BlockIndexPersistence>,
#[allow(dead_code)]
kv_persistence: Arc<GridKVPersistence>,
}
@ -181,12 +182,21 @@ pub async fn make_grid_view_data(
block_metas: vec![build_context.block_metas],
};
// Create grid
let grid_meta_delta = make_grid_delta(&grid_meta);
let grid_delta_data = grid_meta_delta.to_delta_bytes();
let repeated_revision: RepeatedRevision =
Revision::initial_revision(user_id, view_id, grid_delta_data.clone()).into();
let _ = grid_manager.create_grid(view_id, repeated_revision).await?;
// Indexing the block's rows
build_context.block_meta_data.row_metas.iter().for_each(|row| {
let _ = grid_manager
.block_index_persistence
.insert_or_update(&row.block_id, &row.id);
});
// Create grid's block
let grid_block_meta_delta = make_block_meta_delta(&build_context.block_meta_data);
let block_meta_delta_data = grid_block_meta_delta.to_delta_bytes();
let repeated_revision: RepeatedRevision =

View File

@ -1,263 +1,16 @@
use crate::dart_notification::{send_dart_notification, GridNotification};
use crate::manager::GridUser;
use crate::services::persistence::block_index::BlockIndexPersistence;
use crate::services::row::{make_block_row_ids, make_cell_by_field_id, make_rows_from_row_metas, GridBlockSnapshot};
use bytes::Bytes;
use dashmap::DashMap;
use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::entities::{
CellMeta, CellMetaChangeset, CellNotificationData, FieldMeta, GridBlockMeta, GridBlockMetaChangeset,
GridBlockOrder, RepeatedCell, Row, RowMeta, RowMetaChangeset, RowOrder,
};
use flowy_revision::disk::SQLiteGridBlockMetaRevisionPersistence;
use flowy_revision::{
RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder, RevisionPersistence,
};
use flowy_grid_data_model::entities::{CellMeta, RowMeta, RowMetaChangeset, RowOrder};
use flowy_revision::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder};
use flowy_sync::client_grid::{GridBlockMetaChange, GridBlockMetaPad};
use flowy_sync::entities::revision::Revision;
use flowy_sync::util::make_delta_from_revisions;
use lib_infra::future::FutureResult;
use lib_ot::core::PlainTextAttributes;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
pub(crate) struct GridBlockMetaEditorManager {
grid_id: String,
user: Arc<dyn GridUser>,
editor_map: DashMap<String, Arc<ClientGridBlockMetaEditor>>,
persistence: Arc<BlockIndexPersistence>,
}
impl GridBlockMetaEditorManager {
pub(crate) async fn new(
grid_id: &str,
user: &Arc<dyn GridUser>,
blocks: Vec<GridBlockMeta>,
persistence: Arc<BlockIndexPersistence>,
) -> FlowyResult<Self> {
let editor_map = make_block_meta_editor_map(user, blocks).await?;
let user = user.clone();
let grid_id = grid_id.to_owned();
let manager = Self {
grid_id,
user,
editor_map,
persistence,
};
Ok(manager)
}
// #[tracing::instrument(level = "trace", skip(self))]
pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaEditor>> {
debug_assert!(!block_id.is_empty());
match self.editor_map.get(block_id) {
None => {
tracing::error!("The is a fatal error, block is not exist");
let editor = Arc::new(make_block_meta_editor(&self.user, block_id).await?);
self.editor_map.insert(block_id.to_owned(), editor.clone());
Ok(editor)
}
Some(editor) => Ok(editor.clone()),
}
}
async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaEditor>> {
let block_id = self.persistence.get_block_id(row_id)?;
Ok(self.get_editor(&block_id).await?)
}
pub(crate) async fn create_row(
&self,
block_id: &str,
row_meta: RowMeta,
start_row_id: Option<String>,
) -> FlowyResult<i32> {
let _ = self.persistence.insert_or_update(&row_meta.block_id, &row_meta.id)?;
let editor = self.get_editor(&row_meta.block_id).await?;
let row_count = editor.create_row(row_meta, start_row_id).await?;
self.notify_block_did_update_row(block_id).await?;
Ok(row_count)
}
pub(crate) async fn insert_row(
&self,
rows_by_block_id: HashMap<String, Vec<RowMeta>>,
) -> FlowyResult<Vec<GridBlockMetaChangeset>> {
let mut changesets = vec![];
for (block_id, row_metas) in rows_by_block_id {
let editor = self.get_editor(&block_id).await?;
let mut row_count = 0;
for row in &row_metas {
let _ = self.persistence.insert_or_update(&row.block_id, &row.id)?;
row_count = editor.create_row(row.clone(), None).await?;
}
changesets.push(GridBlockMetaChangeset::from_row_count(&block_id, row_count));
let _ = self.notify_block_did_update_row(&block_id).await?;
}
Ok(changesets)
}
pub(crate) async fn delete_rows(&self, row_orders: Vec<RowOrder>) -> FlowyResult<Vec<GridBlockMetaChangeset>> {
let mut changesets = vec![];
for block_row_ids in make_block_row_ids(&row_orders) {
let editor = self.get_editor(&block_row_ids.block_id).await?;
let row_count = editor.delete_rows(block_row_ids.row_ids).await?;
let changeset = GridBlockMetaChangeset::from_row_count(&block_row_ids.block_id, row_count);
changesets.push(changeset);
}
Ok(changesets)
}
pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> {
let editor = self.get_editor_from_row_id(&changeset.row_id).await?;
let _ = editor.update_row(changeset.clone()).await?;
let _ = self.notify_block_did_update_row(&editor.block_id).await?;
Ok(())
}
pub async fn update_cell(&self, changeset: CellMetaChangeset) -> FlowyResult<()> {
let row_id = changeset.row_id.clone();
let editor = self.get_editor_from_row_id(&row_id).await?;
let row_changeset: RowMetaChangeset = changeset.clone().into();
let _ = editor.update_row(row_changeset).await?;
let cell_notification_data = CellNotificationData {
grid_id: changeset.grid_id,
field_id: changeset.field_id,
row_id: changeset.row_id,
content: changeset.data,
};
self.notify_did_update_cell(cell_notification_data).await?;
Ok(())
}
pub async fn get_row_meta(&self, row_id: &str) -> FlowyResult<Option<Arc<RowMeta>>> {
let editor = self.get_editor_from_row_id(row_id).await?;
let row_ids = vec![row_id.to_owned()];
let mut row_metas = editor.get_row_metas(Some(row_ids)).await?;
if row_metas.is_empty() {
Ok(None)
} else {
Ok(row_metas.pop())
}
}
pub(crate) async fn make_block_snapshots(&self, block_ids: Vec<String>) -> FlowyResult<Vec<GridBlockSnapshot>> {
let mut snapshots = vec![];
for block_id in block_ids {
let editor = self.get_editor(&block_id).await?;
let row_metas = editor.get_row_metas(None).await?;
row_metas.iter().for_each(|row| {
let _ = self.persistence.insert_or_update(&row.block_id, &row.id);
});
snapshots.push(GridBlockSnapshot { block_id, row_metas });
}
Ok(snapshots)
}
// Optimization: Using the shared memory(Arc, Cow,etc.) to reduce memory usage.
#[allow(dead_code)]
pub async fn get_cell_metas(
&self,
block_ids: Vec<String>,
field_id: &str,
row_ids: Option<Vec<String>>,
) -> FlowyResult<Vec<CellMeta>> {
let mut block_cell_metas = vec![];
for block_id in block_ids {
let editor = self.get_editor(&block_id).await?;
let cell_metas = editor.get_cell_metas(field_id, &row_ids).await?;
block_cell_metas.extend(cell_metas);
}
Ok(block_cell_metas)
}
async fn notify_block_did_update_row(&self, block_id: &str) -> FlowyResult<()> {
let block_order: GridBlockOrder = block_id.into();
send_dart_notification(&self.grid_id, GridNotification::DidUpdateBlock)
.payload(block_order)
.send();
Ok(())
}
async fn notify_did_update_cell(&self, data: CellNotificationData) -> FlowyResult<()> {
let id = format!("{}:{}", data.row_id, data.field_id);
send_dart_notification(&id, GridNotification::DidUpdateCell)
.payload(data)
.send();
Ok(())
}
async fn notify_did_update_row(&self, row_id: &str, field_metas: &[FieldMeta]) -> FlowyResult<()> {
match self.get_row_meta(row_id).await? {
None => {}
Some(row_meta) => {
let row_metas = vec![row_meta];
if let Some(row) = make_rows_from_row_metas(&field_metas, &row_metas).pop() {
send_dart_notification(row_id, GridNotification::DidUpdateRow)
.payload(row)
.send();
}
}
}
Ok(())
//
// let field_meta_map = field_metas
// .iter()
// .map(|field_meta| (&field_meta.id, field_meta))
// .collect::<HashMap<&String, &FieldMeta>>();
//
// let mut cells = vec![];
// changeset
// .cell_by_field_id
// .into_iter()
// .for_each(
// |(field_id, cell_meta)| match make_cell_by_field_id(&field_meta_map, field_id, cell_meta) {
// None => {}
// Some((_, cell)) => cells.push(cell),
// },
// );
//
// if !cells.is_empty() {
// send_dart_notification(&changeset.row_id, GridNotification::DidUpdateRow)
// .payload(RepeatedCell::from(cells))
// .send();
// }
// Ok(())
}
}
async fn make_block_meta_editor_map(
user: &Arc<dyn GridUser>,
blocks: Vec<GridBlockMeta>,
) -> FlowyResult<DashMap<String, Arc<ClientGridBlockMetaEditor>>> {
let editor_map = DashMap::new();
for block in blocks {
let editor = make_block_meta_editor(user, &block.block_id).await?;
editor_map.insert(block.block_id, Arc::new(editor));
}
Ok(editor_map)
}
async fn make_block_meta_editor(user: &Arc<dyn GridUser>, block_id: &str) -> FlowyResult<ClientGridBlockMetaEditor> {
let token = user.token()?;
let user_id = user.user_id()?;
let pool = user.db_pool()?;
let disk_cache = Arc::new(SQLiteGridBlockMetaRevisionPersistence::new(&user_id, pool));
let rev_persistence = Arc::new(RevisionPersistence::new(&user_id, block_id, disk_cache));
let rev_manager = RevisionManager::new(&user_id, block_id, rev_persistence);
ClientGridBlockMetaEditor::new(&user_id, &token, block_id, rev_manager).await
}
pub struct ClientGridBlockMetaEditor {
user_id: String,
pub block_id: String,
@ -288,7 +41,7 @@ impl ClientGridBlockMetaEditor {
})
}
async fn create_row(&self, row: RowMeta, start_row_id: Option<String>) -> FlowyResult<i32> {
pub(crate) async fn create_row(&self, row: RowMeta, start_row_id: Option<String>) -> FlowyResult<i32> {
let mut row_count = 0;
let _ = self
.modify(|pad| {

View File

@ -0,0 +1,248 @@
use crate::dart_notification::{send_dart_notification, GridNotification};
use crate::manager::GridUser;
use crate::services::block_meta_editor::ClientGridBlockMetaEditor;
use crate::services::persistence::block_index::BlockIndexPersistence;
use crate::services::row::{make_block_row_ids, make_rows_from_row_metas, GridBlockSnapshot};
use dashmap::DashMap;
use flowy_error::FlowyResult;
use flowy_grid_data_model::entities::{
CellMeta, CellMetaChangeset, CellNotificationData, FieldMeta, GridBlockMeta, GridBlockMetaChangeset,
GridBlockOrder, RowMeta, RowMetaChangeset, RowOrder,
};
use flowy_revision::disk::SQLiteGridBlockMetaRevisionPersistence;
use flowy_revision::{RevisionManager, RevisionPersistence};
use std::collections::HashMap;
use std::sync::Arc;
pub(crate) struct GridBlockMetaEditorManager {
grid_id: String,
user: Arc<dyn GridUser>,
editor_map: DashMap<String, Arc<ClientGridBlockMetaEditor>>,
persistence: Arc<BlockIndexPersistence>,
}
impl GridBlockMetaEditorManager {
pub(crate) async fn new(
grid_id: &str,
user: &Arc<dyn GridUser>,
blocks: Vec<GridBlockMeta>,
persistence: Arc<BlockIndexPersistence>,
) -> FlowyResult<Self> {
let editor_map = make_block_meta_editor_map(user, blocks).await?;
let user = user.clone();
let grid_id = grid_id.to_owned();
let manager = Self {
grid_id,
user,
editor_map,
persistence,
};
Ok(manager)
}
// #[tracing::instrument(level = "trace", skip(self))]
pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaEditor>> {
debug_assert!(!block_id.is_empty());
match self.editor_map.get(block_id) {
None => {
tracing::error!("The is a fatal error, block is not exist");
let editor = Arc::new(make_block_meta_editor(&self.user, block_id).await?);
self.editor_map.insert(block_id.to_owned(), editor.clone());
Ok(editor)
}
Some(editor) => Ok(editor.clone()),
}
}
async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaEditor>> {
let block_id = self.persistence.get_block_id(row_id)?;
Ok(self.get_editor(&block_id).await?)
}
pub(crate) async fn create_row(
&self,
block_id: &str,
row_meta: RowMeta,
start_row_id: Option<String>,
) -> FlowyResult<i32> {
let _ = self.persistence.insert_or_update(&row_meta.block_id, &row_meta.id)?;
let editor = self.get_editor(&row_meta.block_id).await?;
let row_count = editor.create_row(row_meta, start_row_id).await?;
self.notify_block_did_update_row(block_id).await?;
Ok(row_count)
}
pub(crate) async fn insert_row(
&self,
rows_by_block_id: HashMap<String, Vec<RowMeta>>,
) -> FlowyResult<Vec<GridBlockMetaChangeset>> {
let mut changesets = vec![];
for (block_id, row_metas) in rows_by_block_id {
let editor = self.get_editor(&block_id).await?;
let mut row_count = 0;
for row in &row_metas {
let _ = self.persistence.insert_or_update(&row.block_id, &row.id)?;
row_count = editor.create_row(row.clone(), None).await?;
}
changesets.push(GridBlockMetaChangeset::from_row_count(&block_id, row_count));
let _ = self.notify_block_did_update_row(&block_id).await?;
}
Ok(changesets)
}
pub(crate) async fn delete_rows(&self, row_orders: Vec<RowOrder>) -> FlowyResult<Vec<GridBlockMetaChangeset>> {
let mut changesets = vec![];
for block_row_ids in make_block_row_ids(&row_orders) {
let editor = self.get_editor(&block_row_ids.block_id).await?;
let row_count = editor.delete_rows(block_row_ids.row_ids).await?;
let changeset = GridBlockMetaChangeset::from_row_count(&block_row_ids.block_id, row_count);
changesets.push(changeset);
}
Ok(changesets)
}
pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> {
let editor = self.get_editor_from_row_id(&changeset.row_id).await?;
let _ = editor.update_row(changeset.clone()).await?;
let _ = self.notify_block_did_update_row(&editor.block_id).await?;
Ok(())
}
pub async fn update_cell(&self, changeset: CellMetaChangeset) -> FlowyResult<()> {
let row_id = changeset.row_id.clone();
let editor = self.get_editor_from_row_id(&row_id).await?;
let row_changeset: RowMetaChangeset = changeset.clone().into();
let _ = editor.update_row(row_changeset).await?;
let cell_notification_data = CellNotificationData {
grid_id: changeset.grid_id,
field_id: changeset.field_id,
row_id: changeset.row_id,
content: changeset.data,
};
self.notify_did_update_cell(cell_notification_data).await?;
Ok(())
}
pub async fn get_row_meta(&self, row_id: &str) -> FlowyResult<Option<Arc<RowMeta>>> {
let editor = self.get_editor_from_row_id(row_id).await?;
let row_ids = vec![row_id.to_owned()];
let mut row_metas = editor.get_row_metas(Some(row_ids)).await?;
if row_metas.is_empty() {
Ok(None)
} else {
Ok(row_metas.pop())
}
}
pub(crate) async fn make_block_snapshots(&self, block_ids: Vec<String>) -> FlowyResult<Vec<GridBlockSnapshot>> {
let mut snapshots = vec![];
for block_id in block_ids {
let editor = self.get_editor(&block_id).await?;
let row_metas = editor.get_row_metas(None).await?;
snapshots.push(GridBlockSnapshot { block_id, row_metas });
}
Ok(snapshots)
}
// Optimization: Using the shared memory(Arc, Cow,etc.) to reduce memory usage.
#[allow(dead_code)]
pub async fn get_cell_metas(
&self,
block_ids: Vec<String>,
field_id: &str,
row_ids: Option<Vec<String>>,
) -> FlowyResult<Vec<CellMeta>> {
let mut block_cell_metas = vec![];
for block_id in block_ids {
let editor = self.get_editor(&block_id).await?;
let cell_metas = editor.get_cell_metas(field_id, &row_ids).await?;
block_cell_metas.extend(cell_metas);
}
Ok(block_cell_metas)
}
async fn notify_block_did_update_row(&self, block_id: &str) -> FlowyResult<()> {
let block_order: GridBlockOrder = block_id.into();
send_dart_notification(&self.grid_id, GridNotification::DidUpdateBlock)
.payload(block_order)
.send();
Ok(())
}
async fn notify_did_update_cell(&self, data: CellNotificationData) -> FlowyResult<()> {
let id = format!("{}:{}", data.row_id, data.field_id);
send_dart_notification(&id, GridNotification::DidUpdateCell)
.payload(data)
.send();
Ok(())
}
#[allow(dead_code)]
async fn notify_did_update_row(&self, row_id: &str, field_metas: &[FieldMeta]) -> FlowyResult<()> {
match self.get_row_meta(row_id).await? {
None => {}
Some(row_meta) => {
let row_metas = vec![row_meta];
if let Some(row) = make_rows_from_row_metas(field_metas, &row_metas).pop() {
send_dart_notification(row_id, GridNotification::DidUpdateRow)
.payload(row)
.send();
}
}
}
Ok(())
//
// let field_meta_map = field_metas
// .iter()
// .map(|field_meta| (&field_meta.id, field_meta))
// .collect::<HashMap<&String, &FieldMeta>>();
//
// let mut cells = vec![];
// changeset
// .cell_by_field_id
// .into_iter()
// .for_each(
// |(field_id, cell_meta)| match make_cell_by_field_id(&field_meta_map, field_id, cell_meta) {
// None => {}
// Some((_, cell)) => cells.push(cell),
// },
// );
//
// if !cells.is_empty() {
// send_dart_notification(&changeset.row_id, GridNotification::DidUpdateRow)
// .payload(RepeatedCell::from(cells))
// .send();
// }
// Ok(())
}
}
async fn make_block_meta_editor_map(
user: &Arc<dyn GridUser>,
blocks: Vec<GridBlockMeta>,
) -> FlowyResult<DashMap<String, Arc<ClientGridBlockMetaEditor>>> {
let editor_map = DashMap::new();
for block in blocks {
let editor = make_block_meta_editor(user, &block.block_id).await?;
editor_map.insert(block.block_id, Arc::new(editor));
}
Ok(editor_map)
}
async fn make_block_meta_editor(user: &Arc<dyn GridUser>, block_id: &str) -> FlowyResult<ClientGridBlockMetaEditor> {
let token = user.token()?;
let user_id = user.user_id()?;
let pool = user.db_pool()?;
let disk_cache = Arc::new(SQLiteGridBlockMetaRevisionPersistence::new(&user_id, pool));
let rev_persistence = Arc::new(RevisionPersistence::new(&user_id, block_id, disk_cache));
let rev_manager = RevisionManager::new(&user_id, block_id, rev_persistence);
ClientGridBlockMetaEditor::new(&user_id, &token, block_id, rev_manager).await
}

View File

@ -43,7 +43,7 @@ const NO: &str = "No";
impl CellDataOperation for CheckboxTypeOption {
fn decode_cell_data(&self, data: String, _field_meta: &FieldMeta) -> String {
if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) {
if !type_option_cell_data.is_text() || !type_option_cell_data.is_checkbox() {
if !type_option_cell_data.is_checkbox() {
return String::new();
}
let cell_data = type_option_cell_data.data;
@ -58,7 +58,7 @@ impl CellDataOperation for CheckboxTypeOption {
fn apply_changeset<T: Into<CellDataChangeset>>(
&self,
changeset: T,
cell_meta: Option<CellMeta>,
_cell_meta: Option<CellMeta>,
) -> Result<String, FlowyError> {
let changeset = changeset.into();
let s = match string_to_bool(&changeset) {
@ -84,6 +84,7 @@ fn string_to_bool(bool_str: &str) -> bool {
#[cfg(test)]
mod tests {
use crate::services::field::type_options::checkbox_type_option::{NO, YES};
use crate::services::field::CheckboxTypeOption;
use crate::services::field::FieldBuilder;
use crate::services::row::CellDataOperation;
@ -94,17 +95,22 @@ mod tests {
let type_option = CheckboxTypeOption::default();
let field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build();
assert_eq!(type_option.apply_changeset("true").unwrap(), "1".to_owned());
assert_eq!(type_option.apply_changeset("1").unwrap(), "1".to_owned());
assert_eq!(type_option.apply_changeset("yes").unwrap(), "1".to_owned());
let data = type_option.apply_changeset("true", None).unwrap();
assert_eq!(type_option.decode_cell_data(data, &field_meta), YES);
assert_eq!(type_option.apply_changeset("false").unwrap(), "0".to_owned());
assert_eq!(type_option.apply_changeset("no").unwrap(), "0".to_owned());
assert_eq!(type_option.apply_changeset("123").unwrap(), "0".to_owned());
let data = type_option.apply_changeset("1", None).unwrap();
assert_eq!(type_option.decode_cell_data(data, &field_meta), YES);
assert_eq!(
type_option.decode_cell_data("1".to_owned(), &field_meta),
"1".to_owned()
);
let data = type_option.apply_changeset("yes", None).unwrap();
assert_eq!(type_option.decode_cell_data(data, &field_meta), YES);
let data = type_option.apply_changeset("false", None).unwrap();
assert_eq!(type_option.decode_cell_data(data, &field_meta), NO);
let data = type_option.apply_changeset("no", None).unwrap();
assert_eq!(type_option.decode_cell_data(data, &field_meta), NO);
let data = type_option.apply_changeset("123", None).unwrap();
assert_eq!(type_option.decode_cell_data(data, &field_meta), NO);
}
}

View File

@ -66,7 +66,7 @@ impl CellDataOperation for DateTypeOption {
fn apply_changeset<T: Into<CellDataChangeset>>(
&self,
changeset: T,
cell_meta: Option<CellMeta>,
_cell_meta: Option<CellMeta>,
) -> Result<String, FlowyError> {
let changeset = changeset.into();
if let Err(e) = changeset.parse::<i64>() {
@ -282,7 +282,7 @@ mod tests {
#[should_panic]
fn date_description_invalid_data_test() {
let type_option = DateTypeOption::default();
type_option.apply_changeset("he").unwrap();
type_option.apply_changeset("he", None).unwrap();
}
fn data(s: &str) -> String {

View File

@ -104,7 +104,7 @@ impl CellDataOperation for NumberTypeOption {
fn apply_changeset<T: Into<CellDataChangeset>>(
&self,
changeset: T,
cell_meta: Option<CellMeta>,
_cell_meta: Option<CellMeta>,
) -> Result<String, FlowyError> {
let changeset = changeset.into();
let data = self.strip_symbol(changeset);

View File

@ -5,15 +5,13 @@ use crate::services::row::{CellDataChangeset, CellDataOperation, TypeOptionCellD
use crate::services::util::*;
use bytes::Bytes;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_error::{ErrorCode, FlowyError};
use flowy_grid_data_model::entities::{
CellMeta, CellMetaChangeset, FieldMeta, FieldType, TypeOptionDataEntity, TypeOptionDataEntry,
};
use flowy_grid_data_model::parser::{NotEmptyStr, NotEmptyUuid};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use flowy_grid_data_model::parser::NotEmptyUuid;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use uuid::Uuid;
pub const SELECTION_IDS_SEPARATOR: &str = ",";
@ -66,9 +64,9 @@ impl CellDataOperation for SingleSelectTypeOption {
return String::new();
}
match select_option_ids(type_option_cell_data.data).pop() {
match select_option_ids(type_option_cell_data.data).first() {
None => String::new(),
Some(option_id) => match self.options.iter().find(|option| option.id == option_id) {
Some(option_id) => match self.options.iter().find(|option| &option.id == option_id) {
None => String::new(),
Some(option) => option.name.clone(),
},
@ -92,7 +90,7 @@ impl CellDataOperation for SingleSelectTypeOption {
new_cell_data = "".to_string()
}
return Ok(TypeOptionCellData::new(&new_cell_data, self.field_type()).json());
Ok(TypeOptionCellData::new(&new_cell_data, self.field_type()).json())
}
}
@ -416,7 +414,7 @@ impl std::default::Default for SelectOptionColor {
}
}
fn make_select_context_from(cell_meta: &Option<CellMeta>, options: &Vec<SelectOption>) -> Vec<SelectOption> {
fn make_select_context_from(cell_meta: &Option<CellMeta>, options: &[SelectOption]) -> Vec<SelectOption> {
match cell_meta {
None => vec![],
Some(cell_meta) => {
@ -434,16 +432,97 @@ fn make_select_context_from(cell_meta: &Option<CellMeta>, options: &Vec<SelectOp
#[cfg(test)]
mod tests {
use crate::services::field::{MultiSelectTypeOption, SingleSelectTypeOption};
use crate::services::field::FieldBuilder;
use crate::services::field::{
MultiSelectTypeOption, MultiSelectTypeOptionBuilder, SelectOption, SelectOptionCellChangeset,
SingleSelectTypeOption, SingleSelectTypeOptionBuilder, SELECTION_IDS_SEPARATOR,
};
use crate::services::row::CellDataOperation;
#[test]
#[should_panic]
fn selection_description_test() {
let type_option = SingleSelectTypeOption::default();
assert_eq!(type_option.apply_changeset("1,2,3").unwrap(), "1".to_owned());
fn single_select_test() {
let google_option = SelectOption::new("Google");
let facebook_option = SelectOption::new("Facebook");
let twitter_option = SelectOption::new("Twitter");
let single_select = SingleSelectTypeOptionBuilder::default()
.option(google_option.clone())
.option(facebook_option.clone())
.option(twitter_option.clone());
let type_option = MultiSelectTypeOption::default();
assert_eq!(type_option.apply_changeset("1,2,3").unwrap(), "1,2,3".to_owned());
let field_meta = FieldBuilder::new(single_select)
.name("Platform")
.visibility(true)
.build();
let type_option = SingleSelectTypeOption::from(&field_meta);
let option_ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR);
let data = SelectOptionCellChangeset::from_insert(&option_ids).cell_data();
let cell_data = type_option.apply_changeset(data, None).unwrap();
assert_eq!(type_option.decode_cell_data(cell_data, &field_meta), google_option.name,);
let data = SelectOptionCellChangeset::from_insert(&google_option.id).cell_data();
let cell_data = type_option.apply_changeset(data, None).unwrap();
assert_eq!(type_option.decode_cell_data(cell_data, &field_meta), google_option.name,);
// Invalid option id
let cell_data = type_option
.apply_changeset(SelectOptionCellChangeset::from_insert("").cell_data(), None)
.unwrap();
assert_eq!(type_option.decode_cell_data(cell_data, &field_meta), "",);
// Invalid option id
let cell_data = type_option
.apply_changeset(SelectOptionCellChangeset::from_insert("123").cell_data(), None)
.unwrap();
assert_eq!(type_option.decode_cell_data(cell_data, &field_meta), "",);
// Invalid changeset
assert!(type_option.apply_changeset("123", None).is_err());
}
#[test]
fn multi_select_test() {
let google_option = SelectOption::new("Google");
let facebook_option = SelectOption::new("Facebook");
let twitter_option = SelectOption::new("Twitter");
let multi_select = MultiSelectTypeOptionBuilder::default()
.option(google_option.clone())
.option(facebook_option.clone())
.option(twitter_option.clone());
let field_meta = FieldBuilder::new(multi_select)
.name("Platform")
.visibility(true)
.build();
let type_option = MultiSelectTypeOption::from(&field_meta);
let option_ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR);
let data = SelectOptionCellChangeset::from_insert(&option_ids).cell_data();
let cell_data = type_option.apply_changeset(data, None).unwrap();
assert_eq!(
type_option.decode_cell_data(cell_data, &field_meta),
vec![google_option.name.clone(), facebook_option.name.clone()].join(SELECTION_IDS_SEPARATOR),
);
let data = SelectOptionCellChangeset::from_insert(&google_option.id).cell_data();
let cell_data = type_option.apply_changeset(data, None).unwrap();
assert_eq!(type_option.decode_cell_data(cell_data, &field_meta), google_option.name,);
// Invalid option id
let cell_data = type_option
.apply_changeset(SelectOptionCellChangeset::from_insert("").cell_data(), None)
.unwrap();
assert_eq!(type_option.decode_cell_data(cell_data, &field_meta), "",);
// Invalid option id
let cell_data = type_option
.apply_changeset(SelectOptionCellChangeset::from_insert("123,456").cell_data(), None)
.unwrap();
assert_eq!(type_option.decode_cell_data(cell_data, &field_meta), "",);
// Invalid changeset
assert!(type_option.apply_changeset("123", None).is_err());
}
}

View File

@ -50,7 +50,7 @@ impl CellDataOperation for RichTextTypeOption {
fn apply_changeset<T: Into<CellDataChangeset>>(
&self,
changeset: T,
cell_meta: Option<CellMeta>,
_cell_meta: Option<CellMeta>,
) -> Result<String, FlowyError> {
let data = changeset.into();
if data.len() > 10000 {
@ -85,29 +85,25 @@ mod tests {
let done_option_id = done_option.id.clone();
let single_select = SingleSelectTypeOptionBuilder::default().option(done_option);
let single_select_field_meta = FieldBuilder::new(single_select).build();
let data = TypeOptionCellData::new(&done_option_id, FieldType::SingleSelect).json();
let cell_data = TypeOptionCellData::new(&done_option_id, FieldType::SingleSelect).json();
assert_eq!(
type_option.decode_cell_data(data, &single_select_field_meta),
type_option.decode_cell_data(cell_data, &single_select_field_meta),
"Done".to_owned()
);
// Multiple select
let google_option = SelectOption::new("Google");
let google_option_id = google_option.id.clone();
let facebook_option = SelectOption::new("Facebook");
let face_option_id = facebook_option.id.clone();
let ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR);
let cell_data_changeset = SelectOptionCellChangeset::from_insert(&ids).cell_data();
let multi_select = MultiSelectTypeOptionBuilder::default()
.option(google_option)
.option(facebook_option)
.option(SelectOption::new("Twitter"));
.option(facebook_option);
let multi_select_field_meta = FieldBuilder::new(multi_select).build();
let data = TypeOptionCellData::new(
&vec![google_option_id, face_option_id].join(SELECTION_IDS_SEPARATOR),
FieldType::SingleSelect,
)
.json();
let multi_type_option = MultiSelectTypeOption::from(&multi_select_field_meta);
let cell_data = multi_type_option.apply_changeset(cell_data_changeset, None).unwrap();
assert_eq!(
type_option.decode_cell_data(data, &multi_select_field_meta),
type_option.decode_cell_data(cell_data, &multi_select_field_meta),
"Google,Facebook".to_owned()
);

View File

@ -1,6 +1,6 @@
use crate::dart_notification::{send_dart_notification, GridNotification};
use crate::manager::GridUser;
use crate::services::block_meta_editor::GridBlockMetaEditorManager;
use crate::services::block_meta_manager::GridBlockMetaEditorManager;
use crate::services::field::{default_type_option_builder_from_type, type_option_builder_from_bytes, FieldBuilder};
use crate::services::persistence::block_index::BlockIndexPersistence;
use crate::services::row::*;
@ -154,7 +154,7 @@ impl ClientGridEditor {
pub async fn get_field_meta(&self, field_id: &str) -> Option<FieldMeta> {
let field_meta = self.pad.read().await.get_field(field_id)?.clone();
return Some(field_meta);
Some(field_meta)
}
pub async fn get_field_metas<T>(&self, field_ids: Option<Vec<T>>) -> FlowyResult<Vec<FieldMeta>>
@ -285,7 +285,7 @@ impl ClientGridEditor {
match self.pad.read().await.get_field(&changeset.field_id) {
None => {
let msg = format!("Field not found with id: {}", &changeset.field_id);
return Err(FlowyError::internal().context(msg));
Err(FlowyError::internal().context(msg))
}
Some(field_meta) => {
// Update the changeset.data property with the return value.

View File

@ -1,6 +1,7 @@
mod util;
pub mod block_meta_editor;
mod block_meta_manager;
pub mod cell;
pub mod field;
pub mod grid_editor;

View File

@ -1,5 +1,6 @@
use crate::services::row::apply_cell_data_changeset;
use crate::services::field::SelectOptionCellChangeset;
use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::entities::{CellMeta, FieldMeta, RowMeta, DEFAULT_ROW_HEIGHT};
use std::collections::HashMap;
@ -44,6 +45,22 @@ impl<'a> CreateRowMetaBuilder<'a> {
}
}
pub fn add_select_option_cell(&mut self, field_id: &str, data: String) -> FlowyResult<()> {
match self.field_meta_map.get(&field_id.to_owned()) {
None => {
let msg = format!("Invalid field_id: {}", field_id);
Err(FlowyError::internal().context(msg))
}
Some(field_meta) => {
let cell_data = SelectOptionCellChangeset::from_insert(&data).cell_data();
let data = apply_cell_data_changeset(&cell_data, None, field_meta)?;
let cell = CellMeta::new(field_id, data);
self.payload.cell_by_field_id.insert(field_id.to_owned(), cell);
Ok(())
}
}
}
#[allow(dead_code)]
pub fn height(mut self, height: i32) -> Self {
self.payload.height = height;

View File

@ -1,6 +1,6 @@
use flowy_derive::ProtoBuf;
use flowy_error::ErrorCode;
use flowy_grid_data_model::parser::{NotEmptyStr, NotEmptyUuid};
use flowy_grid_data_model::parser::NotEmptyUuid;
#[derive(ProtoBuf, Default)]
pub struct RowIdentifierPayload {

View File

@ -8,16 +8,27 @@ pub fn make_default_grid() -> BuildGridContext {
.visibility(true)
.build();
// single select
let single_select = SingleSelectTypeOptionBuilder::default()
.option(SelectOption::new("Done"))
.option(SelectOption::new("Unknown"))
.option(SelectOption::new("Progress"));
let single_select_field = FieldBuilder::new(single_select).name("Status").visibility(true).build();
//multiple select
let multi_select = MultiSelectTypeOptionBuilder::default()
.option(SelectOption::new("A"))
.option(SelectOption::new("B"))
.option(SelectOption::new("C"));
let multi_select_field = FieldBuilder::new(multi_select)
.name("Alphabet")
.visibility(true)
.build();
GridBuilder::default()
.add_field(text_field)
.add_field(single_select_field)
.add_field(multi_select_field)
.add_empty_row()
.add_empty_row()
.add_empty_row()

View File

@ -2,7 +2,7 @@ use crate::grid::script::EditorScript::*;
use crate::grid::script::*;
use chrono::NaiveDateTime;
use flowy_grid::services::field::{
MultiSelectTypeOption, SelectOption, SingleSelectTypeOption, SELECTION_IDS_SEPARATOR,
MultiSelectTypeOption, SelectOption, SelectOptionCellChangeset, SingleSelectTypeOption, SELECTION_IDS_SEPARATOR,
};
use flowy_grid::services::row::{apply_cell_data_changeset, decode_cell_data, CellDataOperation, CreateRowMetaBuilder};
use flowy_grid_data_model::entities::{
@ -233,37 +233,31 @@ async fn grid_row_add_cells_test() {
for field in &test.field_metas {
match field.field_type {
FieldType::RichText => {
let data = apply_cell_data_changeset("hello world", field).unwrap();
builder.add_cell(&field.id, data).unwrap();
builder.add_cell(&field.id, "hello world".to_owned()).unwrap();
}
FieldType::Number => {
let data = apply_cell_data_changeset("¥18,443", field).unwrap();
builder.add_cell(&field.id, data).unwrap();
builder.add_cell(&field.id, "18,443".to_owned()).unwrap();
}
FieldType::DateTime => {
let data = apply_cell_data_changeset("1647251762", field).unwrap();
builder.add_cell(&field.id, data).unwrap();
builder.add_cell(&field.id, "1647251762".to_owned()).unwrap();
}
FieldType::SingleSelect => {
let type_option = SingleSelectTypeOption::from(field);
let options = type_option.options.first().unwrap();
let data = type_option.apply_changeset(&options.id).unwrap();
builder.add_cell(&field.id, data).unwrap();
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 options = type_option
let ops_ids = type_option
.options
.iter()
.map(|option| option.id.clone())
.collect::<Vec<_>>()
.join(SELECTION_IDS_SEPARATOR);
let data = type_option.apply_changeset(&options).unwrap();
builder.add_cell(&field.id, data).unwrap();
builder.add_select_option_cell(&field.id, ops_ids).unwrap();
}
FieldType::Checkbox => {
let data = apply_cell_data_changeset("false", field).unwrap();
builder.add_cell(&field.id, data).unwrap();
builder.add_cell(&field.id, "false".to_string()).unwrap();
}
}
}
@ -272,61 +266,6 @@ async fn grid_row_add_cells_test() {
test.run_scripts(scripts).await;
}
#[tokio::test]
async fn grid_row_add_selection_cell_test() {
let mut test = GridEditorTest::new().await;
let mut builder = CreateRowMetaBuilder::new(&test.field_metas);
let uuid = uuid::Uuid::new_v4().to_string();
let mut single_select_field_id = "".to_string();
let mut multi_select_field_id = "".to_string();
for field in &test.field_metas {
match field.field_type {
FieldType::SingleSelect => {
single_select_field_id = field.id.clone();
// The element must be parsed as uuid
assert!(builder.add_cell(&field.id, "data".to_owned()).is_err());
// // The data should not be empty
assert!(builder.add_cell(&field.id, "".to_owned()).is_err());
// The element must be parsed as uuid
assert!(builder.add_cell(&field.id, "1,2,3".to_owned()).is_err(),);
// The separator must be comma
assert!(builder.add_cell(&field.id, format!("{}. {}", &uuid, &uuid),).is_err());
//
assert!(builder.add_cell(&field.id, uuid.clone()).is_ok());
assert!(builder.add_cell(&field.id, format!("{}, {}", &uuid, &uuid)).is_ok());
}
FieldType::MultiSelect => {
multi_select_field_id = field.id.clone();
assert!(builder.add_cell(&field.id, format!("{}, {}", &uuid, &uuid)).is_ok());
}
_ => {}
}
}
let context = builder.build();
assert_eq!(
&context
.cell_by_field_id
.get(&single_select_field_id)
.as_ref()
.unwrap()
.data,
&uuid
);
assert_eq!(
context
.cell_by_field_id
.get(&multi_select_field_id)
.as_ref()
.unwrap()
.data,
format!("{},{}", &uuid, &uuid)
);
let scripts = vec![CreateRow { context }];
test.run_scripts(scripts).await;
}
#[tokio::test]
async fn grid_row_add_date_cell_test() {
let mut test = GridEditorTest::new().await;
@ -375,11 +314,11 @@ async fn grid_cell_update() {
FieldType::DateTime => "123".to_string(),
FieldType::SingleSelect => {
let type_option = SingleSelectTypeOption::from(field_meta);
type_option.options.first().unwrap().id.clone()
SelectOptionCellChangeset::from_insert(&type_option.options.first().unwrap().id).cell_data()
}
FieldType::MultiSelect => {
let type_option = MultiSelectTypeOption::from(field_meta);
type_option.options.first().unwrap().id.clone()
SelectOptionCellChangeset::from_insert(&type_option.options.first().unwrap().id).cell_data()
}
FieldType::Checkbox => "1".to_string(),
};
@ -400,8 +339,8 @@ async fn grid_cell_update() {
FieldType::RichText => ("1".to_string().repeat(10001), true),
FieldType::Number => ("abc".to_string(), true),
FieldType::DateTime => ("abc".to_string(), true),
FieldType::SingleSelect => ("abc".to_string(), true),
FieldType::MultiSelect => ("abc".to_string(), true),
FieldType::SingleSelect => (SelectOptionCellChangeset::from_insert("abc").cell_data(), false),
FieldType::MultiSelect => (SelectOptionCellChangeset::from_insert("abc").cell_data(), false),
FieldType::Checkbox => ("2".to_string(), false),
};

View File

@ -3,8 +3,8 @@ use flowy_grid::services::field::*;
use flowy_grid::services::grid_editor::{ClientGridEditor, GridPadBuilder};
use flowy_grid::services::row::CreateRowMetaPayload;
use flowy_grid_data_model::entities::{
BuildGridContext, CellMetaChangeset, CreateFieldParams, Field, FieldChangesetParams, FieldMeta, FieldType,
GridBlockMeta, GridBlockMetaChangeset, RowMeta, RowMetaChangeset, RowOrder, TypeOptionDataEntry,
BuildGridContext, CellMetaChangeset, CreateFieldParams, Field, FieldChangesetParams, FieldMeta, FieldOrder,
FieldType, GridBlockMeta, GridBlockMetaChangeset, RowMeta, RowMetaChangeset, RowOrder, TypeOptionDataEntry,
};
use flowy_revision::REVISION_WRITE_INTERVAL_IN_MILLIS;
use flowy_sync::client_grid::GridBuilder;
@ -89,7 +89,7 @@ impl GridEditorTest {
let view_data: Bytes = build_context.try_into().unwrap();
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_metas = editor.get_field_metas(None).await.unwrap();
let field_metas = editor.get_field_metas::<FieldOrder>(None).await.unwrap();
let grid_blocks = editor.get_block_metas().await.unwrap();
let row_metas = get_row_metas(&editor).await;
@ -125,12 +125,12 @@ impl GridEditorTest {
}
self.editor.create_field(params).await.unwrap();
self.field_metas = self.editor.get_field_metas(None).await.unwrap();
self.field_metas = self.editor.get_field_metas::<FieldOrder>(None).await.unwrap();
assert_eq!(self.field_count, self.field_metas.len());
}
EditorScript::UpdateField { changeset: change } => {
self.editor.update_field(change).await.unwrap();
self.field_metas = self.editor.get_field_metas(None).await.unwrap();
self.field_metas = self.editor.get_field_metas::<FieldOrder>(None).await.unwrap();
}
EditorScript::DeleteField { field_meta } => {
if self.editor.contain_field(&field_meta.id).await {
@ -138,17 +138,20 @@ impl GridEditorTest {
}
self.editor.delete_field(&field_meta.id).await.unwrap();
self.field_metas = self.editor.get_field_metas(None).await.unwrap();
self.field_metas = self.editor.get_field_metas::<FieldOrder>(None).await.unwrap();
assert_eq!(self.field_count, self.field_metas.len());
}
EditorScript::AssertFieldCount(count) => {
assert_eq!(self.editor.get_field_metas(None).await.unwrap().len(), count);
assert_eq!(
self.editor.get_field_metas::<FieldOrder>(None).await.unwrap().len(),
count
);
}
EditorScript::AssertFieldEqual {
field_index,
field_meta,
} => {
let field_metas = self.editor.get_field_metas(None).await.unwrap();
let field_metas = self.editor.get_field_metas::<FieldOrder>(None).await.unwrap();
assert_eq!(field_metas[field_index].clone(), field_meta);
}
EditorScript::CreateBlock { block } => {

View File

@ -1,5 +1,5 @@
use crate::entities::{FieldMeta, FieldType, RowMeta};
use crate::parser::{NotEmptyStr, NotEmptyUuid};
use crate::parser::NotEmptyUuid;
use flowy_derive::ProtoBuf;
use flowy_error_code::ErrorCode;
use std::collections::HashMap;
@ -422,6 +422,7 @@ pub struct CreateFieldPayload {
pub start_field_id: Option<String>,
}
#[derive(Clone)]
pub struct CreateFieldParams {
pub grid_id: String,
pub field: Field,

View File

@ -396,7 +396,7 @@ impl std::convert::From<CellMetaChangeset> for RowMetaChangeset {
let field_id = changeset.field_id;
let cell_meta = CellMeta {
field_id: field_id.clone(),
data: changeset.data.unwrap_or("".to_owned()),
data: changeset.data.unwrap_or_else(|| "".to_owned()),
};
cell_by_field_id.insert(field_id, cell_meta);

View File

@ -4,8 +4,5 @@
mod grid;
pub use grid::*;
mod type_option;
pub use type_option::*;
mod meta;
pub use meta::*;

View File

@ -1,978 +0,0 @@
// This file is generated by rust-protobuf 2.25.2. Do not edit
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
#![allow(unknown_lints)]
#![allow(clippy::all)]
#![allow(unused_attributes)]
#![cfg_attr(rustfmt, rustfmt::skip)]
#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(trivial_casts)]
#![allow(unused_imports)]
#![allow(unused_results)]
//! Generated file from `type_option.proto`
/// Generated files are compatible only with the same version
/// of protobuf runtime.
// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_25_2;
#[derive(PartialEq,Clone,Default)]
pub struct SelectOption {
// message fields
pub id: ::std::string::String,
pub name: ::std::string::String,
pub color: SelectOptionColor,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a SelectOption {
fn default() -> &'a SelectOption {
<SelectOption as ::protobuf::Message>::default_instance()
}
}
impl SelectOption {
pub fn new() -> SelectOption {
::std::default::Default::default()
}
// string id = 1;
pub fn get_id(&self) -> &str {
&self.id
}
pub fn clear_id(&mut self) {
self.id.clear();
}
// Param is passed by value, moved
pub fn set_id(&mut self, v: ::std::string::String) {
self.id = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_id(&mut self) -> &mut ::std::string::String {
&mut self.id
}
// Take field
pub fn take_id(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.id, ::std::string::String::new())
}
// string name = 2;
pub fn get_name(&self) -> &str {
&self.name
}
pub fn clear_name(&mut self) {
self.name.clear();
}
// Param is passed by value, moved
pub fn set_name(&mut self, v: ::std::string::String) {
self.name = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_name(&mut self) -> &mut ::std::string::String {
&mut self.name
}
// Take field
pub fn take_name(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.name, ::std::string::String::new())
}
// .SelectOptionColor color = 3;
pub fn get_color(&self) -> SelectOptionColor {
self.color
}
pub fn clear_color(&mut self) {
self.color = SelectOptionColor::Purple;
}
// Param is passed by value, moved
pub fn set_color(&mut self, v: SelectOptionColor) {
self.color = v;
}
}
impl ::protobuf::Message for SelectOption {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.id)?;
},
2 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.name)?;
},
3 => {
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.color, 3, &mut self.unknown_fields)?
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if !self.id.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.id);
}
if !self.name.is_empty() {
my_size += ::protobuf::rt::string_size(2, &self.name);
}
if self.color != SelectOptionColor::Purple {
my_size += ::protobuf::rt::enum_size(3, self.color);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
if !self.id.is_empty() {
os.write_string(1, &self.id)?;
}
if !self.name.is_empty() {
os.write_string(2, &self.name)?;
}
if self.color != SelectOptionColor::Purple {
os.write_enum(3, ::protobuf::ProtobufEnum::value(&self.color))?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &dyn (::std::any::Any) {
self as &dyn (::std::any::Any)
}
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
self as &mut dyn (::std::any::Any)
}
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> SelectOption {
SelectOption::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"id",
|m: &SelectOption| { &m.id },
|m: &mut SelectOption| { &mut m.id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"name",
|m: &SelectOption| { &m.name },
|m: &mut SelectOption| { &mut m.name },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<SelectOptionColor>>(
"color",
|m: &SelectOption| { &m.color },
|m: &mut SelectOption| { &mut m.color },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<SelectOption>(
"SelectOption",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static SelectOption {
static instance: ::protobuf::rt::LazyV2<SelectOption> = ::protobuf::rt::LazyV2::INIT;
instance.get(SelectOption::new)
}
}
impl ::protobuf::Clear for SelectOption {
fn clear(&mut self) {
self.id.clear();
self.name.clear();
self.color = SelectOptionColor::Purple;
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for SelectOption {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for SelectOption {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(PartialEq,Clone,Default)]
pub struct SelectOptionCellChangesetPayload {
// message fields
pub grid_id: ::std::string::String,
pub row_id: ::std::string::String,
pub field_id: ::std::string::String,
// message oneof groups
pub one_of_insert_option_id: ::std::option::Option<SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id>,
pub one_of_delete_option_id: ::std::option::Option<SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id>,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a SelectOptionCellChangesetPayload {
fn default() -> &'a SelectOptionCellChangesetPayload {
<SelectOptionCellChangesetPayload as ::protobuf::Message>::default_instance()
}
}
#[derive(Clone,PartialEq,Debug)]
pub enum SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id {
insert_option_id(::std::string::String),
}
#[derive(Clone,PartialEq,Debug)]
pub enum SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id {
delete_option_id(::std::string::String),
}
impl SelectOptionCellChangesetPayload {
pub fn new() -> SelectOptionCellChangesetPayload {
::std::default::Default::default()
}
// string grid_id = 1;
pub fn get_grid_id(&self) -> &str {
&self.grid_id
}
pub fn clear_grid_id(&mut self) {
self.grid_id.clear();
}
// Param is passed by value, moved
pub fn set_grid_id(&mut self, v: ::std::string::String) {
self.grid_id = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_grid_id(&mut self) -> &mut ::std::string::String {
&mut self.grid_id
}
// Take field
pub fn take_grid_id(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.grid_id, ::std::string::String::new())
}
// string row_id = 2;
pub fn get_row_id(&self) -> &str {
&self.row_id
}
pub fn clear_row_id(&mut self) {
self.row_id.clear();
}
// Param is passed by value, moved
pub fn set_row_id(&mut self, v: ::std::string::String) {
self.row_id = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_row_id(&mut self) -> &mut ::std::string::String {
&mut self.row_id
}
// Take field
pub fn take_row_id(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.row_id, ::std::string::String::new())
}
// string field_id = 3;
pub fn get_field_id(&self) -> &str {
&self.field_id
}
pub fn clear_field_id(&mut self) {
self.field_id.clear();
}
// Param is passed by value, moved
pub fn set_field_id(&mut self, v: ::std::string::String) {
self.field_id = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_field_id(&mut self) -> &mut ::std::string::String {
&mut self.field_id
}
// Take field
pub fn take_field_id(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.field_id, ::std::string::String::new())
}
// string insert_option_id = 4;
pub fn get_insert_option_id(&self) -> &str {
match self.one_of_insert_option_id {
::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref v)) => v,
_ => "",
}
}
pub fn clear_insert_option_id(&mut self) {
self.one_of_insert_option_id = ::std::option::Option::None;
}
pub fn has_insert_option_id(&self) -> bool {
match self.one_of_insert_option_id {
::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(..)) => true,
_ => false,
}
}
// Param is passed by value, moved
pub fn set_insert_option_id(&mut self, v: ::std::string::String) {
self.one_of_insert_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(v))
}
// Mutable pointer to the field.
pub fn mut_insert_option_id(&mut self) -> &mut ::std::string::String {
if let ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(_)) = self.one_of_insert_option_id {
} else {
self.one_of_insert_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(::std::string::String::new()));
}
match self.one_of_insert_option_id {
::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref mut v)) => v,
_ => panic!(),
}
}
// Take field
pub fn take_insert_option_id(&mut self) -> ::std::string::String {
if self.has_insert_option_id() {
match self.one_of_insert_option_id.take() {
::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(v)) => v,
_ => panic!(),
}
} else {
::std::string::String::new()
}
}
// string delete_option_id = 5;
pub fn get_delete_option_id(&self) -> &str {
match self.one_of_delete_option_id {
::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref v)) => v,
_ => "",
}
}
pub fn clear_delete_option_id(&mut self) {
self.one_of_delete_option_id = ::std::option::Option::None;
}
pub fn has_delete_option_id(&self) -> bool {
match self.one_of_delete_option_id {
::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(..)) => true,
_ => false,
}
}
// Param is passed by value, moved
pub fn set_delete_option_id(&mut self, v: ::std::string::String) {
self.one_of_delete_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(v))
}
// Mutable pointer to the field.
pub fn mut_delete_option_id(&mut self) -> &mut ::std::string::String {
if let ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(_)) = self.one_of_delete_option_id {
} else {
self.one_of_delete_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(::std::string::String::new()));
}
match self.one_of_delete_option_id {
::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref mut v)) => v,
_ => panic!(),
}
}
// Take field
pub fn take_delete_option_id(&mut self) -> ::std::string::String {
if self.has_delete_option_id() {
match self.one_of_delete_option_id.take() {
::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(v)) => v,
_ => panic!(),
}
} else {
::std::string::String::new()
}
}
}
impl ::protobuf::Message for SelectOptionCellChangesetPayload {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.grid_id)?;
},
2 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.row_id)?;
},
3 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.field_id)?;
},
4 => {
if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
}
self.one_of_insert_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(is.read_string()?));
},
5 => {
if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
}
self.one_of_delete_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(is.read_string()?));
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if !self.grid_id.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.grid_id);
}
if !self.row_id.is_empty() {
my_size += ::protobuf::rt::string_size(2, &self.row_id);
}
if !self.field_id.is_empty() {
my_size += ::protobuf::rt::string_size(3, &self.field_id);
}
if let ::std::option::Option::Some(ref v) = self.one_of_insert_option_id {
match v {
&SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref v) => {
my_size += ::protobuf::rt::string_size(4, &v);
},
};
}
if let ::std::option::Option::Some(ref v) = self.one_of_delete_option_id {
match v {
&SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref v) => {
my_size += ::protobuf::rt::string_size(5, &v);
},
};
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
if !self.grid_id.is_empty() {
os.write_string(1, &self.grid_id)?;
}
if !self.row_id.is_empty() {
os.write_string(2, &self.row_id)?;
}
if !self.field_id.is_empty() {
os.write_string(3, &self.field_id)?;
}
if let ::std::option::Option::Some(ref v) = self.one_of_insert_option_id {
match v {
&SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref v) => {
os.write_string(4, v)?;
},
};
}
if let ::std::option::Option::Some(ref v) = self.one_of_delete_option_id {
match v {
&SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref v) => {
os.write_string(5, v)?;
},
};
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &dyn (::std::any::Any) {
self as &dyn (::std::any::Any)
}
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
self as &mut dyn (::std::any::Any)
}
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> SelectOptionCellChangesetPayload {
SelectOptionCellChangesetPayload::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"grid_id",
|m: &SelectOptionCellChangesetPayload| { &m.grid_id },
|m: &mut SelectOptionCellChangesetPayload| { &mut m.grid_id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"row_id",
|m: &SelectOptionCellChangesetPayload| { &m.row_id },
|m: &mut SelectOptionCellChangesetPayload| { &mut m.row_id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"field_id",
|m: &SelectOptionCellChangesetPayload| { &m.field_id },
|m: &mut SelectOptionCellChangesetPayload| { &mut m.field_id },
));
fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
"insert_option_id",
SelectOptionCellChangesetPayload::has_insert_option_id,
SelectOptionCellChangesetPayload::get_insert_option_id,
));
fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
"delete_option_id",
SelectOptionCellChangesetPayload::has_delete_option_id,
SelectOptionCellChangesetPayload::get_delete_option_id,
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<SelectOptionCellChangesetPayload>(
"SelectOptionCellChangesetPayload",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static SelectOptionCellChangesetPayload {
static instance: ::protobuf::rt::LazyV2<SelectOptionCellChangesetPayload> = ::protobuf::rt::LazyV2::INIT;
instance.get(SelectOptionCellChangesetPayload::new)
}
}
impl ::protobuf::Clear for SelectOptionCellChangesetPayload {
fn clear(&mut self) {
self.grid_id.clear();
self.row_id.clear();
self.field_id.clear();
self.one_of_insert_option_id = ::std::option::Option::None;
self.one_of_delete_option_id = ::std::option::Option::None;
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for SelectOptionCellChangesetPayload {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for SelectOptionCellChangesetPayload {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(PartialEq,Clone,Default)]
pub struct SelectOptionContext {
// message fields
pub options: ::protobuf::RepeatedField<SelectOption>,
pub select_options: ::protobuf::RepeatedField<SelectOption>,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a SelectOptionContext {
fn default() -> &'a SelectOptionContext {
<SelectOptionContext as ::protobuf::Message>::default_instance()
}
}
impl SelectOptionContext {
pub fn new() -> SelectOptionContext {
::std::default::Default::default()
}
// repeated .SelectOption options = 1;
pub fn get_options(&self) -> &[SelectOption] {
&self.options
}
pub fn clear_options(&mut self) {
self.options.clear();
}
// Param is passed by value, moved
pub fn set_options(&mut self, v: ::protobuf::RepeatedField<SelectOption>) {
self.options = v;
}
// Mutable pointer to the field.
pub fn mut_options(&mut self) -> &mut ::protobuf::RepeatedField<SelectOption> {
&mut self.options
}
// Take field
pub fn take_options(&mut self) -> ::protobuf::RepeatedField<SelectOption> {
::std::mem::replace(&mut self.options, ::protobuf::RepeatedField::new())
}
// repeated .SelectOption select_options = 2;
pub fn get_select_options(&self) -> &[SelectOption] {
&self.select_options
}
pub fn clear_select_options(&mut self) {
self.select_options.clear();
}
// Param is passed by value, moved
pub fn set_select_options(&mut self, v: ::protobuf::RepeatedField<SelectOption>) {
self.select_options = v;
}
// Mutable pointer to the field.
pub fn mut_select_options(&mut self) -> &mut ::protobuf::RepeatedField<SelectOption> {
&mut self.select_options
}
// Take field
pub fn take_select_options(&mut self) -> ::protobuf::RepeatedField<SelectOption> {
::std::mem::replace(&mut self.select_options, ::protobuf::RepeatedField::new())
}
}
impl ::protobuf::Message for SelectOptionContext {
fn is_initialized(&self) -> bool {
for v in &self.options {
if !v.is_initialized() {
return false;
}
};
for v in &self.select_options {
if !v.is_initialized() {
return false;
}
};
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.options)?;
},
2 => {
::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.select_options)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
for value in &self.options {
let len = value.compute_size();
my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
};
for value in &self.select_options {
let len = value.compute_size();
my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
};
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
for v in &self.options {
os.write_tag(1, ::protobuf::wire_format::WireTypeLengthDelimited)?;
os.write_raw_varint32(v.get_cached_size())?;
v.write_to_with_cached_sizes(os)?;
};
for v in &self.select_options {
os.write_tag(2, ::protobuf::wire_format::WireTypeLengthDelimited)?;
os.write_raw_varint32(v.get_cached_size())?;
v.write_to_with_cached_sizes(os)?;
};
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &dyn (::std::any::Any) {
self as &dyn (::std::any::Any)
}
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
self as &mut dyn (::std::any::Any)
}
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> SelectOptionContext {
SelectOptionContext::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<SelectOption>>(
"options",
|m: &SelectOptionContext| { &m.options },
|m: &mut SelectOptionContext| { &mut m.options },
));
fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<SelectOption>>(
"select_options",
|m: &SelectOptionContext| { &m.select_options },
|m: &mut SelectOptionContext| { &mut m.select_options },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<SelectOptionContext>(
"SelectOptionContext",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static SelectOptionContext {
static instance: ::protobuf::rt::LazyV2<SelectOptionContext> = ::protobuf::rt::LazyV2::INIT;
instance.get(SelectOptionContext::new)
}
}
impl ::protobuf::Clear for SelectOptionContext {
fn clear(&mut self) {
self.options.clear();
self.select_options.clear();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for SelectOptionContext {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for SelectOptionContext {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum SelectOptionColor {
Purple = 0,
Pink = 1,
LightPink = 2,
Orange = 3,
Yellow = 4,
Lime = 5,
Green = 6,
Aqua = 7,
Blue = 8,
}
impl ::protobuf::ProtobufEnum for SelectOptionColor {
fn value(&self) -> i32 {
*self as i32
}
fn from_i32(value: i32) -> ::std::option::Option<SelectOptionColor> {
match value {
0 => ::std::option::Option::Some(SelectOptionColor::Purple),
1 => ::std::option::Option::Some(SelectOptionColor::Pink),
2 => ::std::option::Option::Some(SelectOptionColor::LightPink),
3 => ::std::option::Option::Some(SelectOptionColor::Orange),
4 => ::std::option::Option::Some(SelectOptionColor::Yellow),
5 => ::std::option::Option::Some(SelectOptionColor::Lime),
6 => ::std::option::Option::Some(SelectOptionColor::Green),
7 => ::std::option::Option::Some(SelectOptionColor::Aqua),
8 => ::std::option::Option::Some(SelectOptionColor::Blue),
_ => ::std::option::Option::None
}
}
fn values() -> &'static [Self] {
static values: &'static [SelectOptionColor] = &[
SelectOptionColor::Purple,
SelectOptionColor::Pink,
SelectOptionColor::LightPink,
SelectOptionColor::Orange,
SelectOptionColor::Yellow,
SelectOptionColor::Lime,
SelectOptionColor::Green,
SelectOptionColor::Aqua,
SelectOptionColor::Blue,
];
values
}
fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
::protobuf::reflect::EnumDescriptor::new_pb_name::<SelectOptionColor>("SelectOptionColor", file_descriptor_proto())
})
}
}
impl ::std::marker::Copy for SelectOptionColor {
}
impl ::std::default::Default for SelectOptionColor {
fn default() -> Self {
SelectOptionColor::Purple
}
}
impl ::protobuf::reflect::ProtobufValue for SelectOptionColor {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
}
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x11type_option.proto\"\\\n\x0cSelectOption\x12\x0e\n\x02id\x18\x01\
\x20\x01(\tR\x02id\x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\x12(\n\
\x05color\x18\x03\x20\x01(\x0e2\x12.SelectOptionColorR\x05color\"\xfb\
\x01\n\x20SelectOptionCellChangesetPayload\x12\x17\n\x07grid_id\x18\x01\
\x20\x01(\tR\x06gridId\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\
\x12\x19\n\x08field_id\x18\x03\x20\x01(\tR\x07fieldId\x12*\n\x10insert_o\
ption_id\x18\x04\x20\x01(\tH\0R\x0einsertOptionId\x12*\n\x10delete_optio\
n_id\x18\x05\x20\x01(\tH\x01R\x0edeleteOptionIdB\x19\n\x17one_of_insert_\
option_idB\x19\n\x17one_of_delete_option_id\"t\n\x13SelectOptionContext\
\x12'\n\x07options\x18\x01\x20\x03(\x0b2\r.SelectOptionR\x07options\x124\
\n\x0eselect_options\x18\x02\x20\x03(\x0b2\r.SelectOptionR\rselectOption\
s*y\n\x11SelectOptionColor\x12\n\n\x06Purple\x10\0\x12\x08\n\x04Pink\x10\
\x01\x12\r\n\tLightPink\x10\x02\x12\n\n\x06Orange\x10\x03\x12\n\n\x06Yel\
low\x10\x04\x12\x08\n\x04Lime\x10\x05\x12\t\n\x05Green\x10\x06\x12\x08\n\
\x04Aqua\x10\x07\x12\x08\n\x04Blue\x10\x08b\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
}
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
file_descriptor_proto_lazy.get(|| {
parse_descriptor_proto()
})
}

View File

@ -1,29 +0,0 @@
syntax = "proto3";
message SelectOption {
string id = 1;
string name = 2;
SelectOptionColor color = 3;
}
message SelectOptionCellChangesetPayload {
string grid_id = 1;
string row_id = 2;
string field_id = 3;
oneof one_of_insert_option_id { string insert_option_id = 4; };
oneof one_of_delete_option_id { string delete_option_id = 5; };
}
message SelectOptionContext {
repeated SelectOption options = 1;
repeated SelectOption select_options = 2;
}
enum SelectOptionColor {
Purple = 0;
Pink = 1;
LightPink = 2;
Orange = 3;
Yellow = 4;
Lime = 5;
Green = 6;
Aqua = 7;
Blue = 8;
}