mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-07-28 11:31:54 +00:00

* fix: border of field cell * chore: add filter button * chore: refactor setting button event * chore: notify row did changed with filter configuration * chore: add filter bloc test * chore: config add filter button * chore: create filter * chore: load filters and update corresponding field property * chore: add filter choice chip * chore: config choice chip ui * chore: send notification when filter updated * chore: update filter after update field type option data * fix: remove/add filter when update field's type option * chore: create home setting bloc to save the setting of the home screen * chore: add filter test * chore: edit text filter ui * fix: filter cell bugs * fix: insert row out of bound * chore: update setting icon in grid * chore: shrink wrap the filter list * refactor: extract row container from row cache * chore: disable split-debuginfo Co-authored-by: nathan <nathan@appflowy.io>
663 lines
16 KiB
Rust
663 lines
16 KiB
Rust
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
|
use flowy_error::ErrorCode;
|
|
use grid_rev_model::{FieldRevision, FieldTypeRevision};
|
|
use serde_repr::*;
|
|
use std::sync::Arc;
|
|
|
|
use crate::entities::parser::NotEmptyStr;
|
|
use strum_macros::{Display, EnumCount as EnumCountMacro, EnumIter, EnumString};
|
|
|
|
/// [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 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 is_primary: bool,
|
|
}
|
|
|
|
impl std::convert::From<FieldRevision> for FieldPB {
|
|
fn from(field_rev: FieldRevision) -> Self {
|
|
Self {
|
|
id: field_rev.id,
|
|
name: field_rev.name,
|
|
desc: field_rev.desc,
|
|
field_type: field_rev.ty.into(),
|
|
frozen: field_rev.frozen,
|
|
visibility: field_rev.visibility,
|
|
width: field_rev.width,
|
|
is_primary: field_rev.is_primary,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::convert::From<Arc<FieldRevision>> for FieldPB {
|
|
fn from(field_rev: Arc<FieldRevision>) -> Self {
|
|
let field_rev = field_rev.as_ref().clone();
|
|
FieldPB::from(field_rev)
|
|
}
|
|
}
|
|
|
|
/// [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<String> for FieldIdPB {
|
|
fn from(s: String) -> Self {
|
|
FieldIdPB { field_id: s }
|
|
}
|
|
}
|
|
|
|
impl std::convert::From<&Arc<FieldRevision>> for FieldIdPB {
|
|
fn from(field_rev: &Arc<FieldRevision>) -> Self {
|
|
Self {
|
|
field_id: field_rev.id.clone(),
|
|
}
|
|
}
|
|
}
|
|
#[derive(Debug, Clone, Default, ProtoBuf)]
|
|
pub struct GridFieldChangesetPB {
|
|
#[pb(index = 1)]
|
|
pub grid_id: String,
|
|
|
|
#[pb(index = 2)]
|
|
pub inserted_fields: Vec<IndexFieldPB>,
|
|
|
|
#[pb(index = 3)]
|
|
pub deleted_fields: Vec<FieldIdPB>,
|
|
|
|
#[pb(index = 4)]
|
|
pub updated_fields: Vec<FieldPB>,
|
|
}
|
|
|
|
impl GridFieldChangesetPB {
|
|
pub fn insert(grid_id: &str, inserted_fields: Vec<IndexFieldPB>) -> Self {
|
|
Self {
|
|
grid_id: grid_id.to_owned(),
|
|
inserted_fields,
|
|
deleted_fields: vec![],
|
|
updated_fields: vec![],
|
|
}
|
|
}
|
|
|
|
pub fn delete(grid_id: &str, deleted_fields: Vec<FieldIdPB>) -> Self {
|
|
Self {
|
|
grid_id: grid_id.to_string(),
|
|
inserted_fields: vec![],
|
|
deleted_fields,
|
|
updated_fields: vec![],
|
|
}
|
|
}
|
|
|
|
pub fn update(grid_id: &str, updated_fields: Vec<FieldPB>) -> Self {
|
|
Self {
|
|
grid_id: grid_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,
|
|
}
|
|
|
|
impl IndexFieldPB {
|
|
pub fn from_field_rev(field_rev: &Arc<FieldRevision>, index: usize) -> Self {
|
|
Self {
|
|
field: FieldPB::from(field_rev.as_ref().clone()),
|
|
index: index as i32,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default, ProtoBuf)]
|
|
pub struct CreateFieldPayloadPB {
|
|
#[pb(index = 1)]
|
|
pub grid_id: String,
|
|
|
|
#[pb(index = 2)]
|
|
pub field_type: FieldType,
|
|
|
|
#[pb(index = 3, one_of)]
|
|
pub type_option_data: Option<Vec<u8>>,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct CreateFieldParams {
|
|
pub grid_id: String,
|
|
pub field_type: FieldType,
|
|
pub type_option_data: Option<Vec<u8>>,
|
|
}
|
|
|
|
impl TryInto<CreateFieldParams> for CreateFieldPayloadPB {
|
|
type Error = ErrorCode;
|
|
|
|
fn try_into(self) -> Result<CreateFieldParams, Self::Error> {
|
|
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
|
|
Ok(CreateFieldParams {
|
|
grid_id: grid_id.0,
|
|
field_type: self.field_type,
|
|
type_option_data: self.type_option_data,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default, ProtoBuf)]
|
|
pub struct EditFieldChangesetPB {
|
|
#[pb(index = 1)]
|
|
pub grid_id: String,
|
|
|
|
#[pb(index = 2)]
|
|
pub field_id: String,
|
|
|
|
#[pb(index = 3)]
|
|
pub field_type: FieldType,
|
|
|
|
#[pb(index = 4)]
|
|
pub create_if_not_exist: bool,
|
|
}
|
|
|
|
pub struct EditFieldParams {
|
|
pub grid_id: String,
|
|
pub field_id: String,
|
|
pub field_type: FieldType,
|
|
}
|
|
|
|
impl TryInto<EditFieldParams> for EditFieldChangesetPB {
|
|
type Error = ErrorCode;
|
|
|
|
fn try_into(self) -> Result<EditFieldParams, Self::Error> {
|
|
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
|
|
let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
|
|
Ok(EditFieldParams {
|
|
grid_id: grid_id.0,
|
|
field_id: field_id.0,
|
|
field_type: self.field_type,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default, ProtoBuf)]
|
|
pub struct TypeOptionPathPB {
|
|
#[pb(index = 1)]
|
|
pub grid_id: String,
|
|
|
|
#[pb(index = 2)]
|
|
pub field_id: String,
|
|
|
|
#[pb(index = 3)]
|
|
pub field_type: FieldType,
|
|
}
|
|
|
|
pub struct TypeOptionPathParams {
|
|
pub grid_id: String,
|
|
pub field_id: String,
|
|
pub field_type: FieldType,
|
|
}
|
|
|
|
impl TryInto<TypeOptionPathParams> for TypeOptionPathPB {
|
|
type Error = ErrorCode;
|
|
|
|
fn try_into(self) -> Result<TypeOptionPathParams, Self::Error> {
|
|
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
|
|
let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
|
|
Ok(TypeOptionPathParams {
|
|
grid_id: grid_id.0,
|
|
field_id: field_id.0,
|
|
field_type: self.field_type,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default, ProtoBuf)]
|
|
pub struct TypeOptionPB {
|
|
#[pb(index = 1)]
|
|
pub grid_id: String,
|
|
|
|
#[pb(index = 2)]
|
|
pub field: FieldPB,
|
|
|
|
#[pb(index = 3)]
|
|
pub type_option_data: Vec<u8>,
|
|
}
|
|
|
|
/// Collection of the [FieldPB]
|
|
#[derive(Debug, Default, ProtoBuf)]
|
|
pub struct RepeatedFieldPB {
|
|
#[pb(index = 1)]
|
|
pub items: Vec<FieldPB>,
|
|
}
|
|
impl std::ops::Deref for RepeatedFieldPB {
|
|
type Target = Vec<FieldPB>;
|
|
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<Vec<FieldPB>> for RepeatedFieldPB {
|
|
fn from(items: Vec<FieldPB>) -> Self {
|
|
Self { items }
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Default, ProtoBuf)]
|
|
pub struct RepeatedFieldIdPB {
|
|
#[pb(index = 1)]
|
|
pub items: Vec<FieldIdPB>,
|
|
}
|
|
|
|
impl std::ops::Deref for RepeatedFieldIdPB {
|
|
type Target = Vec<FieldIdPB>;
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.items
|
|
}
|
|
}
|
|
|
|
impl std::convert::From<Vec<FieldIdPB>> for RepeatedFieldIdPB {
|
|
fn from(items: Vec<FieldIdPB>) -> Self {
|
|
RepeatedFieldIdPB { items }
|
|
}
|
|
}
|
|
|
|
impl std::convert::From<String> for RepeatedFieldIdPB {
|
|
fn from(s: String) -> Self {
|
|
RepeatedFieldIdPB {
|
|
items: vec![FieldIdPB::from(s)],
|
|
}
|
|
}
|
|
}
|
|
|
|
/// [TypeOptionChangesetPB] is used to update the type-option data.
|
|
#[derive(ProtoBuf, Default)]
|
|
pub struct TypeOptionChangesetPB {
|
|
#[pb(index = 1)]
|
|
pub grid_id: String,
|
|
|
|
#[pb(index = 2)]
|
|
pub field_id: String,
|
|
|
|
/// Check out [TypeOptionPB] for more details.
|
|
#[pb(index = 3)]
|
|
pub type_option_data: Vec<u8>,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct TypeOptionChangesetParams {
|
|
pub grid_id: String,
|
|
pub field_id: String,
|
|
pub type_option_data: Vec<u8>,
|
|
}
|
|
|
|
impl TryInto<TypeOptionChangesetParams> for TypeOptionChangesetPB {
|
|
type Error = ErrorCode;
|
|
|
|
fn try_into(self) -> Result<TypeOptionChangesetParams, Self::Error> {
|
|
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
|
|
let _ = NotEmptyStr::parse(self.field_id.clone()).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
|
|
|
|
Ok(TypeOptionChangesetParams {
|
|
grid_id: grid_id.0,
|
|
field_id: self.field_id,
|
|
type_option_data: self.type_option_data,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(ProtoBuf, Default)]
|
|
pub struct GetFieldPayloadPB {
|
|
#[pb(index = 1)]
|
|
pub grid_id: String,
|
|
|
|
#[pb(index = 2, one_of)]
|
|
pub field_ids: Option<RepeatedFieldIdPB>,
|
|
}
|
|
|
|
pub struct GetFieldParams {
|
|
pub grid_id: String,
|
|
pub field_ids: Option<Vec<String>>,
|
|
}
|
|
|
|
impl TryInto<GetFieldParams> for GetFieldPayloadPB {
|
|
type Error = ErrorCode;
|
|
|
|
fn try_into(self) -> Result<GetFieldParams, Self::Error> {
|
|
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
|
|
let field_ids = self.field_ids.map(|repeated| {
|
|
repeated
|
|
.items
|
|
.into_iter()
|
|
.map(|item| item.field_id)
|
|
.collect::<Vec<String>>()
|
|
});
|
|
|
|
Ok(GetFieldParams {
|
|
grid_id: grid_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 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>>,
|
|
}
|
|
|
|
impl TryInto<FieldChangesetParams> for FieldChangesetPB {
|
|
type Error = ErrorCode;
|
|
|
|
fn try_into(self) -> Result<FieldChangesetParams, Self::Error> {
|
|
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
|
|
let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
|
|
let field_type = self.field_type.map(FieldTypeRevision::from);
|
|
// 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,
|
|
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 grid_id: String,
|
|
|
|
pub name: Option<String>,
|
|
|
|
pub desc: Option<String>,
|
|
|
|
pub field_type: Option<FieldTypeRevision>,
|
|
|
|
pub frozen: Option<bool>,
|
|
|
|
pub visibility: Option<bool>,
|
|
|
|
pub width: Option<i32>,
|
|
// pub type_option_data: Option<Vec<u8>>,
|
|
}
|
|
/// 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,
|
|
Clone,
|
|
PartialEq,
|
|
Hash,
|
|
Eq,
|
|
ProtoBuf_Enum,
|
|
EnumCountMacro,
|
|
EnumString,
|
|
EnumIter,
|
|
Display,
|
|
Serialize_repr,
|
|
Deserialize_repr,
|
|
)]
|
|
#[repr(u8)]
|
|
pub enum FieldType {
|
|
RichText = 0,
|
|
Number = 1,
|
|
DateTime = 2,
|
|
SingleSelect = 3,
|
|
MultiSelect = 4,
|
|
Checkbox = 5,
|
|
URL = 6,
|
|
}
|
|
|
|
pub const RICH_TEXT_FIELD: FieldType = FieldType::RichText;
|
|
pub const NUMBER_FIELD: FieldType = FieldType::Number;
|
|
pub const DATE_FIELD: FieldType = FieldType::DateTime;
|
|
pub const SINGLE_SELECT_FIELD: FieldType = FieldType::SingleSelect;
|
|
pub const MULTI_SELECT_FIELD: FieldType = FieldType::MultiSelect;
|
|
pub const CHECKBOX_FIELD: FieldType = FieldType::Checkbox;
|
|
pub const URL_FIELD: FieldType = FieldType::URL;
|
|
|
|
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 {
|
|
(self.clone() as u8).to_string()
|
|
}
|
|
|
|
pub fn default_cell_width(&self) -> i32 {
|
|
match self {
|
|
FieldType::DateTime => 180,
|
|
_ => 150,
|
|
}
|
|
}
|
|
|
|
pub fn is_number(&self) -> bool {
|
|
self == &NUMBER_FIELD
|
|
}
|
|
|
|
pub fn is_text(&self) -> bool {
|
|
self == &RICH_TEXT_FIELD
|
|
}
|
|
|
|
pub fn is_checkbox(&self) -> bool {
|
|
self == &CHECKBOX_FIELD
|
|
}
|
|
|
|
pub fn is_date(&self) -> bool {
|
|
self == &DATE_FIELD
|
|
}
|
|
|
|
pub fn is_single_select(&self) -> bool {
|
|
self == &SINGLE_SELECT_FIELD
|
|
}
|
|
|
|
pub fn is_multi_select(&self) -> bool {
|
|
self == &MULTI_SELECT_FIELD
|
|
}
|
|
|
|
pub fn is_url(&self) -> bool {
|
|
self == &URL_FIELD
|
|
}
|
|
|
|
pub fn is_select_option(&self) -> bool {
|
|
self == &MULTI_SELECT_FIELD || self == &SINGLE_SELECT_FIELD
|
|
}
|
|
|
|
pub fn can_be_group(&self) -> bool {
|
|
self.is_select_option()
|
|
}
|
|
}
|
|
|
|
impl std::convert::From<&FieldType> for FieldTypeRevision {
|
|
fn from(ty: &FieldType) -> Self {
|
|
ty.clone() as u8
|
|
}
|
|
}
|
|
|
|
impl std::convert::From<FieldType> for FieldTypeRevision {
|
|
fn from(ty: FieldType) -> Self {
|
|
ty as u8
|
|
}
|
|
}
|
|
|
|
impl std::convert::From<&FieldTypeRevision> for FieldType {
|
|
fn from(ty: &FieldTypeRevision) -> Self {
|
|
FieldType::from(*ty)
|
|
}
|
|
}
|
|
|
|
impl std::convert::From<FieldTypeRevision> for FieldType {
|
|
fn from(ty: FieldTypeRevision) -> Self {
|
|
match ty {
|
|
0 => FieldType::RichText,
|
|
1 => FieldType::Number,
|
|
2 => FieldType::DateTime,
|
|
3 => FieldType::SingleSelect,
|
|
4 => FieldType::MultiSelect,
|
|
5 => FieldType::Checkbox,
|
|
6 => FieldType::URL,
|
|
_ => {
|
|
tracing::error!("Can't parser FieldTypeRevision: {} to FieldType", ty);
|
|
FieldType::RichText
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#[derive(Debug, Clone, Default, ProtoBuf)]
|
|
pub struct DuplicateFieldPayloadPB {
|
|
#[pb(index = 1)]
|
|
pub field_id: String,
|
|
|
|
#[pb(index = 2)]
|
|
pub grid_id: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Default, ProtoBuf)]
|
|
pub struct GridFieldIdentifierPayloadPB {
|
|
#[pb(index = 1)]
|
|
pub field_id: String,
|
|
|
|
#[pb(index = 2)]
|
|
pub grid_id: String,
|
|
}
|
|
|
|
impl TryInto<FieldIdParams> for DuplicateFieldPayloadPB {
|
|
type Error = ErrorCode;
|
|
|
|
fn try_into(self) -> Result<FieldIdParams, Self::Error> {
|
|
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
|
|
let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
|
|
Ok(FieldIdParams {
|
|
grid_id: grid_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 grid_id: String,
|
|
}
|
|
|
|
impl TryInto<FieldIdParams> for DeleteFieldPayloadPB {
|
|
type Error = ErrorCode;
|
|
|
|
fn try_into(self) -> Result<FieldIdParams, Self::Error> {
|
|
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
|
|
let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
|
|
Ok(FieldIdParams {
|
|
grid_id: grid_id.0,
|
|
field_id: field_id.0,
|
|
})
|
|
}
|
|
}
|
|
|
|
pub struct FieldIdParams {
|
|
pub field_id: String,
|
|
pub grid_id: String,
|
|
}
|