use thiserror::Error;
use crate::{
BdatVersion, Cell, ColumnMap, LegacyColumn, LegacyRow, LegacyTable, LegacyTableBuilder,
LegacyVersion, ModernColumn, ModernRow, ModernTable, ModernTableBuilder, RowId, ValueType,
};
#[derive(Error, Debug)]
pub enum FormatConvertError {
#[error("unsupported value type {0:?}")]
UnsupportedValueType(ValueType),
#[error("unsupported cell")]
UnsupportedCell,
#[error("max row count exceeded")]
MaxRowCountExceeded,
#[error("unsupported row ID {0}")]
UnsupportedRowId(RowId),
#[error("unsupported label type")]
UnsupportedLabelType,
}
impl<'b> TryFrom<ModernColumn<'b>> for LegacyColumn<'b> {
type Error = FormatConvertError;
fn try_from(modern_col: ModernColumn<'b>) -> Result<Self, Self::Error> {
if !modern_col
.value_type()
.is_supported(BdatVersion::Legacy(LegacyVersion::Switch))
{
return Err(FormatConvertError::UnsupportedValueType(
modern_col.value_type(),
));
}
Ok(Self {
value_type: modern_col.value_type,
label: modern_col
.label
.try_into()
.map_err(|_| FormatConvertError::UnsupportedLabelType)?,
count: 1,
flags: Vec::new(),
})
}
}
impl<'b> From<ModernRow<'b>> for LegacyRow<'b> {
fn from(value: ModernRow<'b>) -> Self {
Self {
cells: value.values.into_iter().map(Cell::Single).collect(),
}
}
}
impl<'b> TryFrom<ModernTable<'b>> for LegacyTable<'b> {
type Error = FormatConvertError;
fn try_from(modern_table: ModernTable<'b>) -> Result<Self, Self::Error> {
let rows: Vec<_> = modern_table.rows.into_iter().map(Into::into).collect();
let base_id = u16::try_from(modern_table.base_id)
.map_err(|_| FormatConvertError::UnsupportedRowId(modern_table.base_id))?;
let name = modern_table
.name
.try_into()
.map_err(|_| FormatConvertError::UnsupportedLabelType)?;
let columns: Result<ColumnMap<_, _>, FormatConvertError> = modern_table
.columns
.into_iter()
.map(TryInto::try_into)
.collect();
let row_len =
u16::try_from(rows.len()).map_err(|_| FormatConvertError::MaxRowCountExceeded)?;
if base_id.checked_add(row_len).is_none() {
return Err(FormatConvertError::UnsupportedRowId(u16::MAX as u32));
}
Ok(LegacyTableBuilder::from_table(name, base_id, columns?, rows).build())
}
}
impl<'b> TryFrom<LegacyColumn<'b>> for ModernColumn<'b> {
type Error = FormatConvertError;
fn try_from(legacy_col: LegacyColumn<'b>) -> Result<Self, Self::Error> {
if !legacy_col.value_type().is_supported(BdatVersion::Modern) {
return Err(FormatConvertError::UnsupportedValueType(
legacy_col.value_type(),
));
}
Ok(Self {
value_type: legacy_col.value_type,
label: legacy_col.label.into(),
})
}
}
impl<'b> TryFrom<LegacyRow<'b>> for ModernRow<'b> {
type Error = FormatConvertError;
fn try_from(legacy_row: LegacyRow<'b>) -> Result<Self, Self::Error> {
let values: Result<Vec<_>, FormatConvertError> = legacy_row
.into_cells()
.map(|c| match c {
Cell::Single(v) => Ok(v),
_ => Err(FormatConvertError::UnsupportedCell),
})
.collect();
Ok(Self { values: values? })
}
}
impl<'b> TryFrom<LegacyTable<'b>> for ModernTable<'b> {
type Error = FormatConvertError;
fn try_from(legacy_table: LegacyTable<'b>) -> Result<Self, Self::Error> {
let columns: Result<ColumnMap<_>, FormatConvertError> = legacy_table
.columns
.into_iter()
.map(TryInto::try_into)
.collect();
let rows: Result<Vec<_>, FormatConvertError> = legacy_table
.rows
.into_iter()
.map(TryInto::try_into)
.collect();
Ok(ModernTableBuilder::from_table(
legacy_table.name.into(),
legacy_table.base_id as u32,
columns?,
rows?,
)
.build())
}
}