Compare commits

...
3 Commits
16 changed files with 280 additions and 131 deletions

No files matched your search

@@ -97,7 +97,6 @@ class GridURLCell extends GridCellWidget {
class _GridURLCellState extends GridCellState<GridURLCell> {
final _popoverController = PopoverController();
GridURLCellController? _cellContext;
late URLCellBloc _cellBloc;
@override
@@ -132,6 +131,7 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
controller: _popoverController,
constraints: BoxConstraints.loose(const Size(300, 160)),
direction: PopoverDirection.bottomWithLeftAligned,
triggerActions: PopoverTriggerFlags.none,
offset: const Offset(0, 20),
child: SizedBox.expand(
child: GestureDetector(
@@ -144,7 +144,8 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
),
popupBuilder: (BuildContext popoverContext) {
return URLEditorPopover(
cellController: _cellContext!,
cellController: widget.cellControllerBuilder.build()
as GridURLCellController,
);
},
onClose: () {
@@ -166,17 +167,13 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
final uri = Uri.parse(url);
if (url.isNotEmpty && await canLaunchUrl(uri)) {
await launchUrl(uri);
} else {
_cellContext =
widget.cellControllerBuilder.build() as GridURLCellController;
widget.onCellEditing.value = true;
_popoverController.show();
}
}
@override
void requestBeginFocus() {
_openUrlOrEdit(_cellBloc.state.url);
widget.onCellEditing.value = true;
_popoverController.show();
}
@override
@@ -210,7 +210,7 @@ impl ViewController {
let index = views
.iter()
.position(|view| view.id == view_id)
.and_then(|index| Some(index as i32));
.map(|index| index as i32);
Ok(DeletedViewPB {
view_id: view_id.clone(),
index,
@@ -1,3 +1,4 @@
use crate::entities::FieldType;
use flowy_derive::ProtoBuf;
use flowy_error::ErrorCode;
use flowy_grid_data_model::parser::NotEmptyStr;
@@ -74,15 +75,20 @@ pub struct GridCellPB {
#[pb(index = 1)]
pub field_id: String,
// The data was encoded in field_type's data type
#[pb(index = 2)]
pub data: Vec<u8>,
#[pb(index = 3, one_of)]
pub field_type: Option<FieldType>,
}
impl GridCellPB {
pub fn new(field_id: &str, data: Vec<u8>) -> Self {
pub fn new(field_id: &str, field_type: FieldType, data: Vec<u8>) -> Self {
Self {
field_id: field_id.to_owned(),
data,
field_type: Some(field_type),
}
}
@@ -90,6 +96,7 @@ impl GridCellPB {
Self {
field_id: field_id.to_owned(),
data: vec![],
field_type: None,
}
}
}
@@ -24,14 +24,28 @@ pub trait CellDisplayable<CD> {
decoded_field_type: &FieldType,
field_rev: &FieldRevision,
) -> FlowyResult<CellBytes>;
fn display_string(
&self,
cell_data: CellData<CD>,
decoded_field_type: &FieldType,
field_rev: &FieldRevision,
) -> FlowyResult<String>;
}
// CD: Short for CellData. This type is the type return by apply_changeset function.
// CS: Short for Changeset. Parse the string into specific Changeset type.
pub trait CellDataOperation<CD, CS> {
/// The cell_data is able to parse into the specific data if CD impl the FromCellData trait.
/// For example:
/// URLCellData, DateCellData. etc.
/// Decode the cell data into `CD` that is certain type of data.
///
/// Each `CD` type represents as a specific field type data. For example:
/// FieldType::URL => URLCellData
/// FieldType::Date=> DateCellData
///
/// `decoded_field_type`: the field type of the cell data
///
/// Returns the error if the cell data can't be parsed into `CD`.
///
fn decode_cell_data(
&self,
cell_data: CellData<CD>,
@@ -77,16 +91,16 @@ pub fn apply_cell_data_changeset<C: ToString, T: AsRef<FieldRevision>>(
pub fn decode_any_cell_data<T: TryInto<AnyCellData, Error = FlowyError> + Debug>(
data: T,
field_rev: &FieldRevision,
) -> CellBytes {
) -> (FieldType, CellBytes) {
let to_field_type = field_rev.ty.into();
match data.try_into() {
Ok(any_cell_data) => {
let AnyCellData { data, field_type } = any_cell_data;
let to_field_type = field_rev.ty.into();
match try_decode_cell_data(data.into(), field_rev, &field_type, &to_field_type) {
Ok(cell_bytes) => cell_bytes,
match try_decode_cell_data(data.into(), &field_type, &to_field_type, field_rev) {
Ok(cell_bytes) => (field_type, cell_bytes),
Err(e) => {
tracing::error!("Decode cell data failed, {:?}", e);
CellBytes::default()
(field_type, CellBytes::default())
}
}
}
@@ -94,42 +108,93 @@ pub fn decode_any_cell_data<T: TryInto<AnyCellData, Error = FlowyError> + Debug>
// It's okay to ignore this error, because it's okay that the current cell can't
// display the existing cell data. For example, the UI of the text cell will be blank if
// the type of the data of cell is Number.
CellBytes::default()
(to_field_type, CellBytes::default())
}
}
}
pub fn decode_cell_data_to_string(
cell_data: CellData<String>,
from_field_type: &FieldType,
to_field_type: &FieldType,
field_rev: &FieldRevision,
) -> FlowyResult<String> {
let cell_data = cell_data.try_into_inner()?;
let get_cell_display_str = || {
let field_type: FieldTypeRevision = to_field_type.into();
let result = match to_field_type {
FieldType::RichText => field_rev
.get_type_option::<RichTextTypeOptionPB>(field_type)?
.display_string(cell_data.into(), from_field_type, field_rev),
FieldType::Number => field_rev
.get_type_option::<NumberTypeOptionPB>(field_type)?
.display_string(cell_data.into(), from_field_type, field_rev),
FieldType::DateTime => field_rev
.get_type_option::<DateTypeOptionPB>(field_type)?
.display_string(cell_data.into(), from_field_type, field_rev),
FieldType::SingleSelect => field_rev
.get_type_option::<SingleSelectTypeOptionPB>(field_type)?
.display_string(cell_data.into(), from_field_type, field_rev),
FieldType::MultiSelect => field_rev
.get_type_option::<MultiSelectTypeOptionPB>(field_type)?
.display_string(cell_data.into(), from_field_type, field_rev),
FieldType::Checkbox => field_rev
.get_type_option::<CheckboxTypeOptionPB>(field_type)?
.display_string(cell_data.into(), from_field_type, field_rev),
FieldType::URL => field_rev
.get_type_option::<URLTypeOptionPB>(field_type)?
.display_string(cell_data.into(), from_field_type, field_rev),
};
Some(result)
};
match get_cell_display_str() {
Some(Ok(s)) => Ok(s),
Some(Err(err)) => {
tracing::error!("{:?}", err);
Ok("".to_owned())
}
None => Ok("".to_owned()),
}
}
/// Use the `to_field_type`'s TypeOption to parse the cell data into `from_field_type` type's data.
///
/// Each `FieldType` has its corresponding `TypeOption` that implements the `CellDisplayable`
/// and `CellDataOperation` traits.
///
pub fn try_decode_cell_data(
cell_data: CellData<String>,
from_field_type: &FieldType,
to_field_type: &FieldType,
field_rev: &FieldRevision,
s_field_type: &FieldType,
t_field_type: &FieldType,
) -> FlowyResult<CellBytes> {
let cell_data = cell_data.try_into_inner()?;
let get_cell_data = || {
let field_type: FieldTypeRevision = t_field_type.into();
let data = match t_field_type {
let field_type: FieldTypeRevision = to_field_type.into();
let data = match to_field_type {
FieldType::RichText => field_rev
.get_type_option::<RichTextTypeOptionPB>(field_type)?
.decode_cell_data(cell_data.into(), s_field_type, field_rev),
.decode_cell_data(cell_data.into(), from_field_type, field_rev),
FieldType::Number => field_rev
.get_type_option::<NumberTypeOptionPB>(field_type)?
.decode_cell_data(cell_data.into(), s_field_type, field_rev),
.decode_cell_data(cell_data.into(), from_field_type, field_rev),
FieldType::DateTime => field_rev
.get_type_option::<DateTypeOptionPB>(field_type)?
.decode_cell_data(cell_data.into(), s_field_type, field_rev),
.decode_cell_data(cell_data.into(), from_field_type, field_rev),
FieldType::SingleSelect => field_rev
.get_type_option::<SingleSelectTypeOptionPB>(field_type)?
.decode_cell_data(cell_data.into(), s_field_type, field_rev),
.decode_cell_data(cell_data.into(), from_field_type, field_rev),
FieldType::MultiSelect => field_rev
.get_type_option::<MultiSelectTypeOptionPB>(field_type)?
.decode_cell_data(cell_data.into(), s_field_type, field_rev),
.decode_cell_data(cell_data.into(), from_field_type, field_rev),
FieldType::Checkbox => field_rev
.get_type_option::<CheckboxTypeOptionPB>(field_type)?
.decode_cell_data(cell_data.into(), s_field_type, field_rev),
.decode_cell_data(cell_data.into(), from_field_type, field_rev),
FieldType::URL => field_rev
.get_type_option::<URLTypeOptionPB>(field_type)?
.decode_cell_data(cell_data.into(), s_field_type, field_rev),
.decode_cell_data(cell_data.into(), from_field_type, field_rev),
};
Some(data)
};
@@ -224,6 +289,12 @@ where
}
}
impl std::convert::From<usize> for CellData<String> {
fn from(n: usize) -> Self {
CellData(Some(n.to_string()))
}
}
impl<T> std::convert::From<T> for CellData<T> {
fn from(val: T) -> Self {
CellData(Some(val))
@@ -48,6 +48,16 @@ impl CellDisplayable<CheckboxCellData> for CheckboxTypeOptionPB {
let cell_data = cell_data.try_into_inner()?;
Ok(CellBytes::new(cell_data))
}
fn display_string(
&self,
cell_data: CellData<CheckboxCellData>,
_decoded_field_type: &FieldType,
_field_rev: &FieldRevision,
) -> FlowyResult<String> {
let cell_data = cell_data.try_into_inner()?;
Ok(cell_data.to_string())
}
}
impl CellDataOperation<CheckboxCellData, String> for CheckboxTypeOptionPB {
@@ -127,6 +127,17 @@ impl CellDisplayable<DateTimestamp> for DateTypeOptionPB {
let date_cell_data = self.today_desc_from_timestamp(timestamp);
CellBytes::from(date_cell_data)
}
fn display_string(
&self,
cell_data: CellData<DateTimestamp>,
_decoded_field_type: &FieldType,
_field_rev: &FieldRevision,
) -> FlowyResult<String> {
let timestamp = cell_data.try_into_inner()?;
let date_cell_data = self.today_desc_from_timestamp(timestamp);
Ok(date_cell_data.date)
}
}
impl CellDataOperation<DateTimestamp, DateCellChangesetPB> for DateTypeOptionPB {
@@ -1,6 +1,6 @@
use crate::entities::FieldType;
use crate::impl_type_option;
use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation};
use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable};
use crate::services::field::type_options::number_type_option::format::*;
use crate::services::field::{BoxTypeOptionBuilder, NumberCellData, TypeOptionBuilder};
use bytes::Bytes;
@@ -102,17 +102,13 @@ pub(crate) fn strip_currency_symbol<T: ToString>(s: T) -> String {
s
}
impl CellDataOperation<String, String> for NumberTypeOptionPB {
fn decode_cell_data(
impl CellDisplayable<String> for NumberTypeOptionPB {
fn display_data(
&self,
cell_data: CellData<String>,
decoded_field_type: &FieldType,
_decoded_field_type: &FieldType,
_field_rev: &FieldRevision,
) -> FlowyResult<CellBytes> {
if decoded_field_type.is_date() {
return Ok(CellBytes::default());
}
let cell_data: String = cell_data.try_into_inner()?;
match self.format_cell_data(&cell_data) {
Ok(num) => Ok(CellBytes::new(num.to_string())),
@@ -120,6 +116,31 @@ impl CellDataOperation<String, String> for NumberTypeOptionPB {
}
}
fn display_string(
&self,
cell_data: CellData<String>,
_decoded_field_type: &FieldType,
_field_rev: &FieldRevision,
) -> FlowyResult<String> {
let cell_data: String = cell_data.try_into_inner()?;
Ok(cell_data)
}
}
impl CellDataOperation<String, String> for NumberTypeOptionPB {
fn decode_cell_data(
&self,
cell_data: CellData<String>,
decoded_field_type: &FieldType,
field_rev: &FieldRevision,
) -> FlowyResult<CellBytes> {
if decoded_field_type.is_date() {
return Ok(CellBytes::default());
}
self.display_data(cell_data, decoded_field_type, field_rev)
}
fn apply_changeset(
&self,
changeset: CellDataChangeset<String>,
@@ -120,6 +120,21 @@ where
) -> FlowyResult<CellBytes> {
CellBytes::from(self.selected_select_option(cell_data))
}
fn display_string(
&self,
cell_data: CellData<SelectOptionIds>,
_decoded_field_type: &FieldType,
_field_rev: &FieldRevision,
) -> FlowyResult<String> {
Ok(self
.selected_select_option(cell_data)
.select_options
.into_iter()
.map(|option| option.name)
.collect::<Vec<String>>()
.join(SELECTION_IDS_SEPARATOR))
}
}
pub fn select_option_operation(field_rev: &FieldRevision) -> FlowyResult<Box<dyn SelectOptionOperation>> {
@@ -1,3 +1,5 @@
#![allow(clippy::module_inception)]
mod text_tests;
mod text_type_option;
pub use text_type_option::*;
@@ -0,0 +1,49 @@
#[cfg(test)]
mod tests {
use crate::entities::FieldType;
use crate::services::cell::CellDataOperation;
use crate::services::field::FieldBuilder;
use crate::services::field::*;
// Test parser the cell data which field's type is FieldType::Date to cell data
// which field's type is FieldType::Text
#[test]
fn date_type_to_text_type() {
let type_option = RichTextTypeOptionPB::default();
let field_type = FieldType::DateTime;
let field_rev = FieldBuilder::from_field_type(&field_type).build();
assert_eq!(
type_option
.decode_cell_data(1647251762.into(), &field_type, &field_rev)
.unwrap()
.parser::<TextCellDataParser>()
.unwrap()
.as_ref(),
"Mar 14,2022"
);
}
// Test parser the cell data which field's type is FieldType::SingleSelect to cell data
// which field's type is FieldType::Text
#[test]
fn single_select_to_text_type() {
let type_option = RichTextTypeOptionPB::default();
let field_type = FieldType::SingleSelect;
let done_option = SelectOptionPB::new("Done");
let option_id = done_option.id.clone();
let single_select = SingleSelectTypeOptionBuilder::default().add_option(done_option.clone());
let field_rev = FieldBuilder::new(single_select).build();
assert_eq!(
type_option
.decode_cell_data(option_id.into(), &field_type, &field_rev)
.unwrap()
.parser::<TextCellDataParser>()
.unwrap()
.to_string(),
done_option.name,
);
}
}
@@ -1,8 +1,8 @@
use crate::entities::FieldType;
use crate::impl_type_option;
use crate::services::cell::{
try_decode_cell_data, CellBytes, CellBytesParser, CellData, CellDataChangeset, CellDataOperation, CellDisplayable,
FromCellString,
decode_cell_data_to_string, CellBytes, CellBytesParser, CellData, CellDataChangeset, CellDataOperation,
CellDisplayable, FromCellString,
};
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
use bytes::Bytes;
@@ -44,6 +44,16 @@ impl CellDisplayable<String> for RichTextTypeOptionPB {
let cell_str: String = cell_data.try_into_inner()?;
Ok(CellBytes::new(cell_str))
}
fn display_string(
&self,
cell_data: CellData<String>,
_decoded_field_type: &FieldType,
_field_rev: &FieldRevision,
) -> FlowyResult<String> {
let cell_str: String = cell_data.try_into_inner()?;
Ok(cell_str)
}
}
impl CellDataOperation<String, String> for RichTextTypeOptionPB {
@@ -57,8 +67,10 @@ impl CellDataOperation<String, String> for RichTextTypeOptionPB {
|| decoded_field_type.is_single_select()
|| decoded_field_type.is_multi_select()
|| decoded_field_type.is_number()
|| decoded_field_type.is_url()
{
try_decode_cell_data(cell_data, field_rev, decoded_field_type, decoded_field_type)
let s = decode_cell_data_to_string(cell_data, decoded_field_type, decoded_field_type, field_rev);
Ok(CellBytes::new(s.unwrap_or_else(|_| "".to_owned())))
} else {
self.display_data(cell_data, decoded_field_type, field_rev)
}
@@ -85,6 +97,14 @@ impl AsRef<str> for TextCellData {
}
}
impl std::ops::Deref for TextCellData {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl FromCellString for TextCellData {
fn from_cell_str(s: &str) -> FlowyResult<Self>
where
@@ -94,6 +114,12 @@ impl FromCellString for TextCellData {
}
}
impl ToString for TextCellData {
fn to_string(&self) -> String {
self.0.clone()
}
}
pub struct TextCellDataParser();
impl CellBytesParser for TextCellDataParser {
type Object = TextCellData;
@@ -104,85 +130,3 @@ impl CellBytesParser for TextCellDataParser {
}
}
}
#[cfg(test)]
mod tests {
use crate::entities::FieldType;
use crate::services::cell::CellDataOperation;
use crate::services::field::FieldBuilder;
use crate::services::field::*;
#[test]
fn text_description_test() {
let type_option = RichTextTypeOptionPB::default();
// date
let field_type = FieldType::DateTime;
let date_time_field_rev = FieldBuilder::from_field_type(&field_type).build();
assert_eq!(
type_option
.decode_cell_data(1647251762.to_string().into(), &field_type, &date_time_field_rev)
.unwrap()
.parser::<DateCellDataParser>()
.unwrap()
.date,
"Mar 14,2022".to_owned()
);
// Single select
let done_option = SelectOptionPB::new("Done");
let done_option_id = done_option.id.clone();
let single_select = SingleSelectTypeOptionBuilder::default().add_option(done_option.clone());
let single_select_field_rev = FieldBuilder::new(single_select).build();
assert_eq!(
type_option
.decode_cell_data(
done_option_id.into(),
&FieldType::SingleSelect,
&single_select_field_rev
)
.unwrap()
.parser::<SelectOptionCellDataParser>()
.unwrap()
.select_options,
vec![done_option],
);
// Multiple select
let google_option = SelectOptionPB::new("Google");
let facebook_option = SelectOptionPB::new("Facebook");
let ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR);
let cell_data_changeset = SelectOptionCellChangeset::from_insert(&ids).to_str();
let multi_select = MultiSelectTypeOptionBuilder::default()
.add_option(google_option.clone())
.add_option(facebook_option.clone());
let multi_select_field_rev = FieldBuilder::new(multi_select).build();
let multi_type_option = MultiSelectTypeOptionPB::from(&multi_select_field_rev);
let cell_data = multi_type_option
.apply_changeset(cell_data_changeset.into(), None)
.unwrap();
assert_eq!(
type_option
.decode_cell_data(cell_data.into(), &FieldType::MultiSelect, &multi_select_field_rev)
.unwrap()
.parser::<SelectOptionCellDataParser>()
.unwrap()
.select_options,
vec![google_option, facebook_option]
);
//Number
let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD);
let number_field_rev = FieldBuilder::new(number).build();
assert_eq!(
type_option
.decode_cell_data("18443".to_owned().into(), &FieldType::Number, &number_field_rev)
.unwrap()
.to_string(),
"$18,443".to_owned()
);
}
}
@@ -42,6 +42,16 @@ impl CellDisplayable<URLCellDataPB> for URLTypeOptionPB {
let cell_data: URLCellDataPB = cell_data.try_into_inner()?;
CellBytes::from(cell_data)
}
fn display_string(
&self,
cell_data: CellData<URLCellDataPB>,
_decoded_field_type: &FieldType,
_field_rev: &FieldRevision,
) -> FlowyResult<String> {
let cell_data: URLCellDataPB = cell_data.try_into_inner()?;
Ok(cell_data.content)
}
}
impl CellDataOperation<URLCellDataPB, String> for URLTypeOptionPB {
@@ -435,14 +435,18 @@ impl GridRevisionEditor {
}
pub async fn get_cell(&self, params: &GridCellIdParams) -> Option<GridCellPB> {
let cell_bytes = self.get_cell_bytes(params).await?;
Some(GridCellPB::new(&params.field_id, cell_bytes.to_vec()))
let (field_type, cell_bytes) = self.decode_any_cell_data(params).await?;
Some(GridCellPB::new(&params.field_id, field_type, cell_bytes.to_vec()))
}
pub async fn get_cell_bytes(&self, params: &GridCellIdParams) -> Option<CellBytes> {
let (_, cell_data) = self.decode_any_cell_data(params).await?;
Some(cell_data)
}
async fn decode_any_cell_data(&self, params: &GridCellIdParams) -> Option<(FieldType, CellBytes)> {
let field_rev = self.get_field_rev(&params.field_id).await?;
let row_rev = self.block_manager.get_row_rev(&params.row_id).await.ok()??;
let cell_rev = row_rev.cells.get(&params.field_id)?.clone();
Some(decode_any_cell_data(cell_rev.data, &field_rev))
}
@@ -201,6 +201,10 @@ impl GridViewRevisionEditor {
Ok(())
}
pub(crate) async fn group_id(&self) -> String {
self.group_controller.read().await.field_id().to_owned()
}
pub(crate) async fn get_setting(&self) -> GridSettingPB {
let field_revs = self.field_delegate.get_field_revs().await;
let grid_setting = make_grid_setting(&*self.pad.read().await, &field_revs);
@@ -178,12 +178,16 @@ impl GridViewManager {
#[tracing::instrument(level = "trace", skip(self), err)]
pub(crate) async fn did_update_field(&self, field_id: &str, is_type_option_changed: bool) -> FlowyResult<()> {
let view_editor = self.get_default_view_editor().await?;
// Only the field_id of the updated field is equal to the field_id of the group.
// Update the group
if view_editor.group_id().await != field_id {
return Ok(());
}
if is_type_option_changed {
let _ = view_editor.group_by_field(field_id).await?;
} else {
let _ = view_editor.did_update_field(field_id).await?;
}
Ok(())
}
@@ -205,7 +205,7 @@ where
if let Some(cell_rev) = cell_rev {
let mut grouped_rows: Vec<GroupedRow> = vec![];
let cell_bytes = decode_any_cell_data(cell_rev.data, field_rev);
let cell_bytes = decode_any_cell_data(cell_rev.data, field_rev).1;
let cell_data = cell_bytes.parser::<P>()?;
for group in self.group_ctx.groups() {
if self.can_group(&group.filter_content, &cell_data) {
@@ -244,7 +244,7 @@ where
field_rev: &FieldRevision,
) -> FlowyResult<Vec<GroupChangesetPB>> {
if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev);
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev).1;
let cell_data = cell_bytes.parser::<P>()?;
let mut changesets = self.add_row_if_match(row_rev, &cell_data);
let default_group_changeset = self.update_default_group(row_rev, &changesets);
@@ -265,7 +265,7 @@ where
) -> FlowyResult<Vec<GroupChangesetPB>> {
// if the cell_rev is none, then the row must be crated from the default group.
if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev);
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev).1;
let cell_data = cell_bytes.parser::<P>()?;
Ok(self.remove_row_if_match(row_rev, &cell_data))
} else {
@@ -285,7 +285,7 @@ where
};
if let Some(cell_rev) = cell_rev {
let cell_bytes = decode_any_cell_data(cell_rev.data, context.field_rev);
let cell_bytes = decode_any_cell_data(cell_rev.data, context.field_rev).1;
let cell_data = cell_bytes.parser::<P>()?;
Ok(self.move_row(&cell_data, context))
} else {