#![allow(clippy::upper_case_acronyms)] use std::fmt::{Display, Formatter}; use std::sync::Arc; use collab_database::fields::Field; use collab_database::views::{FieldOrder, OrderObjectPosition}; use serde_repr::*; use strum_macros::{EnumCount as EnumCountMacro, EnumIter}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_error::ErrorCode; use crate::entities::parser::NotEmptyStr; use crate::entities::position_entities::OrderObjectPositionPB; use crate::impl_into_field_type; use crate::services::field::{default_type_option_data_from_type, type_option_to_pb}; /// [FieldPB] defines a Field's attributes. Such as the name, field_type, and width. etc. #[derive(Debug, Clone, Default, ProtoBuf)] pub struct FieldPB { #[pb(index = 1)] pub id: String, #[pb(index = 2)] pub name: String, #[pb(index = 3)] pub field_type: FieldType, #[pb(index = 4)] pub visibility: bool, #[pb(index = 5)] pub width: i32, #[pb(index = 6)] pub is_primary: bool, #[pb(index = 7)] pub type_option_data: Vec, } impl FieldPB { pub fn new(field: Field) -> Self { let field_type = field.field_type.into(); let type_option = field .get_any_type_option(field_type) .unwrap_or_else(|| default_type_option_data_from_type(&field_type)); Self { id: field.id, name: field.name, field_type, visibility: field.visibility, width: field.width as i32, is_primary: field.is_primary, type_option_data: type_option_to_pb(type_option, &field_type).to_vec(), } } } /// [FieldIdPB] id of the [Field] #[derive(Debug, Clone, Default, ProtoBuf)] pub struct FieldIdPB { #[pb(index = 1)] pub field_id: String, } impl std::convert::From<&str> for FieldIdPB { fn from(s: &str) -> Self { FieldIdPB { field_id: s.to_owned(), } } } impl std::convert::From for FieldIdPB { fn from(s: String) -> Self { FieldIdPB { field_id: s } } } impl From for FieldIdPB { fn from(field_order: FieldOrder) -> Self { Self { field_id: field_order.id, } } } impl std::convert::From<&Arc> for FieldIdPB { fn from(field_rev: &Arc) -> Self { Self { field_id: field_rev.id.clone(), } } } #[derive(Debug, Clone, Default, ProtoBuf)] pub struct DatabaseFieldChangesetPB { #[pb(index = 1)] pub view_id: String, #[pb(index = 2)] pub inserted_fields: Vec, #[pb(index = 3)] pub deleted_fields: Vec, #[pb(index = 4)] pub updated_fields: Vec, } impl DatabaseFieldChangesetPB { pub fn insert(database_id: &str, inserted_fields: Vec) -> Self { Self { view_id: database_id.to_owned(), inserted_fields, deleted_fields: vec![], updated_fields: vec![], } } pub fn delete(database_id: &str, deleted_fields: Vec) -> Self { Self { view_id: database_id.to_string(), inserted_fields: vec![], deleted_fields, updated_fields: vec![], } } pub fn update(database_id: &str, updated_fields: Vec) -> Self { Self { view_id: database_id.to_string(), inserted_fields: vec![], deleted_fields: vec![], updated_fields, } } } #[derive(Debug, Clone, Default, ProtoBuf)] pub struct IndexFieldPB { #[pb(index = 1)] pub field: FieldPB, #[pb(index = 2)] pub index: i32, } #[derive(Debug, Default, ProtoBuf)] pub struct CreateFieldPayloadPB { #[pb(index = 1)] pub view_id: String, #[pb(index = 2)] pub field_type: FieldType, #[pb(index = 3, one_of)] pub field_name: Option, /// If the type_option_data is not empty, it will be used to create the field. /// Otherwise, the default value will be used. #[pb(index = 4, one_of)] pub type_option_data: Option>, #[pb(index = 5)] pub field_position: OrderObjectPositionPB, } #[derive(Clone)] pub struct CreateFieldParams { pub view_id: String, pub field_name: Option, pub field_type: FieldType, pub type_option_data: Option>, pub position: OrderObjectPosition, } impl TryInto for CreateFieldPayloadPB { type Error = ErrorCode; fn try_into(self) -> Result { let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::ViewIdIsInvalid)?; let field_name = match self.field_name { Some(name) => Some( NotEmptyStr::parse(name) .map_err(|_| ErrorCode::InvalidParams)? .0, ), None => None, }; let position = self.field_position.try_into()?; Ok(CreateFieldParams { view_id: view_id.0, field_name, field_type: self.field_type, type_option_data: self.type_option_data, position, }) } } #[derive(Debug, Default, ProtoBuf)] pub struct UpdateFieldTypePayloadPB { #[pb(index = 1)] pub view_id: String, #[pb(index = 2)] pub field_id: String, #[pb(index = 3)] pub field_type: FieldType, } pub struct EditFieldParams { pub view_id: String, pub field_id: String, pub field_type: FieldType, } impl TryInto for UpdateFieldTypePayloadPB { type Error = ErrorCode; fn try_into(self) -> Result { let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseIdIsEmpty)?; let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?; Ok(EditFieldParams { view_id: view_id.0, field_id: field_id.0, field_type: self.field_type, }) } } /// Collection of the [FieldPB] #[derive(Debug, Default, ProtoBuf)] pub struct RepeatedFieldPB { #[pb(index = 1)] pub items: Vec, } impl std::ops::Deref for RepeatedFieldPB { type Target = Vec; fn deref(&self) -> &Self::Target { &self.items } } impl std::ops::DerefMut for RepeatedFieldPB { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.items } } impl std::convert::From> for RepeatedFieldPB { fn from(items: Vec) -> Self { Self { items } } } #[derive(Debug, Clone, Default, ProtoBuf)] pub struct RepeatedFieldIdPB { #[pb(index = 1)] pub items: Vec, } impl std::ops::Deref for RepeatedFieldIdPB { type Target = Vec; fn deref(&self) -> &Self::Target { &self.items } } impl std::convert::From> for RepeatedFieldIdPB { fn from(items: Vec) -> Self { RepeatedFieldIdPB { items } } } impl std::convert::From for RepeatedFieldIdPB { fn from(s: String) -> Self { RepeatedFieldIdPB { items: vec![FieldIdPB::from(s)], } } } impl From> for RepeatedFieldIdPB { fn from(value: Vec) -> Self { let field_ids = value.into_iter().map(FieldIdPB::from).collect(); RepeatedFieldIdPB { items: field_ids } } } /// [TypeOptionChangesetPB] is used to update the type-option data. #[derive(ProtoBuf, Default)] pub struct TypeOptionChangesetPB { #[pb(index = 1)] pub view_id: String, #[pb(index = 2)] pub field_id: String, /// Check out [TypeOptionPB] for more details. #[pb(index = 3)] pub type_option_data: Vec, } #[derive(Clone)] pub struct TypeOptionChangesetParams { pub view_id: String, pub field_id: String, pub type_option_data: Vec, } impl TryInto for TypeOptionChangesetPB { type Error = ErrorCode; fn try_into(self) -> Result { let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseIdIsEmpty)?; let _ = NotEmptyStr::parse(self.field_id.clone()).map_err(|_| ErrorCode::FieldIdIsEmpty)?; Ok(TypeOptionChangesetParams { view_id: view_id.0, field_id: self.field_id, type_option_data: self.type_option_data, }) } } #[derive(ProtoBuf, Default)] pub struct GetFieldPayloadPB { #[pb(index = 1)] pub view_id: String, #[pb(index = 2, one_of)] pub field_ids: Option, } pub struct GetFieldParams { pub view_id: String, pub field_ids: Option>, } impl TryInto for GetFieldPayloadPB { type Error = ErrorCode; fn try_into(self) -> Result { let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseIdIsEmpty)?; let field_ids = self.field_ids.map(|repeated| { repeated .items .into_iter() .map(|item| item.field_id) .collect::>() }); Ok(GetFieldParams { view_id: view_id.0, field_ids, }) } } /// [FieldChangesetPB] is used to modify the corresponding field. It defines which properties of /// the field can be modified. /// /// Pass in None if you don't want to modify a property /// Pass in Some(Value) if you want to modify a property /// #[derive(Debug, Clone, Default, ProtoBuf)] pub struct FieldChangesetPB { #[pb(index = 1)] pub field_id: String, #[pb(index = 2)] pub view_id: String, #[pb(index = 3, one_of)] pub name: Option, #[pb(index = 4, one_of)] pub desc: Option, #[pb(index = 5, one_of)] pub frozen: Option, #[pb(index = 6, one_of)] pub visibility: Option, #[pb(index = 7, one_of)] pub width: Option, } impl TryInto for FieldChangesetPB { type Error = ErrorCode; fn try_into(self) -> Result { let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseIdIsEmpty)?; let field_id = NotEmptyStr::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, view_id: view_id.0, name: self.name, desc: self.desc, frozen: self.frozen, visibility: self.visibility, width: self.width, // type_option_data: self.type_option_data, }) } } #[derive(Debug, Clone, Default)] pub struct FieldChangesetParams { pub field_id: String, pub view_id: String, pub name: Option, pub desc: Option, pub frozen: Option, pub visibility: Option, pub width: Option, // pub type_option_data: Option>, } /// Certain field types have user-defined options such as color, date format, number format, /// or a list of values for a multi-select list. These options are defined within a specialization /// of the FieldTypeOption class. /// /// You could check [this](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/grid#fieldtype) /// for more information. /// /// The order of the enum can't be changed. If you want to add a new type, /// it would be better to append it to the end of the list. #[derive( Debug, Copy, Clone, PartialEq, Hash, Eq, ProtoBuf_Enum, EnumCountMacro, EnumIter, Serialize_repr, Deserialize_repr, )] #[repr(u8)] #[derive(Default)] pub enum FieldType { #[default] RichText = 0, Number = 1, DateTime = 2, SingleSelect = 3, MultiSelect = 4, Checkbox = 5, URL = 6, Checklist = 7, LastEditedTime = 8, CreatedTime = 9, } impl Display for FieldType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let value: i64 = (*self).into(); f.write_fmt(format_args!("{}", value)) } } impl AsRef for FieldType { fn as_ref(&self) -> &FieldType { self } } impl From<&FieldType> for FieldType { fn from(field_type: &FieldType) -> Self { *field_type } } impl FieldType { pub fn value(&self) -> i64 { (*self).into() } pub fn default_name(&self) -> String { let s = match self { FieldType::RichText => "Text", FieldType::Number => "Number", FieldType::DateTime => "Date", FieldType::SingleSelect => "Single Select", FieldType::MultiSelect => "Multi Select", FieldType::Checkbox => "Checkbox", FieldType::URL => "URL", FieldType::Checklist => "Checklist", FieldType::LastEditedTime => "Last edited time", FieldType::CreatedTime => "Created time", }; s.to_string() } pub fn is_number(&self) -> bool { matches!(self, FieldType::Number) } pub fn is_text(&self) -> bool { matches!(self, FieldType::RichText) } pub fn is_checkbox(&self) -> bool { matches!(self, FieldType::Checkbox) } pub fn is_date(&self) -> bool { matches!(self, FieldType::DateTime) } pub fn is_single_select(&self) -> bool { matches!(self, FieldType::SingleSelect) } pub fn is_multi_select(&self) -> bool { matches!(self, FieldType::MultiSelect) } pub fn is_last_edited_time(&self) -> bool { matches!(self, FieldType::LastEditedTime) } pub fn is_created_time(&self) -> bool { matches!(self, FieldType::CreatedTime) } pub fn is_url(&self) -> bool { matches!(self, FieldType::URL) } pub fn is_select_option(&self) -> bool { self.is_single_select() || self.is_multi_select() } pub fn is_checklist(&self) -> bool { matches!(self, FieldType::Checklist) } pub fn can_be_group(&self) -> bool { self.is_select_option() || self.is_checkbox() || self.is_url() } pub fn is_auto_update(&self) -> bool { self.is_last_edited_time() } } impl_into_field_type!(i64); impl_into_field_type!(u8); impl From for i64 { fn from(ty: FieldType) -> Self { (ty as u8) as i64 } } impl From<&FieldType> for i64 { fn from(ty: &FieldType) -> Self { i64::from(*ty) } } #[derive(Debug, Clone, Default, ProtoBuf)] pub struct DuplicateFieldPayloadPB { #[pb(index = 1)] pub field_id: String, #[pb(index = 2)] pub view_id: String, } // #[derive(Debug, Clone, Default, ProtoBuf)] // pub struct GridFieldIdentifierPayloadPB { // #[pb(index = 1)] // pub field_id: String, // // #[pb(index = 2)] // pub view_id: String, // } impl TryInto for DuplicateFieldPayloadPB { type Error = ErrorCode; fn try_into(self) -> Result { let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseIdIsEmpty)?; let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?; Ok(FieldIdParams { view_id: view_id.0, field_id: field_id.0, }) } } #[derive(Debug, Clone, Default, ProtoBuf)] pub struct DeleteFieldPayloadPB { #[pb(index = 1)] pub field_id: String, #[pb(index = 2)] pub view_id: String, } impl TryInto for DeleteFieldPayloadPB { type Error = ErrorCode; fn try_into(self) -> Result { let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseIdIsEmpty)?; let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?; Ok(FieldIdParams { view_id: view_id.0, field_id: field_id.0, }) } } pub struct FieldIdParams { pub field_id: String, pub view_id: String, }