chore: add more test and fix warnings

This commit is contained in:
appflowy 2022-03-16 10:02:37 +08:00
parent 47081f3095
commit b3ed254caf
24 changed files with 318 additions and 132 deletions

View File

@ -64,6 +64,8 @@ impl FlowyError {
static_flowy_error!(name_empty, ErrorCode::UserNameIsEmpty);
static_flowy_error!(user_id, ErrorCode::UserIdInvalid);
static_flowy_error!(user_not_exist, ErrorCode::UserNotExist);
static_flowy_error!(text_too_long, ErrorCode::TextTooLong);
static_flowy_error!(invalid_data, ErrorCode::InvalidData);
}
impl std::convert::From<ErrorCode> for FlowyError {

View File

@ -351,7 +351,7 @@ pub async fn create_view(sdk: &FlowySDKTest, app_id: &str, name: &str, desc: &st
thumbnail: None,
data_type,
plugin_type: 0,
data: "".to_string(),
data: vec![],
};
let view = FolderEventBuilder::new(sdk.clone())
.event(CreateView)

View File

@ -36,11 +36,7 @@ pub(crate) async fn get_fields_handler(
let payload: QueryFieldPayload = data.into_inner();
let editor = manager.get_grid_editor(&payload.grid_id)?;
let field_metas = editor.get_field_metas(Some(payload.field_orders)).await?;
let repeated_field: RepeatedField = field_metas
.into_iter()
.map(|field_meta| Field::from(field_meta))
.collect::<Vec<_>>()
.into();
let repeated_field: RepeatedField = field_metas.into_iter().map(Field::from).collect::<Vec<_>>().into();
data_result(repeated_field)
}

View File

@ -111,8 +111,7 @@ impl GridManager {
) -> Result<Arc<ClientGridEditor>, FlowyError> {
let user = self.grid_user.clone();
let rev_manager = self.make_grid_rev_manager(grid_id, pool.clone())?;
let kv_persistence = self.get_kv_persistence()?;
let grid_editor = ClientGridEditor::new(grid_id, user, rev_manager, kv_persistence).await?;
let grid_editor = ClientGridEditor::new(grid_id, user, rev_manager).await?;
Ok(grid_editor)
}
@ -137,6 +136,7 @@ impl GridManager {
Ok(rev_manager)
}
#[allow(dead_code)]
fn get_kv_persistence(&self) -> FlowyResult<Arc<GridKVPersistence>> {
let read_guard = self.kv_persistence.read();
if read_guard.is_some() {
@ -198,7 +198,7 @@ pub async fn make_grid_view_data(
let grid_block_meta_delta = make_block_meta_delta(&build_context.grid_block_meta);
let block_meta_delta_data = grid_block_meta_delta.to_delta_bytes();
let repeated_revision: RepeatedRevision =
Revision::initial_revision(&user_id, &block_id, block_meta_delta_data).into();
Revision::initial_revision(user_id, &block_id, block_meta_delta_data).into();
let _ = grid_manager
.create_grid_block_meta(&block_id, repeated_revision)
.await?;

View File

@ -139,7 +139,7 @@ impl GridBlockMetaEditorManager {
for grid_block in grid_blocks {
let editor = self.get_editor(&grid_block.id).await?;
let row_metas = editor.get_row_metas(None).await?;
let block_row_orders = row_metas.iter().map(|row_meta| RowOrder::from(row_meta));
let block_row_orders = row_metas.iter().map(RowOrder::from);
row_orders.extend(block_row_orders);
}
Ok(row_orders)
@ -257,7 +257,7 @@ impl ClientGridBlockMetaEditor {
.await
.get_rows(None)?
.iter()
.map(|row_meta| RowOrder::from(row_meta))
.map(RowOrder::from)
.collect::<Vec<RowOrder>>();
Ok(row_orders)
}

View File

@ -53,14 +53,11 @@ impl CellDataSerde for DateDescription {
}
fn serialize_cell_data(&self, data: &str) -> Result<String, FlowyError> {
let timestamp = match data.parse::<i64>() {
Ok(timestamp) => timestamp,
Err(e) => {
tracing::error!("Parse {} to i64 failed: {}", data, e);
chrono::Utc::now().timestamp()
}
if let Err(e) = data.parse::<i64>() {
tracing::error!("Parse {} to i64 failed: {}", data, e);
return Err(FlowyError::internal().context(e));
};
Ok(format!("{}", timestamp))
Ok(data.to_owned())
}
}

View File

@ -93,7 +93,7 @@ impl NumberDescription {
}
fn decimal_from_str(&self, s: &str) -> Decimal {
let mut decimal = Decimal::from_str(s).unwrap_or(Decimal::zero());
let mut decimal = Decimal::from_str(s).unwrap_or_else(|_| Decimal::zero());
match decimal.set_scale(self.scale) {
Ok(_) => {}
Err(e) => {
@ -130,7 +130,12 @@ impl CellDataSerde for NumberDescription {
}
fn serialize_cell_data(&self, data: &str) -> Result<String, FlowyError> {
Ok(self.strip_symbol(data))
let data = self.strip_symbol(data);
if !data.chars().all(char::is_numeric) {
return Err(FlowyError::invalid_data().context("Should only contain numbers"));
}
Ok(data)
}
}
@ -188,8 +193,10 @@ mod tests {
#[test]
fn number_description_scale_test() {
let mut description = NumberDescription::default();
description.scale = 1;
let mut description = NumberDescription {
scale: 1,
..Default::default()
};
for format in NumberFormat::iter() {
description.format = format;
@ -224,8 +231,10 @@ mod tests {
#[test]
fn number_description_sign_test() {
let mut description = NumberDescription::default();
description.sign_positive = false;
let mut description = NumberDescription {
sign_positive: false,
..Default::default()
};
for format in NumberFormat::iter() {
description.format = format;

View File

@ -2,9 +2,13 @@ use crate::impl_from_and_to_type_option;
use crate::services::row::CellDataSerde;
use crate::services::util::*;
use flowy_derive::ProtoBuf;
use flowy_error::FlowyError;
use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::entities::{FieldMeta, FieldType};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
pub const SELECTION_IDS_SEPARATOR: &str = ",";
// Single select
#[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)]
@ -23,7 +27,7 @@ impl CellDataSerde for SingleSelectDescription {
}
fn serialize_cell_data(&self, data: &str) -> Result<String, FlowyError> {
Ok(select_option_id_from_data(data.to_owned(), true))
single_select_option_id_from_data(data.to_owned())
}
}
@ -43,20 +47,45 @@ impl CellDataSerde for MultiSelectDescription {
}
fn serialize_cell_data(&self, data: &str) -> Result<String, FlowyError> {
Ok(select_option_id_from_data(data.to_owned(), false))
multi_select_option_id_from_data(data.to_owned())
}
}
fn select_option_id_from_data(data: String, is_single_select: bool) -> String {
if !is_single_select {
return data;
}
let select_option_ids = data.split(',').collect::<Vec<&str>>();
fn single_select_option_id_from_data(data: String) -> FlowyResult<String> {
let select_option_ids = select_option_ids(data)?;
if select_option_ids.is_empty() {
return "".to_owned();
return Ok("".to_owned());
}
select_option_ids.split_first().unwrap().0.to_string()
Ok(select_option_ids.split_first().unwrap().0.to_string())
}
fn multi_select_option_id_from_data(data: String) -> FlowyResult<String> {
let select_option_ids = select_option_ids(data)?;
Ok(select_option_ids.join(SELECTION_IDS_SEPARATOR))
}
fn select_option_ids(mut data: String) -> FlowyResult<Vec<String>> {
data.retain(|c| !c.is_whitespace());
let select_option_ids = data.split(SELECTION_IDS_SEPARATOR).collect::<Vec<&str>>();
if select_option_ids
.par_iter()
.find_first(|option_id| match Uuid::parse_str(option_id) {
Ok(_) => false,
Err(e) => {
tracing::error!("{}", e);
true
}
})
.is_some()
{
let msg = format!(
"Invalid selection id string: {}. It should consist of the uuid string and separated by comma",
data
);
return Err(FlowyError::internal().context(msg));
}
Ok(select_option_ids.iter().map(|id| id.to_string()).collect::<Vec<_>>())
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)]

View File

@ -19,6 +19,11 @@ impl CellDataSerde for RichTextDescription {
}
fn serialize_cell_data(&self, data: &str) -> Result<String, FlowyError> {
Ok(data.to_owned())
let data = data.to_owned();
if data.len() > 10000 {
Err(FlowyError::text_too_long().context("The len of the text should not be more than 10000"))
} else {
Ok(data)
}
}
}

View File

@ -1,19 +1,19 @@
use crate::manager::GridUser;
use crate::services::block_meta_editor::GridBlockMetaEditorManager;
use crate::services::kv_persistence::{GridKVPersistence, KVTransaction};
use bytes::Bytes;
use flowy_collaboration::client_grid::{GridChange, GridMetaPad};
use flowy_collaboration::entities::revision::Revision;
use flowy_collaboration::util::make_delta_from_revisions;
use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::entities::{
CellMetaChangeset, Field, FieldChangeset, FieldMeta, Grid, GridBlock, GridBlockChangeset, RepeatedFieldOrder,
CellMetaChangeset, FieldChangeset, FieldMeta, Grid, GridBlock, GridBlockChangeset, RepeatedFieldOrder,
RepeatedRowOrder, Row, RowMeta, RowMetaChangeset,
};
use std::collections::HashMap;
use crate::services::row::{
make_row_by_row_id, make_rows, row_meta_from_context, CreateRowContext, CreateRowContextBuilder,
make_row_by_row_id, make_rows, row_meta_from_context, serialize_cell_data, CreateRowContext,
CreateRowContextBuilder,
};
use flowy_sync::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder};
use lib_infra::future::FutureResult;
@ -27,7 +27,6 @@ pub struct ClientGridEditor {
grid_meta_pad: Arc<RwLock<GridMetaPad>>,
rev_manager: Arc<RevisionManager>,
block_meta_manager: Arc<GridBlockMetaEditorManager>,
kv_persistence: Arc<GridKVPersistence>,
}
impl ClientGridEditor {
@ -35,7 +34,6 @@ impl ClientGridEditor {
grid_id: &str,
user: Arc<dyn GridUser>,
mut rev_manager: RevisionManager,
kv_persistence: Arc<GridKVPersistence>,
) -> FlowyResult<Arc<Self>> {
let token = user.token()?;
let cloud = Arc::new(GridRevisionCloudService { token });
@ -52,7 +50,6 @@ impl ClientGridEditor {
grid_meta_pad,
rev_manager,
block_meta_manager,
kv_persistence,
}))
}
@ -88,7 +85,8 @@ impl ClientGridEditor {
pub async fn create_row(&self) -> FlowyResult<()> {
let field_metas = self.grid_meta_pad.read().await.get_field_metas(None)?;
let block_id = self.last_block_id().await?;
let row = row_meta_from_context(&block_id, CreateRowContextBuilder::new(&field_metas).build());
let create_row_ctx = CreateRowContextBuilder::new(&field_metas).build();
let row = row_meta_from_context(&block_id, create_row_ctx);
let row_count = self.block_meta_manager.create_row(row).await?;
let changeset = GridBlockChangeset::from_row_count(&block_id, row_count);
let _ = self.update_block(changeset).await?;
@ -98,12 +96,11 @@ impl ClientGridEditor {
pub async fn insert_rows(&self, contexts: Vec<CreateRowContext>) -> FlowyResult<()> {
let block_id = self.last_block_id().await?;
let mut rows_by_block_id: HashMap<String, Vec<RowMeta>> = HashMap::new();
for ctx in contexts {
let row_meta = row_meta_from_context(&block_id, ctx);
rows_by_block_id
.entry(block_id.clone())
.or_insert(Vec::new())
.or_insert_with(Vec::new)
.push(row_meta);
}
let changesets = self.block_meta_manager.insert_row(rows_by_block_id).await?;
@ -118,6 +115,18 @@ impl ClientGridEditor {
}
pub async fn update_cell(&self, changeset: CellMetaChangeset) -> FlowyResult<()> {
if let Some(cell_data) = changeset.data.as_ref() {
match self.grid_meta_pad.read().await.get_field(&changeset.field_id) {
None => {
return Err(FlowyError::internal()
.context(format!("Can not find the field with id: {}", &changeset.field_id)));
}
Some(field_meta) => {
let _ = serialize_cell_data(cell_data, field_meta)?;
}
}
}
let row_changeset: RowMetaChangeset = changeset.into();
self.update_row(row_changeset).await
}

View File

@ -1,28 +1,43 @@
use crate::services::row::serialize_cell_data;
use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::entities::{CellMeta, FieldMeta, RowMeta, DEFAULT_ROW_HEIGHT};
use std::collections::HashMap;
pub struct CreateRowContextBuilder<'a> {
#[allow(dead_code)]
fields: &'a [FieldMeta],
field_meta_map: HashMap<&'a String, &'a FieldMeta>,
ctx: CreateRowContext,
}
impl<'a> CreateRowContextBuilder<'a> {
pub fn new(fields: &'a [FieldMeta]) -> Self {
let field_meta_map = fields
.iter()
.map(|field| (&field.id, field))
.collect::<HashMap<&String, &FieldMeta>>();
let ctx = CreateRowContext {
row_id: uuid::Uuid::new_v4().to_string(),
cell_by_field_id: Default::default(),
height: DEFAULT_ROW_HEIGHT,
visibility: true,
};
Self { fields, ctx }
Self { field_meta_map, ctx }
}
#[allow(dead_code)]
pub fn add_cell(mut self, field_id: &str, data: String) -> Self {
let cell = CellMeta::new(field_id, data);
self.ctx.cell_by_field_id.insert(field_id.to_owned(), cell);
self
pub fn add_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 data = serialize_cell_data(&data, field_meta)?;
let cell = CellMeta::new(field_id, data);
self.ctx.cell_by_field_id.insert(field_id.to_owned(), cell);
Ok(())
}
}
}
#[allow(dead_code)]

View File

@ -1,8 +1,11 @@
use crate::grid::script::EditorScript::*;
use crate::grid::script::*;
use chrono::NaiveDateTime;
use flowy_grid::services::cell::*;
use flowy_grid::services::row::{deserialize_cell_data, serialize_cell_data, CellDataSerde, CreateRowContextBuilder};
use flowy_grid_data_model::entities::{FieldChangeset, FieldType, GridBlock, GridBlockChangeset, RowMetaChangeset};
use flowy_grid_data_model::entities::{
CellMetaChangeset, FieldChangeset, FieldType, GridBlock, GridBlockChangeset, RowMetaChangeset,
};
#[tokio::test]
async fn grid_create_field() {
@ -237,29 +240,28 @@ async fn grid_delete_row() {
}
#[tokio::test]
async fn grid_update_cell() {
async fn grid_row_add_cells_test() {
let mut test = GridEditorTest::new().await;
let mut builder = CreateRowContextBuilder::new(&test.field_metas);
for field in &test.field_metas {
match field.field_type {
FieldType::RichText => {
let data = serialize_cell_data("hello world", field).unwrap();
builder = builder.add_cell(&field.id, data);
builder.add_cell(&field.id, data).unwrap();
}
FieldType::Number => {
let data = serialize_cell_data("¥18,443", field).unwrap();
builder = builder.add_cell(&field.id, data);
builder.add_cell(&field.id, data).unwrap();
}
FieldType::DateTime => {
let data = serialize_cell_data("1647251762", field).unwrap();
builder = builder.add_cell(&field.id, data);
builder.add_cell(&field.id, data).unwrap();
}
FieldType::SingleSelect => {
let description = SingleSelectDescription::from(field);
let options = description.options.first().unwrap();
let data = description.serialize_cell_data(&options.id).unwrap();
builder = builder.add_cell(&field.id, data);
builder.add_cell(&field.id, data).unwrap();
}
FieldType::MultiSelect => {
let description = MultiSelectDescription::from(field);
@ -268,17 +270,161 @@ async fn grid_update_cell() {
.iter()
.map(|option| option.id.clone())
.collect::<Vec<_>>()
.join(",");
.join(SELECTION_IDS_SEPARATOR);
let data = description.serialize_cell_data(&options).unwrap();
builder = builder.add_cell(&field.id, data);
builder.add_cell(&field.id, data).unwrap();
}
FieldType::Checkbox => {
let data = serialize_cell_data("false", field).unwrap();
builder = builder.add_cell(&field.id, data);
builder.add_cell(&field.id, data).unwrap();
}
}
}
let context = builder.build();
let scripts = vec![AssertRowCount(3), CreateRow { context }, AssertGridMetaPad];
let scripts = vec![CreateRow { context }, AssertGridMetaPad];
test.run_scripts(scripts).await;
}
#[tokio::test]
async fn grid_row_add_selection_cell_test() {
let mut test = GridEditorTest::new().await;
let mut builder = CreateRowContextBuilder::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;
let mut builder = CreateRowContextBuilder::new(&test.field_metas);
let mut date_field = None;
let timestamp = 1647390674;
for field in &test.field_metas {
if field.field_type == FieldType::DateTime {
date_field = Some(field.clone());
NaiveDateTime::from_timestamp(123, 0);
// The data should not be empty
assert!(builder.add_cell(&field.id, "".to_owned()).is_err());
assert!(builder.add_cell(&field.id, "123".to_owned()).is_ok());
assert!(builder.add_cell(&field.id, format!("{}", timestamp)).is_ok());
}
}
let context = builder.build();
let date_field = date_field.unwrap();
let cell_data = context.cell_by_field_id.get(&date_field.id).unwrap().clone();
assert_eq!(
deserialize_cell_data(cell_data.data.clone(), &date_field).unwrap(),
"2022/03/16 08:31",
);
let scripts = vec![CreateRow { context }];
test.run_scripts(scripts).await;
}
#[tokio::test]
async fn grid_cell_update() {
let mut test = GridEditorTest::new().await;
let field_metas = &test.field_metas;
let row_metas = &test.row_metas;
assert_eq!(row_metas.len(), 3);
let mut scripts = vec![];
for (index, row_meta) in row_metas.iter().enumerate() {
for field_meta in field_metas {
if index == 0 {
let data = match field_meta.field_type {
FieldType::RichText => "".to_string(),
FieldType::Number => "123".to_string(),
FieldType::DateTime => "123".to_string(),
FieldType::SingleSelect => {
let description = SingleSelectDescription::from(field_meta);
description.options.first().unwrap().id.clone()
}
FieldType::MultiSelect => {
let description = MultiSelectDescription::from(field_meta);
description.options.first().unwrap().id.clone()
}
FieldType::Checkbox => "1".to_string(),
};
scripts.push(UpdateCell {
changeset: CellMetaChangeset {
row_id: row_meta.id.clone(),
field_id: field_meta.id.clone(),
data: Some(data),
},
is_err: false,
});
}
if index == 1 {
let (data, is_err) = match field_meta.field_type {
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::Checkbox => ("2".to_string(), false),
};
scripts.push(UpdateCell {
changeset: CellMetaChangeset {
row_id: row_meta.id.clone(),
field_id: field_meta.id.clone(),
data: Some(data),
},
is_err,
});
}
}
}
test.run_scripts(scripts).await;
}

View File

@ -1,8 +1,6 @@
use bytes::Bytes;
use flowy_collaboration::client_grid::GridBuilder;
use flowy_collaboration::entities::revision::{RepeatedRevision, Revision};
use flowy_error::FlowyResult;
use flowy_grid::manager::{make_grid_view_data, GridManager};
use flowy_grid::services::cell::*;
use flowy_grid::services::field::*;
use flowy_grid::services::grid_editor::{ClientGridEditor, GridPadBuilder};
@ -16,7 +14,7 @@ use flowy_test::helper::ViewTest;
use flowy_test::FlowySDKTest;
use std::sync::Arc;
use std::time::Duration;
use strum::{EnumCount, IntoEnumIterator};
use strum::EnumCount;
use tokio::time::sleep;
pub enum EditorScript {
@ -65,6 +63,7 @@ pub enum EditorScript {
},
UpdateCell {
changeset: CellMetaChangeset,
is_err: bool,
},
AssertRowCount(usize),
// AssertRowEqual{ row_index: usize, row: RowMeta},
@ -199,9 +198,14 @@ impl GridEditorTest {
assert_eq!(row.height, height);
}
}
EditorScript::UpdateCell { changeset } => {
self.editor.update_cell(changeset).await.unwrap();
self.row_metas = self.editor.get_row_metas(None).await.unwrap();
EditorScript::UpdateCell { changeset, is_err } => {
let result = self.editor.update_cell(changeset).await;
if is_err {
assert!(result.is_err())
} else {
let _ = result.unwrap();
self.row_metas = self.editor.get_row_metas(None).await.unwrap();
}
}
EditorScript::AssertRowCount(count) => {
assert_eq!(self.editor.get_rows(None).await.unwrap().len(), count);

View File

@ -4,7 +4,7 @@ use flowy_collaboration::client_document::default::initial_quill_delta_string;
use flowy_collaboration::entities::revision::{RepeatedRevision, Revision};
use flowy_collaboration::entities::ws_data::ClientRevisionWSData;
use flowy_database::ConnectionPool;
use flowy_folder::errors::FlowyResult;
use flowy_folder::manager::{ViewDataProcessor, ViewDataProcessorMap};
use flowy_folder::prelude::ViewDataType;
use flowy_folder::{

View File

@ -52,7 +52,7 @@ impl GridBlockMetaPad {
pub fn get_rows(&self, row_ids: Option<Vec<String>>) -> CollaborateResult<Vec<Arc<RowMeta>>> {
match row_ids {
None => Ok(self.rows.iter().map(|row| row.clone()).collect::<Vec<_>>()),
None => Ok(self.rows.to_vec()),
Some(row_ids) => {
let row_map = self
.rows

View File

@ -1,19 +1,11 @@
use crate::client_grid::{make_block_meta_delta, make_grid_delta, GridBlockMetaDelta, GridMetaDelta};
use crate::errors::{CollaborateError, CollaborateResult};
use flowy_grid_data_model::entities::{BuildGridContext, FieldMeta, GridBlock, GridBlockMeta, GridMeta, RowMeta};
use flowy_grid_data_model::entities::{BuildGridContext, FieldMeta, RowMeta};
#[derive(Default)]
pub struct GridBuilder {
build_context: BuildGridContext,
}
impl std::default::Default for GridBuilder {
fn default() -> Self {
Self {
build_context: Default::default(),
}
}
}
impl GridBuilder {
pub fn add_field(mut self, field: FieldMeta) -> Self {
self.build_context.field_metas.push(field);
@ -63,7 +55,7 @@ mod tests {
.build();
let grid_meta = GridMeta {
grid_id: grid_id.clone(),
grid_id,
fields: build_context.field_metas,
blocks: vec![build_context.grid_block],
};

View File

@ -58,19 +58,15 @@ impl GridMetaPad {
}
pub fn contain_field(&self, field_id: &str) -> bool {
self.grid_meta
.fields
.iter()
.find(|field| &field.id == field_id)
.is_some()
self.grid_meta.fields.iter().any(|field| field.id == field_id)
}
pub fn get_field(&self, field_id: &str) -> Option<&FieldMeta> {
self.grid_meta.fields.iter().find(|field| field.id == field_id)
}
pub fn get_field_orders(&self) -> Vec<FieldOrder> {
self.grid_meta
.fields
.iter()
.map(|field_meta| FieldOrder::from(field_meta))
.collect()
self.grid_meta.fields.iter().map(FieldOrder::from).collect()
}
pub fn get_field_metas(&self, field_orders: Option<RepeatedFieldOrder>) -> CollaborateResult<Vec<FieldMeta>> {
@ -154,7 +150,7 @@ impl GridMetaPad {
if last_block.start_row_index > block.start_row_index
&& last_block.len() > block.start_row_index
{
let msg = format!("GridBlock's start_row_index should be greater than the last_block's start_row_index and its len");
let msg = "GridBlock's start_row_index should be greater than the last_block's start_row_index and its len".to_string();
return Err(CollaborateError::internal().context(msg))
}
grid.blocks.push(block);

View File

@ -1,7 +1,7 @@
mod block_pad;
mod grid_builder;
mod grid_pad;
mod grid_meta_pad;
pub use block_pad::*;
pub use grid_builder::*;
pub use grid_pad::*;
pub use grid_meta_pad::*;

View File

@ -83,6 +83,10 @@ pub enum ErrorCode {
UserIdInvalid = 311,
#[display(fmt = "User not exist")]
UserNotExist = 312,
#[display(fmt = "Text is too long")]
TextTooLong = 400,
#[display(fmt = "Invalid data")]
InvalidData = 401,
}
impl ErrorCode {

View File

@ -55,6 +55,8 @@ pub enum ErrorCode {
UserNameIsEmpty = 310,
UserIdInvalid = 311,
UserNotExist = 312,
TextTooLong = 400,
InvalidData = 401,
}
impl ::protobuf::ProtobufEnum for ErrorCode {
@ -94,6 +96,8 @@ impl ::protobuf::ProtobufEnum for ErrorCode {
310 => ::std::option::Option::Some(ErrorCode::UserNameIsEmpty),
311 => ::std::option::Option::Some(ErrorCode::UserIdInvalid),
312 => ::std::option::Option::Some(ErrorCode::UserNotExist),
400 => ::std::option::Option::Some(ErrorCode::TextTooLong),
401 => ::std::option::Option::Some(ErrorCode::InvalidData),
_ => ::std::option::Option::None
}
}
@ -130,6 +134,8 @@ impl ::protobuf::ProtobufEnum for ErrorCode {
ErrorCode::UserNameIsEmpty,
ErrorCode::UserIdInvalid,
ErrorCode::UserNotExist,
ErrorCode::TextTooLong,
ErrorCode::InvalidData,
];
values
}
@ -158,7 +164,7 @@ impl ::protobuf::reflect::ProtobufValue for ErrorCode {
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\ncode.proto*\xc4\x05\n\tErrorCode\x12\x0c\n\x08Internal\x10\0\x12\x14\
\n\ncode.proto*\xe8\x05\n\tErrorCode\x12\x0c\n\x08Internal\x10\0\x12\x14\
\n\x10UserUnauthorized\x10\x02\x12\x12\n\x0eRecordNotFound\x10\x03\x12\
\x18\n\x14WorkspaceNameInvalid\x10d\x12\x16\n\x12WorkspaceIdInvalid\x10e\
\x12\x18\n\x14AppColorStyleInvalid\x10f\x12\x18\n\x14WorkspaceDescTooLon\
@ -174,8 +180,9 @@ static file_descriptor_proto_data: &'static [u8] = b"\
rmatInvalid\x10\xb2\x02\x12\x15\n\x10PasswordNotMatch\x10\xb3\x02\x12\
\x14\n\x0fUserNameTooLong\x10\xb4\x02\x12'\n\"UserNameContainForbiddenCh\
aracters\x10\xb5\x02\x12\x14\n\x0fUserNameIsEmpty\x10\xb6\x02\x12\x12\n\
\rUserIdInvalid\x10\xb7\x02\x12\x11\n\x0cUserNotExist\x10\xb8\x02b\x06pr\
oto3\
\rUserIdInvalid\x10\xb7\x02\x12\x11\n\x0cUserNotExist\x10\xb8\x02\x12\
\x10\n\x0bTextTooLong\x10\x90\x03\x12\x10\n\x0bInvalidData\x10\x91\x03b\
\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -31,4 +31,6 @@ enum ErrorCode {
UserNameIsEmpty = 310;
UserIdInvalid = 311;
UserNotExist = 312;
TextTooLong = 400;
InvalidData = 401;
}

View File

@ -4,7 +4,7 @@ use crate::{
impl_def_and_def_mut,
parser::{
app::AppIdentify,
view::{ViewDesc, ViewExtensionData, ViewIdentify, ViewName, ViewThumbnail},
view::{ViewDesc, ViewIdentify, ViewName, ViewThumbnail},
},
};
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};

View File

@ -1,7 +1,7 @@
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use strum::{EnumCount, IntoEnumIterator};
use strum_macros::{Display, EnumCount as EnumCountMacro, EnumIter, EnumString};
pub const DEFAULT_ROW_HEIGHT: i32 = 36;
@ -35,6 +35,10 @@ impl GridBlock {
pub fn len(&self) -> i32 {
self.start_row_index + self.row_count
}
pub fn is_empty(&self) -> bool {
self.row_count == 0
}
}
impl GridBlock {

View File

@ -65,34 +65,3 @@ where
}
pub type BoxResultFuture<'a, T, E> = BoxFuture<'a, Result<T, E>>;
#[pin_project]
pub struct FutureResultSend<T, E> {
#[pin]
pub fut: Pin<Box<dyn Future<Output = Result<T, E>> + Send>>,
}
impl<T, E> FutureResultSend<T, E> {
pub fn new<F>(f: F) -> Self
where
F: Future<Output = Result<T, E>> + Send + 'static,
{
Self {
fut: Box::pin(async { f.await }),
}
}
}
impl<T, E> Future for FutureResultSend<T, E>
where
T: Send,
E: Debug,
{
type Output = Result<T, E>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.as_mut().project();
let result = ready!(this.fut.poll(cx));
Poll::Ready(result)
}
}