oxidize_pdf/advanced_tables/
error.rs1use thiserror::Error;
4
5#[derive(Debug, Error, Clone, PartialEq)]
7pub enum TableError {
8 #[error("Table must have at least one column")]
10 NoColumns,
11
12 #[error("Row {row} has {found} cells but expected {expected} columns")]
14 ColumnMismatch {
15 row: usize,
17 found: usize,
19 expected: usize,
21 },
22
23 #[error(
25 "Header cell at level {level} extends beyond table width ({start} + {span} > {total})"
26 )]
27 HeaderOutOfBounds {
28 level: usize,
30 start: usize,
32 span: usize,
34 total: usize,
36 },
37
38 #[error("Overlapping header cells at level {level} column {column}")]
40 HeaderOverlap {
41 level: usize,
43 column: usize,
45 },
46
47 #[error("Invalid column width {width} at column {column}: must be positive")]
49 InvalidColumnWidth {
50 column: usize,
52 width: f64,
54 },
55
56 #[error("Invalid row height {height} at row {row}: must be positive")]
58 InvalidRowHeight {
59 row: usize,
61 height: f64,
63 },
64
65 #[error("Invalid {span_type} span {span} at row {row} col {col}: must be at least 1")]
67 InvalidCellSpan {
68 span_type: String,
70 row: usize,
72 col: usize,
74 span: usize,
76 },
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn test_no_columns_error() {
85 let err = TableError::NoColumns;
86 assert_eq!(format!("{}", err), "Table must have at least one column");
87 }
88
89 #[test]
90 fn test_column_mismatch_error() {
91 let err = TableError::ColumnMismatch {
92 row: 2,
93 found: 3,
94 expected: 5,
95 };
96 let msg = format!("{}", err);
97 assert!(msg.contains("Row 2"));
98 assert!(msg.contains("3 cells"));
99 assert!(msg.contains("5 columns"));
100 }
101
102 #[test]
103 fn test_header_out_of_bounds_error() {
104 let err = TableError::HeaderOutOfBounds {
105 level: 1,
106 start: 3,
107 span: 4,
108 total: 5,
109 };
110 let msg = format!("{}", err);
111 assert!(msg.contains("level 1"));
112 assert!(msg.contains("3 + 4 > 5"));
113 }
114
115 #[test]
116 fn test_header_overlap_error() {
117 let err = TableError::HeaderOverlap {
118 level: 0,
119 column: 2,
120 };
121 let msg = format!("{}", err);
122 assert!(msg.contains("Overlapping"));
123 assert!(msg.contains("level 0"));
124 assert!(msg.contains("column 2"));
125 }
126
127 #[test]
128 fn test_invalid_column_width_error() {
129 let err = TableError::InvalidColumnWidth {
130 column: 3,
131 width: -10.5,
132 };
133 let msg = format!("{}", err);
134 assert!(msg.contains("Invalid column width"));
135 assert!(msg.contains("-10.5"));
136 assert!(msg.contains("column 3"));
137 assert!(msg.contains("must be positive"));
138 }
139
140 #[test]
141 fn test_invalid_column_width_zero() {
142 let err = TableError::InvalidColumnWidth {
143 column: 0,
144 width: 0.0,
145 };
146 let msg = format!("{}", err);
147 assert!(msg.contains("0"));
148 }
149
150 #[test]
151 fn test_invalid_row_height_error() {
152 let err = TableError::InvalidRowHeight {
153 row: 5,
154 height: -2.0,
155 };
156 let msg = format!("{}", err);
157 assert!(msg.contains("Invalid row height"));
158 assert!(msg.contains("-2"));
159 assert!(msg.contains("row 5"));
160 assert!(msg.contains("must be positive"));
161 }
162
163 #[test]
164 fn test_invalid_cell_span_colspan() {
165 let err = TableError::InvalidCellSpan {
166 span_type: "colspan".to_string(),
167 row: 1,
168 col: 2,
169 span: 0,
170 };
171 let msg = format!("{}", err);
172 assert!(msg.contains("Invalid colspan span"));
173 assert!(msg.contains("row 1"));
174 assert!(msg.contains("col 2"));
175 assert!(msg.contains("must be at least 1"));
176 }
177
178 #[test]
179 fn test_invalid_cell_span_rowspan() {
180 let err = TableError::InvalidCellSpan {
181 span_type: "rowspan".to_string(),
182 row: 3,
183 col: 4,
184 span: 0,
185 };
186 let msg = format!("{}", err);
187 assert!(msg.contains("Invalid rowspan span"));
188 }
189
190 #[test]
191 fn test_error_clone() {
192 let err1 = TableError::NoColumns;
193 let err2 = err1.clone();
194 assert_eq!(err1, err2);
195 }
196
197 #[test]
198 fn test_error_partial_eq() {
199 let err1 = TableError::ColumnMismatch {
200 row: 1,
201 found: 2,
202 expected: 3,
203 };
204 let err2 = TableError::ColumnMismatch {
205 row: 1,
206 found: 2,
207 expected: 3,
208 };
209 let err3 = TableError::ColumnMismatch {
210 row: 1,
211 found: 2,
212 expected: 4,
213 };
214
215 assert_eq!(err1, err2);
216 assert_ne!(err1, err3);
217 }
218
219 #[test]
220 fn test_error_debug() {
221 let err = TableError::NoColumns;
222 let debug_str = format!("{:?}", err);
223 assert!(debug_str.contains("NoColumns"));
224 }
225
226 #[test]
227 fn test_all_variants_debug() {
228 let variants: Vec<TableError> = vec![
229 TableError::NoColumns,
230 TableError::ColumnMismatch {
231 row: 0,
232 found: 1,
233 expected: 2,
234 },
235 TableError::HeaderOutOfBounds {
236 level: 0,
237 start: 0,
238 span: 1,
239 total: 1,
240 },
241 TableError::HeaderOverlap {
242 level: 0,
243 column: 0,
244 },
245 TableError::InvalidColumnWidth {
246 column: 0,
247 width: -1.0,
248 },
249 TableError::InvalidRowHeight {
250 row: 0,
251 height: -1.0,
252 },
253 TableError::InvalidCellSpan {
254 span_type: "test".to_string(),
255 row: 0,
256 col: 0,
257 span: 0,
258 },
259 ];
260
261 for err in variants {
262 let debug_str = format!("{:?}", err);
263 assert!(!debug_str.is_empty());
264 }
265 }
266
267 #[test]
268 fn test_error_is_send_sync() {
269 fn assert_send_sync<T: Send + Sync>() {}
270 assert_send_sync::<TableError>();
271 }
272
273 #[test]
274 fn test_error_is_std_error() {
275 fn assert_error<T: std::error::Error>(_: &T) {}
276 let err = TableError::NoColumns;
277 assert_error(&err);
278 }
279
280 #[test]
281 fn test_large_indices() {
282 let err = TableError::ColumnMismatch {
283 row: usize::MAX,
284 found: usize::MAX - 1,
285 expected: 100,
286 };
287 let msg = format!("{}", err);
288 assert!(!msg.is_empty());
289 }
290
291 #[test]
292 fn test_special_float_values() {
293 let err = TableError::InvalidColumnWidth {
295 column: 0,
296 width: f64::INFINITY,
297 };
298 let msg = format!("{}", err);
299 assert!(msg.contains("inf"));
300
301 let err = TableError::InvalidRowHeight {
303 row: 0,
304 height: f64::NAN,
305 };
306 let msg = format!("{}", err);
307 assert!(msg.contains("NaN") || msg.contains("nan"));
308 }
309}