AppFlowy/shared-lib/flowy-grid-data-model/src/entities/meta.rs
2022-04-02 21:30:25 +08:00

441 lines
11 KiB
Rust

use crate::parser::NotEmptyUuid;
use bytes::Bytes;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error_code::ErrorCode;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use strum_macros::{Display, EnumCount as EnumCountMacro, EnumIter, EnumString};
pub const DEFAULT_ROW_HEIGHT: i32 = 36;
pub const DEFAULT_FIELD_WIDTH: i32 = 150;
#[derive(Debug, Clone, Default, Serialize, Deserialize, ProtoBuf)]
pub struct GridMeta {
#[pb(index = 1)]
pub grid_id: String,
#[pb(index = 2)]
pub fields: Vec<FieldMeta>,
#[pb(index = 3)]
pub block_metas: Vec<GridBlockMeta>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, ProtoBuf)]
pub struct GridBlockMeta {
#[pb(index = 1)]
pub block_id: String,
#[pb(index = 2)]
pub start_row_index: i32,
#[pb(index = 3)]
pub row_count: i32,
}
impl GridBlockMeta {
pub fn len(&self) -> i32 {
self.row_count
}
pub fn is_empty(&self) -> bool {
self.row_count == 0
}
}
impl GridBlockMeta {
pub fn new() -> Self {
GridBlockMeta {
block_id: uuid::Uuid::new_v4().to_string(),
..Default::default()
}
}
}
pub struct GridBlockMetaChangeset {
pub block_id: String,
pub start_row_index: Option<i32>,
pub row_count: Option<i32>,
}
impl GridBlockMetaChangeset {
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, ProtoBuf)]
pub struct GridBlockMetaData {
#[pb(index = 1)]
pub block_id: String,
#[pb(index = 2)]
pub row_metas: Vec<RowMeta>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, ProtoBuf, Eq, PartialEq)]
pub struct FieldMeta {
#[pb(index = 1)]
pub id: String,
#[pb(index = 2)]
pub name: String,
#[pb(index = 3)]
pub desc: String,
#[pb(index = 4)]
pub field_type: FieldType,
#[pb(index = 5)]
pub frozen: bool,
#[pb(index = 6)]
pub visibility: bool,
#[pb(index = 7)]
pub width: i32,
#[pb(index = 8)]
pub type_option_by_field_type_id: TypeOptionDataByFieldTypeId,
}
impl FieldMeta {
pub fn new(name: &str, desc: &str, field_type: FieldType) -> Self {
Self {
id: uuid::Uuid::new_v4().to_string(),
name: name.to_string(),
desc: desc.to_string(),
field_type,
frozen: false,
visibility: true,
width: DEFAULT_FIELD_WIDTH,
type_option_by_field_type_id: Default::default(),
}
}
pub fn insert_type_option_entry<T: TypeOptionDataEntry + ?Sized>(&mut self, entry: &T) {
self.type_option_by_field_type_id
.insert(entry.field_type().type_id(), entry.json_str());
}
pub fn get_type_option_entry<T: TypeOptionDataEntity>(&self, field_type: Option<FieldType>) -> Option<T> {
let field_type = field_type.as_ref().unwrap_or(&self.field_type);
self.type_option_by_field_type_id
.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_option_by_field_type_id.insert(field_type.type_id(), json_str);
}
pub fn get_type_option_str(&self, field_type: Option<FieldType>) -> Option<String> {
let field_type = field_type.as_ref().unwrap_or(&self.field_type);
self.type_option_by_field_type_id
.get(&field_type.type_id())
.map(|s| s.to_owned())
}
}
pub trait TypeOptionDataEntry {
fn field_type(&self) -> FieldType;
fn json_str(&self) -> String;
fn protobuf_bytes(&self) -> Bytes;
}
pub trait TypeOptionDataEntity {
fn from_json_str(s: &str) -> Self;
fn from_protobuf_bytes(bytes: Bytes) -> Self;
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, ProtoBuf, PartialEq, Eq)]
pub struct TypeOptionDataByFieldTypeId {
#[pb(index = 1)]
pub map: HashMap<String, String>,
}
impl std::ops::Deref for TypeOptionDataByFieldTypeId {
type Target = HashMap<String, String>;
fn deref(&self) -> &Self::Target {
&self.map
}
}
impl std::ops::DerefMut for TypeOptionDataByFieldTypeId {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.map
}
}
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct FieldChangesetPayload {
#[pb(index = 1)]
pub field_id: String,
#[pb(index = 2)]
pub grid_id: String,
#[pb(index = 3, one_of)]
pub name: Option<String>,
#[pb(index = 4, one_of)]
pub desc: Option<String>,
#[pb(index = 5, one_of)]
pub field_type: Option<FieldType>,
#[pb(index = 6, one_of)]
pub frozen: Option<bool>,
#[pb(index = 7, one_of)]
pub visibility: Option<bool>,
#[pb(index = 8, one_of)]
pub width: Option<i32>,
#[pb(index = 9, one_of)]
pub type_option_data: Option<Vec<u8>>,
}
#[derive(Debug, Clone, Default)]
pub struct FieldChangesetParams {
pub field_id: String,
pub grid_id: String,
pub name: Option<String>,
pub desc: Option<String>,
pub field_type: Option<FieldType>,
pub frozen: Option<bool>,
pub visibility: Option<bool>,
pub width: Option<i32>,
pub type_option_data: Option<Vec<u8>>,
}
impl TryInto<FieldChangesetParams> for FieldChangesetPayload {
type Error = ErrorCode;
fn try_into(self) -> Result<FieldChangesetParams, Self::Error> {
let grid_id = NotEmptyUuid::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
let field_id = NotEmptyUuid::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
if let Some(type_option_data) = self.type_option_data.as_ref() {
if type_option_data.is_empty() {
return Err(ErrorCode::TypeOptionDataIsEmpty);
}
}
Ok(FieldChangesetParams {
field_id: field_id.0,
grid_id: grid_id.0,
name: self.name,
desc: self.desc,
field_type: self.field_type,
frozen: self.frozen,
visibility: self.visibility,
width: self.width,
type_option_data: self.type_option_data,
})
}
}
#[derive(
Debug, Clone, PartialEq, Eq, ProtoBuf_Enum, EnumCountMacro, EnumString, EnumIter, Display, Serialize, Deserialize,
)]
#[repr(u8)]
pub enum FieldType {
RichText = 0,
Number = 1,
DateTime = 2,
SingleSelect = 3,
MultiSelect = 4,
Checkbox = 5,
}
impl std::default::Default for FieldType {
fn default() -> Self {
FieldType::RichText
}
}
impl AsRef<FieldType> for FieldType {
fn as_ref(&self) -> &FieldType {
self
}
}
impl From<&FieldType> for FieldType {
fn from(field_type: &FieldType) -> Self {
field_type.clone()
}
}
impl FieldType {
pub fn type_id(&self) -> String {
let ty = self.clone();
format!("{}", ty as u8)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, ProtoBuf)]
pub struct AnyData {
#[pb(index = 1)]
pub type_id: String,
#[pb(index = 2)]
pub value: Vec<u8>,
}
impl AnyData {
pub fn from_str<F: Into<FieldType>>(field_type: F, s: &str) -> AnyData {
Self::from_bytes(field_type, s.as_bytes().to_vec())
}
pub fn from_bytes<T: AsRef<[u8]>, F: Into<FieldType>>(field_type: F, bytes: T) -> AnyData {
AnyData {
type_id: field_type.into().type_id(),
value: bytes.as_ref().to_vec(),
}
}
}
impl ToString for AnyData {
fn to_string(&self) -> String {
match String::from_utf8(self.value.clone()) {
Ok(s) => s,
Err(_) => "".to_owned(),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, ProtoBuf)]
pub struct RowMeta {
#[pb(index = 1)]
pub id: String,
#[pb(index = 2)]
pub block_id: String,
#[pb(index = 3)]
pub cell_by_field_id: HashMap<String, CellMeta>,
#[pb(index = 4)]
pub height: i32,
#[pb(index = 5)]
pub visibility: bool,
}
impl RowMeta {
pub fn new(block_id: &str) -> Self {
Self {
id: uuid::Uuid::new_v4().to_string(),
block_id: block_id.to_owned(),
cell_by_field_id: Default::default(),
height: DEFAULT_ROW_HEIGHT,
visibility: true,
}
}
}
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct RowMetaChangeset {
#[pb(index = 1)]
pub row_id: String,
#[pb(index = 2, one_of)]
pub height: Option<i32>,
#[pb(index = 3, one_of)]
pub visibility: Option<bool>,
#[pb(index = 4)]
pub cell_by_field_id: HashMap<String, CellMeta>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, ProtoBuf)]
pub struct CellMeta {
#[pb(index = 1)]
pub field_id: String,
#[pb(index = 2)]
pub data: String,
}
impl CellMeta {
pub fn new(field_id: &str, data: String) -> Self {
Self {
field_id: field_id.to_string(),
data,
}
}
}
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct CellMetaChangeset {
#[pb(index = 1)]
pub grid_id: String,
#[pb(index = 2)]
pub row_id: String,
#[pb(index = 3)]
pub field_id: String,
#[pb(index = 4, one_of)]
pub data: Option<String>,
}
impl std::convert::From<CellMetaChangeset> for RowMetaChangeset {
fn from(changeset: CellMetaChangeset) -> Self {
let mut cell_by_field_id = HashMap::with_capacity(1);
if let Some(data) = changeset.data {
let field_id = changeset.field_id;
let cell_meta = CellMeta {
field_id: field_id.clone(),
data,
};
cell_by_field_id.insert(field_id, cell_meta);
}
RowMetaChangeset {
row_id: changeset.row_id,
height: None,
visibility: None,
cell_by_field_id,
}
}
}
#[derive(Clone, ProtoBuf)]
pub struct BuildGridContext {
#[pb(index = 1)]
pub field_metas: Vec<FieldMeta>,
#[pb(index = 2)]
pub block_metas: GridBlockMeta,
#[pb(index = 3)]
pub block_meta_data: GridBlockMetaData,
}
impl std::default::Default for BuildGridContext {
fn default() -> Self {
let grid_block = GridBlockMeta::new();
let grid_block_meta_data = GridBlockMetaData {
block_id: grid_block.block_id.clone(),
row_metas: vec![],
};
Self {
field_metas: vec![],
block_metas: grid_block,
block_meta_data: grid_block_meta_data,
}
}
}