1use thiserror::Error;
2
3use crate::legacy::{LegacyColumn, LegacyRow, LegacyTable, LegacyTableBuilder};
4use crate::modern::{ModernColumn, ModernRow, ModernTable, ModernTableBuilder};
5use crate::{BdatVersion, Cell, LegacyVersion, RowId, ValueType};
6
7use super::column::ColumnMap;
8
9#[derive(Error, Debug)]
12pub enum FormatConvertError {
13 #[error("unsupported value type {0:?}")]
17 UnsupportedValueType(ValueType),
18 #[error("unsupported cell")]
22 UnsupportedCell,
23 #[error("max row count exceeded")]
26 MaxRowCountExceeded,
27 #[error("unsupported row ID {0}")]
31 UnsupportedRowId(RowId),
32 #[error("unsupported label type")]
34 UnsupportedLabelType,
35}
36
37impl<'b> TryFrom<ModernColumn<'b>> for LegacyColumn<'b> {
40 type Error = FormatConvertError;
41
42 fn try_from(modern_col: ModernColumn<'b>) -> Result<Self, Self::Error> {
43 if !modern_col
45 .value_type()
46 .is_supported(BdatVersion::Legacy(LegacyVersion::Switch))
47 {
48 return Err(FormatConvertError::UnsupportedValueType(
49 modern_col.value_type(),
50 ));
51 }
52 Ok(Self {
53 value_type: modern_col.value_type,
54 label: modern_col
55 .label
56 .try_into()
57 .map_err(|_| FormatConvertError::UnsupportedLabelType)?,
58 count: 1,
59 flags: Vec::new(),
60 })
61 }
62}
63
64impl<'b> From<ModernRow<'b>> for LegacyRow<'b> {
65 fn from(value: ModernRow<'b>) -> Self {
66 Self {
67 cells: value.values.into_iter().map(Cell::Single).collect(),
68 }
69 }
70}
71
72impl<'b> TryFrom<ModernTable<'b>> for LegacyTable<'b> {
73 type Error = FormatConvertError;
74
75 fn try_from(modern_table: ModernTable<'b>) -> Result<Self, Self::Error> {
76 let rows: Vec<_> = modern_table.rows.into_iter().map(Into::into).collect();
77 let base_id = u16::try_from(modern_table.base_id)
78 .map_err(|_| FormatConvertError::UnsupportedRowId(modern_table.base_id))?;
79 let name = modern_table
80 .name
81 .try_into()
82 .map_err(|_| FormatConvertError::UnsupportedLabelType)?;
83 let columns: Result<ColumnMap<_, _>, FormatConvertError> = modern_table
84 .columns
85 .into_iter()
86 .map(TryInto::try_into)
87 .collect();
88 let row_len =
89 u16::try_from(rows.len()).map_err(|_| FormatConvertError::MaxRowCountExceeded)?;
90 if base_id.checked_add(row_len).is_none() {
91 return Err(FormatConvertError::UnsupportedRowId(u16::MAX as u32));
94 }
95 Ok(LegacyTableBuilder::from_table(name, base_id, columns?, rows).build())
96 }
97}
98
99impl<'b> TryFrom<LegacyColumn<'b>> for ModernColumn<'b> {
102 type Error = FormatConvertError;
103
104 fn try_from(legacy_col: LegacyColumn<'b>) -> Result<Self, Self::Error> {
105 if !legacy_col.value_type().is_supported(BdatVersion::Modern) {
106 return Err(FormatConvertError::UnsupportedValueType(
107 legacy_col.value_type(),
108 ));
109 }
110 Ok(Self {
111 value_type: legacy_col.value_type,
112 label: legacy_col.label.into(),
113 })
114 }
115}
116
117impl<'b> TryFrom<LegacyRow<'b>> for ModernRow<'b> {
118 type Error = FormatConvertError;
119
120 fn try_from(legacy_row: LegacyRow<'b>) -> Result<Self, Self::Error> {
121 let values: Result<Vec<_>, FormatConvertError> = legacy_row
122 .into_cells()
123 .map(|c| match c {
124 Cell::Single(v) => Ok(v),
125 _ => Err(FormatConvertError::UnsupportedCell),
126 })
127 .collect();
128 Ok(Self { values: values? })
129 }
130}
131
132impl<'b> TryFrom<LegacyTable<'b>> for ModernTable<'b> {
133 type Error = FormatConvertError;
134
135 fn try_from(legacy_table: LegacyTable<'b>) -> Result<Self, Self::Error> {
136 let columns: Result<ColumnMap<_>, FormatConvertError> = legacy_table
137 .columns
138 .into_iter()
139 .map(TryInto::try_into)
140 .collect();
141 let rows: Result<Vec<_>, FormatConvertError> = legacy_table
142 .rows
143 .into_iter()
144 .map(TryInto::try_into)
145 .collect();
146
147 Ok(ModernTableBuilder::from_table(
148 legacy_table.name.into(),
149 legacy_table.base_id as u32,
150 columns?,
151 rows?,
152 )
153 .build())
154 }
155}