calcsheet-native 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,815 @@
1
+ //! The cells of one table of an ODS `content.xml`, read as calcsheet's `Ods::ContentParser` reads them through Ox
2
+ //! (`skip: :skip_off`, `convert_special: false`, names with their prefixes), for the XML subset of `xml.rs`.
3
+ //! Anything else, and any value calcsheet would refuse, makes the table `Unsupported`: the Ruby side then reads it
4
+ //! with calcsheet's own parser. The state machine follows `ContentParser`'s callbacks one for one; the stream starts
5
+ //! at the table start tag, or at a row inside it (see calcsheet's `Scanner#io_at_sheet`), and ends with the table.
6
+
7
+ use std::collections::HashMap;
8
+
9
+ use memchr::memchr;
10
+
11
+ use crate::xml::{Item, Lexer, Result, Unsupported, decode, utf8};
12
+
13
+ /// The rows and columns of calcsheet's grid.
14
+ const MAX_ROWS: u64 = 1_048_576;
15
+ const MAX_COLUMNS: u64 = 16_384;
16
+ /// Cast::MAX_NUMBER_BYTES.
17
+ const MAX_NUMBER_BYTES: usize = 1_024;
18
+ /// Odf::MAX_SPACES.
19
+ const MAX_SPACES: u64 = 32_767;
20
+ /// Count::MAX_DIGITS.
21
+ const MAX_DIGITS: usize = 18;
22
+
23
+ /// The elements and attributes ContentParser reads, by qualified name.
24
+ #[derive(Clone, Copy, PartialEq, Eq)]
25
+ enum Code {
26
+ Table,
27
+ Row,
28
+ Cell,
29
+ Covered,
30
+ Paragraph,
31
+ Spaces,
32
+ Tab,
33
+ LineBreak,
34
+ Column,
35
+ }
36
+
37
+ #[derive(Clone, Copy, PartialEq, Eq)]
38
+ enum Attribute {
39
+ ValueType,
40
+ Value,
41
+ DateValue,
42
+ TimeValue,
43
+ BooleanValue,
44
+ StringValue,
45
+ CalcValueType,
46
+ ColumnsRepeated,
47
+ RowsRepeated,
48
+ SpaceCount,
49
+ StyleName,
50
+ DefaultStyle,
51
+ }
52
+
53
+ /// The kind of a cell's value type (ContentParser::KINDS), `Text` for another one.
54
+ #[derive(Clone, Copy, PartialEq, Eq)]
55
+ enum ValueKind {
56
+ Number,
57
+ IsoDate,
58
+ Duration,
59
+ Boolean,
60
+ String,
61
+ Text,
62
+ }
63
+
64
+ impl ValueKind {
65
+ /// ContentParser::VALUE_OF: the attribute that holds the value of the kind.
66
+ fn value_of(self) -> Option<Attribute> {
67
+ match self {
68
+ ValueKind::Number => Some(Attribute::Value),
69
+ ValueKind::IsoDate => Some(Attribute::DateValue),
70
+ ValueKind::Duration => Some(Attribute::TimeValue),
71
+ ValueKind::Boolean => Some(Attribute::BooleanValue),
72
+ ValueKind::String => Some(Attribute::StringValue),
73
+ ValueKind::Text => None,
74
+ }
75
+ }
76
+ }
77
+
78
+ /// The RowBuilder kind of a cell, as the Ruby side maps it.
79
+ #[derive(Clone, Copy)]
80
+ pub enum Kind {
81
+ Text = 0,
82
+ Number = 1,
83
+ IsoDate = 2,
84
+ Duration = 3,
85
+ Boolean = 4,
86
+ Error = 5,
87
+ Missing = 6,
88
+ }
89
+
90
+ /// A cell with a value: its first column, its repeat, kind, the reason of a missing value (0 :invalid_decimal, 1
91
+ /// :invalid_date, 2 :invalid_boolean), its raw value in `out`, and its format: the index of its style's format, -1
92
+ /// for a style without one, -2 for no style (the format of its column).
93
+ pub struct Cell {
94
+ pub column: u64,
95
+ pub repeat: u64,
96
+ pub kind: Kind,
97
+ pub missing: u8,
98
+ pub start: usize,
99
+ pub end: usize,
100
+ pub format: i64,
101
+ }
102
+
103
+ /// A row with cells: its first number, its last (a repeated row) and its cells.
104
+ pub struct Row {
105
+ pub number: u64,
106
+ pub last: u64,
107
+ pub cells: Vec<Cell>,
108
+ }
109
+
110
+ pub enum Stop {
111
+ NeedMore,
112
+ End,
113
+ Unsupported,
114
+ Full,
115
+ }
116
+
117
+ pub struct TableReader {
118
+ lexer: Lexer,
119
+ max_cell_bytes: Option<usize>,
120
+ start_row: u64,
121
+ done: bool,
122
+ unsupported: bool,
123
+ just_started: bool,
124
+ // Few names: a scan of the byte strings costs less than hashing each name.
125
+ elements: Vec<(Vec<u8>, Code)>,
126
+ attributes: Vec<(Vec<u8>, Attribute)>,
127
+ text_qualifier: Vec<u8>,
128
+ formats: HashMap<Vec<u8>, usize>,
129
+ // The texts of the rows handed out, and the bytes of them the next batch drops.
130
+ out: Vec<u8>,
131
+ consumed: usize,
132
+ row_start: usize,
133
+ // ContentParser's state.
134
+ next_row: u64,
135
+ columns: u64,
136
+ /// `[first, last, format]` of the columns whose default cell style has a format.
137
+ pub column_formats: Vec<(u64, u64, usize)>,
138
+ element: Option<Code>,
139
+ column_repeated: u64,
140
+ column_style: Option<i64>,
141
+ in_row: bool,
142
+ row_number: u64,
143
+ rows_repeated: u64,
144
+ row_style: Option<i64>,
145
+ column: u64,
146
+ in_cell: bool,
147
+ covered: bool,
148
+ depth: u32,
149
+ columns_repeated: u64,
150
+ kind: ValueKind,
151
+ value: Vec<u8>,
152
+ value_code: Option<Attribute>,
153
+ cell_style: Option<i64>,
154
+ error: bool,
155
+ text: Option<usize>,
156
+ paragraphs: u32,
157
+ paragraph: bool,
158
+ capture: bool,
159
+ ignored: Option<u32>,
160
+ spaces: u64,
161
+ space_count: Option<Vec<u8>>,
162
+ ignore_space: bool,
163
+ cells: Vec<Cell>,
164
+ }
165
+
166
+ impl TableReader {
167
+ /// `prefixes` are those of the table, office, text and calcext namespaces; `formats` the cell style names that
168
+ /// have a format, in the order of the formats the Ruby side holds.
169
+ pub fn new(
170
+ prefixes: [&str; 4],
171
+ formats: Vec<Vec<u8>>,
172
+ max_cell_bytes: Option<usize>,
173
+ first_row: u64,
174
+ start_row: u64,
175
+ ) -> Self {
176
+ let qualified = |prefix: &str, local: &str| {
177
+ if prefix.is_empty() {
178
+ local.as_bytes().to_vec()
179
+ } else {
180
+ format!("{prefix}:{local}").into_bytes()
181
+ }
182
+ };
183
+ let [table, office, text, calcext] = prefixes;
184
+ let elements = [
185
+ (qualified(table, "table"), Code::Table),
186
+ (qualified(table, "table-row"), Code::Row),
187
+ (qualified(table, "table-cell"), Code::Cell),
188
+ (qualified(table, "covered-table-cell"), Code::Covered),
189
+ (qualified(table, "table-column"), Code::Column),
190
+ (qualified(text, "p"), Code::Paragraph),
191
+ (qualified(text, "s"), Code::Spaces),
192
+ (qualified(text, "tab"), Code::Tab),
193
+ (qualified(text, "line-break"), Code::LineBreak),
194
+ ]
195
+ .into();
196
+ let attributes = [
197
+ (
198
+ qualified(table, "number-rows-repeated"),
199
+ Attribute::RowsRepeated,
200
+ ),
201
+ (
202
+ qualified(table, "number-columns-repeated"),
203
+ Attribute::ColumnsRepeated,
204
+ ),
205
+ (qualified(office, "value-type"), Attribute::ValueType),
206
+ (qualified(office, "value"), Attribute::Value),
207
+ (qualified(office, "date-value"), Attribute::DateValue),
208
+ (qualified(office, "time-value"), Attribute::TimeValue),
209
+ (qualified(office, "boolean-value"), Attribute::BooleanValue),
210
+ (qualified(office, "string-value"), Attribute::StringValue),
211
+ (qualified(calcext, "value-type"), Attribute::CalcValueType),
212
+ (qualified(text, "c"), Attribute::SpaceCount),
213
+ (qualified(table, "style-name"), Attribute::StyleName),
214
+ (
215
+ qualified(table, "default-cell-style-name"),
216
+ Attribute::DefaultStyle,
217
+ ),
218
+ ]
219
+ .into();
220
+ TableReader {
221
+ lexer: Lexer::new(),
222
+ max_cell_bytes,
223
+ start_row,
224
+ done: false,
225
+ unsupported: false,
226
+ just_started: false,
227
+ elements,
228
+ attributes,
229
+ text_qualifier: if text.is_empty() {
230
+ Vec::new()
231
+ } else {
232
+ format!("{text}:").into_bytes()
233
+ },
234
+ formats: formats
235
+ .into_iter()
236
+ .enumerate()
237
+ .map(|(index, name)| (name, index))
238
+ .collect(),
239
+ out: Vec::new(),
240
+ consumed: 0,
241
+ row_start: 0,
242
+ next_row: first_row,
243
+ columns: 0,
244
+ column_formats: Vec::new(),
245
+ element: None,
246
+ column_repeated: 1,
247
+ column_style: None,
248
+ in_row: false,
249
+ row_number: 0,
250
+ rows_repeated: 1,
251
+ row_style: None,
252
+ column: 0,
253
+ in_cell: false,
254
+ covered: false,
255
+ depth: 0,
256
+ columns_repeated: 1,
257
+ kind: ValueKind::Text,
258
+ value: Vec::new(),
259
+ value_code: None,
260
+ cell_style: None,
261
+ error: false,
262
+ text: None,
263
+ paragraphs: 0,
264
+ paragraph: false,
265
+ capture: false,
266
+ ignored: None,
267
+ spaces: 0,
268
+ space_count: None,
269
+ ignore_space: false,
270
+ cells: Vec::new(),
271
+ }
272
+ }
273
+
274
+ pub fn feed(&mut self, chunk: &[u8]) {
275
+ self.lexer.feed(chunk);
276
+ }
277
+
278
+ pub fn finish(&mut self) {
279
+ self.lexer.eof = true;
280
+ }
281
+
282
+ /// The raw value of a cell of the last batch.
283
+ pub fn value(&self, cell: &Cell) -> &[u8] {
284
+ &self.out[cell.start..cell.end]
285
+ }
286
+
287
+ /// Reads items until `max` rows with cells are complete, the text fed so far ends, or the table ends.
288
+ pub fn rows(&mut self, max: usize) -> (Vec<Row>, Stop) {
289
+ self.drop_consumed();
290
+ let mut rows = Vec::new();
291
+ let stop = if self.unsupported {
292
+ Stop::Unsupported
293
+ } else if self.done {
294
+ Stop::End
295
+ } else {
296
+ self.read(max, &mut rows)
297
+ };
298
+ if matches!(stop, Stop::Unsupported) {
299
+ self.unsupported = true;
300
+ }
301
+ self.consumed = if self.in_row {
302
+ self.row_start
303
+ } else {
304
+ self.out.len()
305
+ };
306
+ (rows, stop)
307
+ }
308
+
309
+ fn read(&mut self, max: usize, rows: &mut Vec<Row>) -> Stop {
310
+ while rows.len() < max {
311
+ let result = match self.lexer.item() {
312
+ Ok(Some(item)) => self.handle(item, rows),
313
+ // The table ends with its end tag: an end of the text before it is left to Ox.
314
+ Ok(None) if self.lexer.eof => return Stop::Unsupported,
315
+ Ok(None) => return Stop::NeedMore,
316
+ Err(Unsupported) => return Stop::Unsupported,
317
+ };
318
+ match result {
319
+ Ok(true) => {
320
+ self.done = true;
321
+ return Stop::End;
322
+ }
323
+ Ok(false) => {}
324
+ Err(Unsupported) => return Stop::Unsupported,
325
+ }
326
+ }
327
+ Stop::Full
328
+ }
329
+
330
+ fn drop_consumed(&mut self) {
331
+ let consumed = self.consumed;
332
+ if consumed == 0 {
333
+ return;
334
+ }
335
+ self.out.drain(..consumed);
336
+ for cell in &mut self.cells {
337
+ cell.start -= consumed;
338
+ cell.end -= consumed;
339
+ }
340
+ if let Some(text) = self.text.as_mut() {
341
+ *text -= consumed;
342
+ }
343
+ self.row_start = self.row_start.saturating_sub(consumed);
344
+ self.consumed = 0;
345
+ }
346
+
347
+ /// Handles an item; true at the end of the table. Ox hands over an empty text for an element without content
348
+ /// (`<a></a>`) and the white space of an element without children twice (`<a> </a>`); the second report changes
349
+ /// nothing (#text drops white space after a space), so only the first is made here.
350
+ fn handle(&mut self, item: Item, rows: &mut Vec<Row>) -> Result<bool> {
351
+ let just_started = std::mem::replace(&mut self.just_started, false);
352
+ match item {
353
+ Item::Text(start, end) => {
354
+ self.text(start, end)?;
355
+ Ok(false)
356
+ }
357
+ Item::Start(name_start, name_end, empty) => {
358
+ self.start_element(name_start, name_end)?;
359
+ if empty {
360
+ self.end_element(name_start, name_end, rows)
361
+ } else {
362
+ self.just_started = true;
363
+ Ok(false)
364
+ }
365
+ }
366
+ Item::End(name_start, name_end) => {
367
+ if just_started {
368
+ let at = self.lexer.pos;
369
+ self.text(at, at)?;
370
+ }
371
+ self.end_element(name_start, name_end, rows)
372
+ }
373
+ }
374
+ }
375
+
376
+ fn code(&self, start: usize, end: usize) -> Option<Code> {
377
+ let name = &self.lexer.buf[start..end];
378
+ self.elements
379
+ .iter()
380
+ .find(|(known, _)| known.as_slice() == name)
381
+ .map(|&(_, code)| code)
382
+ }
383
+
384
+ /// The format index of a style name: -1 for a style without a format.
385
+ fn style(&self, name: &[u8]) -> i64 {
386
+ self.formats.get(name).map_or(-1, |&index| index as i64)
387
+ }
388
+
389
+ fn start_element(&mut self, name_start: usize, name_end: usize) -> Result<()> {
390
+ if self.in_cell {
391
+ self.depth += 1;
392
+ self.element = None;
393
+ if self.paragraph {
394
+ self.start_in_paragraph(name_start, name_end);
395
+ } else if self.depth == 1 && self.code(name_start, name_end) == Some(Code::Paragraph) {
396
+ self.begin_paragraph()?;
397
+ }
398
+ return self.attributes();
399
+ }
400
+ let code = self.code(name_start, name_end);
401
+ self.element = code;
402
+ match code {
403
+ Some(Code::Row) => self.begin_row()?,
404
+ Some(Code::Cell | Code::Covered) => {
405
+ if !self.in_row {
406
+ return Err(Unsupported);
407
+ }
408
+ self.begin_cell(code == Some(Code::Covered));
409
+ }
410
+ Some(Code::Column) => {
411
+ self.column_repeated = 1;
412
+ self.column_style = None;
413
+ }
414
+ _ => {}
415
+ }
416
+ self.attributes()
417
+ }
418
+
419
+ fn end_element(
420
+ &mut self,
421
+ name_start: usize,
422
+ name_end: usize,
423
+ rows: &mut Vec<Row>,
424
+ ) -> Result<bool> {
425
+ if self.in_cell {
426
+ if self.depth == 0 {
427
+ self.finish_cell()?;
428
+ return Ok(false);
429
+ }
430
+ if self.paragraph {
431
+ self.end_in_paragraph()?;
432
+ }
433
+ self.element = None;
434
+ self.depth -= 1;
435
+ return Ok(false);
436
+ }
437
+ match self.code(name_start, name_end) {
438
+ Some(Code::Row) if self.in_row => self.finish_row(rows)?,
439
+ Some(Code::Column) => self.finish_column(),
440
+ Some(Code::Table) => return Ok(true),
441
+ _ => {}
442
+ }
443
+ Ok(false)
444
+ }
445
+
446
+ /// ContentParser#attr, for the attributes of the start tag just read.
447
+ fn attributes(&mut self) -> Result<()> {
448
+ self.lexer.check_unique()?;
449
+ let Some(element) = self.element else {
450
+ return Ok(());
451
+ };
452
+ for index in 0..self.lexer.attrs.len() {
453
+ let (name_start, name_end, value_start, value_end) = self.lexer.attrs[index];
454
+ let name = &self.lexer.buf[name_start..name_end];
455
+ let Some(&(_, attribute)) = self
456
+ .attributes
457
+ .iter()
458
+ .find(|(known, _)| known.as_slice() == name)
459
+ else {
460
+ continue;
461
+ };
462
+ let value = &self.lexer.buf[value_start..value_end];
463
+ match (element, attribute) {
464
+ (Code::Cell, Attribute::ValueType) => {
465
+ self.kind = match value {
466
+ b"float" | b"percentage" | b"currency" => ValueKind::Number,
467
+ b"date" => ValueKind::IsoDate,
468
+ b"time" => ValueKind::Duration,
469
+ b"boolean" => ValueKind::Boolean,
470
+ b"string" => ValueKind::String,
471
+ _ => ValueKind::Text,
472
+ }
473
+ }
474
+ (
475
+ Code::Cell,
476
+ Attribute::Value
477
+ | Attribute::DateValue
478
+ | Attribute::TimeValue
479
+ | Attribute::BooleanValue
480
+ | Attribute::StringValue,
481
+ ) => {
482
+ self.value.clear();
483
+ self.value.extend_from_slice(value);
484
+ self.value_code = Some(attribute);
485
+ }
486
+ (Code::Cell, Attribute::CalcValueType) => self.error = value == b"error",
487
+ (Code::Cell | Code::Covered, Attribute::ColumnsRepeated) => {
488
+ self.columns_repeated = repeat(value)?
489
+ }
490
+ (Code::Cell, Attribute::StyleName) => self.cell_style = Some(self.style(value)),
491
+ (Code::Row, Attribute::RowsRepeated) => self.rows_repeated = repeat(value)?,
492
+ (Code::Row, Attribute::DefaultStyle) => self.row_style = Some(self.style(value)),
493
+ (Code::Column, Attribute::ColumnsRepeated) => self.column_repeated = repeat(value)?,
494
+ (Code::Column, Attribute::DefaultStyle) => {
495
+ self.column_style = Some(self.style(value))
496
+ }
497
+ (Code::Spaces, Attribute::SpaceCount) => self.space_count = Some(value.to_vec()),
498
+ _ => {}
499
+ }
500
+ }
501
+ Ok(())
502
+ }
503
+
504
+ /// ContentParser#text: the text of a paragraph, its references decoded, collapsed as ODF collapses white space.
505
+ fn text(&mut self, start: usize, end: usize) -> Result<()> {
506
+ if !(self.capture && self.paragraph && self.ignored.is_none()) {
507
+ return Ok(());
508
+ }
509
+ let piece = &self.lexer.buf[start..end];
510
+ let decoded;
511
+ let value: &[u8] = if memchr(b'&', piece).is_some() {
512
+ decoded = decode(piece)?;
513
+ &decoded
514
+ } else {
515
+ piece
516
+ };
517
+ utf8(value)?;
518
+ let collapsible = value.iter().any(|&c| matches!(c, b'\t' | b'\n' | b'\r'))
519
+ || memchr::memmem::find(value, b" ").is_some();
520
+ if (self.ignore_space && value.first().copied().is_some_and(white)) || collapsible {
521
+ let mut collapsed = Vec::with_capacity(value.len());
522
+ for &c in value {
523
+ let c = if white(c) { b' ' } else { c };
524
+ if !(c == b' ' && collapsed.last() == Some(&b' ')) {
525
+ collapsed.push(c);
526
+ }
527
+ }
528
+ if self.ignore_space && collapsed.first() == Some(&b' ') {
529
+ collapsed.remove(0);
530
+ }
531
+ if collapsed.is_empty() {
532
+ return Ok(());
533
+ }
534
+ self.ignore_space = collapsed.last() == Some(&b' ');
535
+ return self.append(&collapsed);
536
+ }
537
+ self.ignore_space = value.last() == Some(&b' ');
538
+ // `value` may borrow the lexer's buffer: #append's fields are borrowed one by one.
539
+ append(&mut self.out, &mut self.text, self.max_cell_bytes, value)
540
+ }
541
+
542
+ fn start_in_paragraph(&mut self, name_start: usize, name_end: usize) {
543
+ if self.ignored.is_some() {
544
+ return;
545
+ }
546
+ match self.code(name_start, name_end) {
547
+ Some(code @ (Code::Spaces | Code::Tab | Code::LineBreak)) => {
548
+ self.element = Some(code);
549
+ self.space_count = None;
550
+ }
551
+ None if !self.text_element(name_start, name_end) => self.ignored = Some(self.depth),
552
+ _ => {}
553
+ }
554
+ }
555
+
556
+ fn end_in_paragraph(&mut self) -> Result<()> {
557
+ if let Some(depth) = self.ignored {
558
+ if depth == self.depth {
559
+ self.ignored = None;
560
+ }
561
+ } else if self.depth == 1 {
562
+ self.paragraph = false;
563
+ } else if self.capture {
564
+ self.control()?;
565
+ }
566
+ Ok(())
567
+ }
568
+
569
+ fn text_element(&self, start: usize, end: usize) -> bool {
570
+ self.text_qualifier.is_empty()
571
+ || self.lexer.buf[start..end].starts_with(&self.text_qualifier)
572
+ }
573
+
574
+ fn begin_paragraph(&mut self) -> Result<()> {
575
+ self.paragraph = true;
576
+ if self.paragraphs == 0 {
577
+ self.capture = self.wants_text();
578
+ }
579
+ self.paragraphs += 1;
580
+ if !self.capture {
581
+ return Ok(());
582
+ }
583
+ if self.paragraphs > 1 {
584
+ self.append(b"\n")?;
585
+ }
586
+ self.ignore_space = true;
587
+ Ok(())
588
+ }
589
+
590
+ fn wants_text(&self) -> bool {
591
+ !self.covered
592
+ && (self.error || self.value_code.is_none() || self.value_code != self.kind.value_of())
593
+ }
594
+
595
+ /// The end of a text:s, text:tab or text:line-break element of a paragraph.
596
+ fn control(&mut self) -> Result<()> {
597
+ match self.element {
598
+ Some(Code::Spaces) => {
599
+ let count = self.space_count.as_deref().and_then(count).unwrap_or(1);
600
+ self.spaces += count;
601
+ if self.spaces > MAX_SPACES {
602
+ return Err(Unsupported);
603
+ }
604
+ self.append(&vec![b' '; count as usize])?;
605
+ }
606
+ Some(Code::Tab) => self.append(b"\t")?,
607
+ Some(Code::LineBreak) => self.append(b"\n")?,
608
+ _ => return Ok(()),
609
+ }
610
+ self.ignore_space = false;
611
+ Ok(())
612
+ }
613
+
614
+ /// Joins a piece to the text, measured first: a text longer than max_cell_bytes is left to calcsheet.
615
+ fn append(&mut self, piece: &[u8]) -> Result<()> {
616
+ append(&mut self.out, &mut self.text, self.max_cell_bytes, piece)
617
+ }
618
+
619
+ // ---- columns, rows and cells ----
620
+
621
+ fn finish_column(&mut self) {
622
+ let first = self.columns;
623
+ self.columns = self.columns.saturating_add(self.column_repeated);
624
+ let Some(format) = self.column_style else {
625
+ return;
626
+ };
627
+ if format >= 0 && first < MAX_COLUMNS {
628
+ self.column_formats
629
+ .push((first, self.columns - 1, format as usize));
630
+ }
631
+ }
632
+
633
+ fn begin_row(&mut self) -> Result<()> {
634
+ if self.in_row {
635
+ return Err(Unsupported);
636
+ }
637
+ self.in_row = true;
638
+ self.row_number = self.next_row;
639
+ self.row_style = None;
640
+ self.rows_repeated = 1;
641
+ self.column = 0;
642
+ self.row_start = self.out.len();
643
+ Ok(())
644
+ }
645
+
646
+ fn finish_row(&mut self, rows: &mut Vec<Row>) -> Result<()> {
647
+ self.in_row = false;
648
+ let number = self.row_number;
649
+ let last = number
650
+ .checked_add(self.rows_repeated - 1)
651
+ .ok_or(Unsupported)?;
652
+ self.next_row = last.checked_add(1).ok_or(Unsupported)?;
653
+ if self.cells.is_empty() {
654
+ return Ok(());
655
+ }
656
+ // ContentParser refuses a non-blank row beyond the grid; the builder decides what is blank.
657
+ if last > MAX_ROWS && last >= self.start_row {
658
+ return Err(Unsupported);
659
+ }
660
+ let cells = std::mem::take(&mut self.cells);
661
+ rows.push(Row {
662
+ number,
663
+ last,
664
+ cells,
665
+ });
666
+ Ok(())
667
+ }
668
+
669
+ fn begin_cell(&mut self, covered: bool) {
670
+ self.in_cell = true;
671
+ self.covered = covered;
672
+ self.depth = 0;
673
+ self.columns_repeated = 1;
674
+ self.kind = ValueKind::Text;
675
+ self.value.clear();
676
+ self.value_code = None;
677
+ self.cell_style = None;
678
+ self.error = false;
679
+ self.text = None;
680
+ self.paragraphs = 0;
681
+ self.paragraph = false;
682
+ self.capture = false;
683
+ self.ignored = None;
684
+ self.spaces = 0;
685
+ }
686
+
687
+ fn finish_cell(&mut self) -> Result<()> {
688
+ self.in_cell = false;
689
+ let column = self.column;
690
+ let repeat = self.columns_repeated;
691
+ self.column = column.checked_add(repeat).ok_or(Unsupported)?;
692
+ let text_start = self.text.take();
693
+ let raw = if self.covered {
694
+ None
695
+ } else {
696
+ self.raw_value(text_start)?
697
+ };
698
+ let Some((kind, missing, start)) = raw else {
699
+ if let Some(start) = text_start {
700
+ self.out.truncate(start);
701
+ }
702
+ return Ok(());
703
+ };
704
+ if self.column > MAX_COLUMNS {
705
+ return Err(Unsupported);
706
+ }
707
+ let format = self.cell_style.or(self.row_style).unwrap_or(-2);
708
+ self.cells.push(Cell {
709
+ column,
710
+ repeat,
711
+ kind,
712
+ missing,
713
+ start,
714
+ end: self.out.len(),
715
+ format,
716
+ });
717
+ Ok(())
718
+ }
719
+
720
+ /// ContentParser#raw_value: the kind, the reason of a missing value and where the raw value starts in `out`;
721
+ /// `None` for an empty cell.
722
+ fn raw_value(&mut self, text_start: Option<usize>) -> Result<Option<(Kind, u8, usize)>> {
723
+ let text_end = self.out.len();
724
+ let start = text_start.unwrap_or(text_end);
725
+ if self.error {
726
+ utf8(&self.out[start..])?;
727
+ return Ok(Some((Kind::Error, 0, start)));
728
+ }
729
+ if self.value_code.is_some() && self.value_code == self.kind.value_of() {
730
+ // The value attribute replaces the text.
731
+ self.out.truncate(start);
732
+ if self.kind == ValueKind::String {
733
+ if self
734
+ .max_cell_bytes
735
+ .is_some_and(|max| self.value.len() > max)
736
+ {
737
+ return Err(Unsupported);
738
+ }
739
+ utf8(&self.value)?;
740
+ if self.value.is_empty() {
741
+ return Ok(None);
742
+ }
743
+ self.out.extend_from_slice(&self.value);
744
+ return Ok(Some((Kind::Text, 0, start)));
745
+ }
746
+ if self.value.len() > MAX_NUMBER_BYTES {
747
+ return Err(Unsupported);
748
+ }
749
+ utf8(&self.value)?;
750
+ self.out.extend_from_slice(&self.value);
751
+ let kind = match self.kind {
752
+ ValueKind::Number => Kind::Number,
753
+ ValueKind::IsoDate => Kind::IsoDate,
754
+ ValueKind::Duration => Kind::Duration,
755
+ _ => Kind::Boolean,
756
+ };
757
+ return Ok(Some((kind, 0, start)));
758
+ }
759
+ match self.kind {
760
+ ValueKind::Text | ValueKind::String => {
761
+ if text_start.is_none() || start == text_end {
762
+ return Ok(None);
763
+ }
764
+ utf8(&self.out[start..])?;
765
+ Ok(Some((Kind::Text, 0, start)))
766
+ }
767
+ kind => {
768
+ utf8(&self.out[start..])?;
769
+ let missing = match kind {
770
+ ValueKind::IsoDate => 1,
771
+ ValueKind::Boolean => 2,
772
+ _ => 0,
773
+ };
774
+ Ok(Some((Kind::Missing, missing, start)))
775
+ }
776
+ }
777
+ }
778
+ }
779
+
780
+ /// Joins a piece to the text that starts at `text` in `out` (or here, for the first piece), measured first.
781
+ fn append(
782
+ out: &mut Vec<u8>,
783
+ text: &mut Option<usize>,
784
+ max_cell_bytes: Option<usize>,
785
+ piece: &[u8],
786
+ ) -> Result<()> {
787
+ let start = *text.get_or_insert(out.len());
788
+ if max_cell_bytes.is_some_and(|max| out.len() - start + piece.len() > max) {
789
+ return Err(Unsupported);
790
+ }
791
+ out.extend_from_slice(piece);
792
+ Ok(())
793
+ }
794
+
795
+ /// White space of XML: space, tab, line feed and carriage return.
796
+ fn white(c: u8) -> bool {
797
+ matches!(c, b' ' | b'\t' | b'\n' | b'\r')
798
+ }
799
+
800
+ /// Count.parse: ASCII digits only, at most MAX_DIGITS of them.
801
+ fn count(value: &[u8]) -> Option<u64> {
802
+ if value.is_empty() || value.len() > MAX_DIGITS || !value.iter().all(u8::is_ascii_digit) {
803
+ return None;
804
+ }
805
+ Some(
806
+ value
807
+ .iter()
808
+ .fold(0u64, |count, &digit| count * 10 + u64::from(digit - b'0')),
809
+ )
810
+ }
811
+
812
+ /// ContentParser#repeat: a positive count, else calcsheet raises.
813
+ fn repeat(value: &[u8]) -> Result<u64> {
814
+ count(value).filter(|&count| count > 0).ok_or(Unsupported)
815
+ }