mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-24 10:46:50 -06:00
This commit is contained in:
+14
-5
@@ -87,8 +87,9 @@ pub fn sniff_delimiter(text: &str, quote: char) -> u8 {
|
||||
best.0
|
||||
}
|
||||
|
||||
/// Splits CSV text into records of fields.
|
||||
fn parse_records(text: &str, delim: char, quote: char, mut on_record: impl FnMut(Vec<String>) -> bool) {
|
||||
/// Splits CSV text into records of fields. A record keeps at most [`MAX_COLS`] fields; the rest are
|
||||
/// dropped as they are read, so a row of endless delimiters cannot allocate a string per field.
|
||||
pub(crate) fn parse_records(text: &str, delim: char, quote: char, mut on_record: impl FnMut(Vec<String>) -> bool) {
|
||||
let mut field = String::new();
|
||||
let mut record: Vec<String> = vec![];
|
||||
let mut in_q = false;
|
||||
@@ -112,13 +113,19 @@ fn parse_records(text: &str, delim: char, quote: char, mut on_record: impl FnMut
|
||||
in_q = true;
|
||||
field_started_quoted = true;
|
||||
} else if c == delim {
|
||||
record.push(std::mem::take(&mut field));
|
||||
let f = std::mem::take(&mut field);
|
||||
if record.len() < MAX_COLS as usize {
|
||||
record.push(f);
|
||||
}
|
||||
field_started_quoted = false;
|
||||
} else if c == '\r' || c == '\n' {
|
||||
if c == '\r' && chars.peek() == Some(&'\n') {
|
||||
chars.next();
|
||||
}
|
||||
record.push(std::mem::take(&mut field));
|
||||
let f = std::mem::take(&mut field);
|
||||
if record.len() < MAX_COLS as usize {
|
||||
record.push(f);
|
||||
}
|
||||
field_started_quoted = false;
|
||||
if !on_record(std::mem::take(&mut record)) {
|
||||
return;
|
||||
@@ -128,7 +135,9 @@ fn parse_records(text: &str, delim: char, quote: char, mut on_record: impl FnMut
|
||||
}
|
||||
}
|
||||
if !field.is_empty() || !record.is_empty() || field_started_quoted {
|
||||
record.push(field);
|
||||
if record.len() < MAX_COLS as usize {
|
||||
record.push(field);
|
||||
}
|
||||
on_record(record);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use gridcraft_core::{CellRef, DateSystem, Value};
|
||||
use gridcraft_core::{CellRef, DateSystem, MAX_COLS, Value};
|
||||
use gridcraft_model::{Sheet, Workbook};
|
||||
|
||||
use crate::csv::{decode_text, sniff_delimiter};
|
||||
use crate::csv::{decode_text, parse_records, sniff_delimiter};
|
||||
use crate::{CsvOptions, Format, IoError, read_csv, sniff, write_csv, write_csv_limited};
|
||||
|
||||
fn v(wb: &Workbook, r: u32, c: u32) -> Value {
|
||||
@@ -209,3 +209,24 @@ fn the_cap_counts_the_bytes_the_cells_hold_and_accepted_exports_keep_every_posit
|
||||
let tabs = write_csv_limited(&s, b'\t', 14, text_of(&s)).unwrap();
|
||||
assert_eq!(tabs.len(), 14);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parser_stops_collecting_fields_at_max_cols() {
|
||||
let extra = 50;
|
||||
// MAX_COLS + extra fields on the first row (the last one filled), then a short second row.
|
||||
let mut text = ",".repeat((MAX_COLS + extra) as usize - 1);
|
||||
text.push_str("end\nx,y\n");
|
||||
let mut lens = vec![];
|
||||
parse_records(&text, ',', '"', |rec| {
|
||||
lens.push(rec.len());
|
||||
true
|
||||
});
|
||||
assert_eq!(lens, vec![MAX_COLS as usize, 2]);
|
||||
// An unterminated last row is capped the same way.
|
||||
let mut lens = vec![];
|
||||
parse_records(&",".repeat(MAX_COLS as usize + extra as usize), ',', '"', |rec| {
|
||||
lens.push(rec.len());
|
||||
true
|
||||
});
|
||||
assert_eq!(lens, vec![MAX_COLS as usize]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user