use crate::entities::{CellChangeset, Field, FieldOrder, FieldType, RowOrder}; use crate::revision::GridSettingRevision; use bytes::Bytes; use indexmap::IndexMap; use nanoid::nanoid; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::sync::Arc; pub const DEFAULT_ROW_HEIGHT: i32 = 42; pub fn gen_grid_id() -> String { // nanoid calculator https://zelark.github.io/nano-id-cc/ nanoid!(10) } pub fn gen_block_id() -> String { nanoid!(10) } pub fn gen_row_id() -> String { nanoid!(6) } pub fn gen_field_id() -> String { nanoid!(6) } #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct GridRevision { pub grid_id: String, pub fields: Vec, pub blocks: Vec, pub setting: GridSettingRevision, } impl GridRevision { pub fn new(grid_id: &str) -> Self { Self { grid_id: grid_id.to_owned(), fields: vec![], blocks: vec![], setting: GridSettingRevision::default(), } } } #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct GridBlockRevision { pub block_id: String, pub start_row_index: i32, pub row_count: i32, } impl GridBlockRevision { pub fn len(&self) -> i32 { self.row_count } pub fn is_empty(&self) -> bool { self.row_count == 0 } } impl GridBlockRevision { pub fn new() -> Self { GridBlockRevision { block_id: gen_block_id(), ..Default::default() } } } pub struct GridBlockRevisionChangeset { pub block_id: String, pub start_row_index: Option, pub row_count: Option, } impl GridBlockRevisionChangeset { pub fn from_row_count(block_id: &str, row_count: i32) -> Self { Self { block_id: block_id.to_string(), start_row_index: None, row_count: Some(row_count), } } } #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct GridBlockRevisionData { pub block_id: String, pub rows: Vec, } #[derive(Debug, Clone, Default, Serialize, Deserialize, Eq, PartialEq)] pub struct FieldRevision { pub id: String, pub name: String, pub desc: String, pub field_type: FieldType, pub frozen: bool, pub visibility: bool, pub width: i32, /// type_options contains key/value pairs /// key: id of the FieldType /// value: type option data that can be parsed into specified TypeOptionStruct. /// For example, CheckboxTypeOption, MultiSelectTypeOption etc. #[serde(with = "indexmap::serde_seq")] pub type_options: IndexMap, #[serde(default = "default_is_primary")] pub is_primary: bool, } fn default_is_primary() -> bool { false } impl FieldRevision { pub fn new(name: &str, desc: &str, field_type: FieldType, is_primary: bool) -> Self { let width = field_type.default_cell_width(); Self { id: gen_field_id(), name: name.to_string(), desc: desc.to_string(), field_type, frozen: false, visibility: true, width, type_options: Default::default(), is_primary, } } pub fn insert_type_option_entry(&mut self, entry: &T) where T: TypeOptionDataEntry + ?Sized, { self.type_options.insert(entry.field_type().type_id(), entry.json_str()); } pub fn get_type_option_entry(&self, field_type: &FieldType) -> Option { self.type_options .get(&field_type.type_id()) .map(|s| T::from_json_str(s)) } pub fn insert_type_option_str(&mut self, field_type: &FieldType, json_str: String) { self.type_options.insert(field_type.type_id(), json_str); } pub fn get_type_option_str(&self, field_type: &FieldType) -> Option { self.type_options.get(&field_type.type_id()).map(|s| s.to_owned()) } } impl std::convert::From for Field { fn from(field_rev: FieldRevision) -> Self { Self { id: field_rev.id, name: field_rev.name, desc: field_rev.desc, field_type: field_rev.field_type, frozen: field_rev.frozen, visibility: field_rev.visibility, width: field_rev.width, is_primary: field_rev.is_primary, } } } impl std::convert::From<&FieldRevision> for FieldOrder { fn from(field_rev: &FieldRevision) -> Self { Self { field_id: field_rev.id.clone(), } } } pub trait TypeOptionDataEntry { fn field_type(&self) -> FieldType; fn json_str(&self) -> String; fn protobuf_bytes(&self) -> Bytes; } pub trait TypeOptionDataDeserializer { fn from_json_str(s: &str) -> Self; fn from_protobuf_bytes(bytes: Bytes) -> Self; } #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct RowRevision { pub id: String, pub block_id: String, /// cells contains key/value pairs. /// key: field id, /// value: CellMeta #[serde(with = "indexmap::serde_seq")] pub cells: IndexMap, pub height: i32, pub visibility: bool, } impl RowRevision { pub fn new(block_id: &str) -> Self { Self { id: gen_row_id(), block_id: block_id.to_owned(), cells: Default::default(), height: DEFAULT_ROW_HEIGHT, visibility: true, } } } impl std::convert::From<&RowRevision> for RowOrder { fn from(row: &RowRevision) -> Self { Self { row_id: row.id.clone(), block_id: row.block_id.clone(), height: row.height, } } } impl std::convert::From<&Arc> for RowOrder { fn from(row: &Arc) -> Self { Self { row_id: row.id.clone(), block_id: row.block_id.clone(), height: row.height, } } } #[derive(Debug, Clone, Default)] pub struct RowMetaChangeset { pub row_id: String, pub height: Option, pub visibility: Option, pub cell_by_field_id: HashMap, } impl std::convert::From for RowMetaChangeset { fn from(changeset: CellChangeset) -> Self { let mut cell_by_field_id = HashMap::with_capacity(1); let field_id = changeset.field_id; let cell_rev = CellRevision { data: changeset.cell_content_changeset.unwrap_or_else(|| "".to_owned()), }; cell_by_field_id.insert(field_id, cell_rev); RowMetaChangeset { row_id: changeset.row_id, height: None, visibility: None, cell_by_field_id, } } } #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct CellRevision { pub data: String, } impl CellRevision { pub fn new(data: String) -> Self { Self { data } } } #[derive(Clone, Default, Deserialize, Serialize)] pub struct BuildGridContext { pub field_revs: Vec, pub blocks: Vec, pub blocks_meta_data: Vec, } impl BuildGridContext { pub fn new() -> Self { Self::default() } } impl std::convert::From for Bytes { fn from(ctx: BuildGridContext) -> Self { let bytes = serde_json::to_vec(&ctx).unwrap_or_else(|_| vec![]); Bytes::from(bytes) } } impl std::convert::TryFrom for BuildGridContext { type Error = serde_json::Error; fn try_from(bytes: Bytes) -> Result { let ctx: BuildGridContext = serde_json::from_slice(&bytes)?; Ok(ctx) } }