1use std::io::{self, BufRead};
70use std::sync::{Arc, Mutex};
71
72use tabnas::Tabnas;
73use tabnas_csv::CsvOptions;
74
75use crate::error::{Code, Fail};
76use crate::event::JsonEvent;
77use crate::limits::{AbortFlag, Limits, Metrics};
78use crate::sink::{Flow, Sink};
79use crate::source::guard::Guarded;
80use crate::source::rule_events::{self, Adapter, Status, GUARD};
81use crate::source::{engine_failure, walk_value, Prune, Source};
82
83pub const DEFAULT_CHUNK_BYTES: usize = 256 * 1024;
85
86#[derive(Clone, Debug)]
88pub enum LineFormat {
89 Jsonl,
91 Csv {
96 header: bool,
97 options: Box<CsvOptions>,
98 },
99}
100
101impl LineFormat {
102 pub fn csv() -> LineFormat {
104 LineFormat::Csv {
105 header: true,
106 options: Box::new(CsvOptions::default()),
107 }
108 }
109
110 pub fn csv_with(header: bool, options: CsvOptions) -> LineFormat {
113 LineFormat::Csv {
114 header,
115 options: Box::new(options),
116 }
117 }
118}
119
120pub struct LinesSource<R: BufRead> {
122 reader: R,
123 format: LineFormat,
124 limits: Limits,
125 abort: AbortFlag,
126 metrics: Arc<Metrics>,
127 chunk_bytes: usize,
128}
129
130impl<R: BufRead> LinesSource<R> {
131 pub fn new(reader: R, format: LineFormat) -> LinesSource<R> {
132 LinesSource {
133 reader,
134 format,
135 limits: Limits::default(),
136 abort: AbortFlag::new(),
137 metrics: Metrics::new(),
138 chunk_bytes: DEFAULT_CHUNK_BYTES,
139 }
140 }
141
142 pub fn limits(mut self, limits: Limits) -> Self {
143 self.limits = limits;
144 self
145 }
146
147 pub fn abort(mut self, abort: AbortFlag) -> Self {
148 self.abort = abort;
149 self
150 }
151
152 pub fn metrics(mut self, metrics: Arc<Metrics>) -> Self {
153 self.metrics = metrics;
154 self
155 }
156
157 pub fn chunk_bytes(mut self, bytes: usize) -> Self {
160 self.chunk_bytes = bytes;
161 self
162 }
163
164 pub fn run_owned<S: Sink + Send + 'static>(self, sink: S) -> (Result<Flow, Fail>, S) {
167 match &self.format {
168 LineFormat::Jsonl => self.jsonl_incremental(sink),
169 LineFormat::Csv { .. } => {
170 let mut guarded =
171 Guarded::new(sink, &self.limits, self.abort.clone(), self.metrics.clone());
172 let outcome = self.drive(&mut guarded);
173 (outcome, guarded.into_inner())
174 }
175 }
176 }
177
178 pub fn run_boxed(
180 self,
181 sink: Box<dyn Sink + Send>,
182 ) -> (Result<Flow, Fail>, Box<dyn Sink + Send>) {
183 self.run_owned(sink)
184 }
185
186 fn drive<S: Sink>(self, guarded: &mut Guarded<S>) -> Result<Flow, Fail> {
188 let LinesSource {
189 reader,
190 format,
191 limits,
192 abort,
193 chunk_bytes,
194 ..
195 } = self;
196 match format {
197 LineFormat::Jsonl => {
198 let mut parser = tabnas_json::make();
199 install_guard(&mut parser, &abort);
200 let mut records = JsonRecords::new(reader, &parser, limits.max_record_bytes);
201 if guarded.event(JsonEvent::ArrayStart)? == Flow::Stop {
202 return Ok(Flow::Stop);
203 }
204 while let Some((number, record)) = records.next_record()? {
205 let value = parser
206 .parse(record)
207 .map_err(|e| line_failure(&e, number, number, &abort))?;
208 if walk_value(&value, guarded).map_err(|fail| at_record(fail, number))?
209 == Flow::Stop
210 {
211 return Ok(Flow::Stop);
212 }
213 }
214 if guarded.event(JsonEvent::ArrayEnd)? == Flow::Stop {
215 return Ok(Flow::Stop);
216 }
217 guarded.event(JsonEvent::End)
218 }
219 LineFormat::Csv { header, options } => {
220 let mut options = *options;
221 options.header = header;
222 let mut parser = tabnas_csv::make_with(options.clone());
223 install_guard(&mut parser, &abort);
224 let mut chunks = Chunks::new(
225 reader,
226 &parser,
227 &options,
228 chunk_bytes,
229 limits.max_record_bytes,
230 );
231 if guarded.event(JsonEvent::ArrayStart)? == Flow::Stop {
232 return Ok(Flow::Stop);
233 }
234 while let Some(chunk) = chunks.next_chunk()? {
235 let value = parser
236 .parse(&chunk.text)
237 .map_err(|e| chunk.failure(&e, &abort))?;
238 for record in records_of(&value) {
239 if walk_value(record, guarded)
240 .map_err(|fail| at_record(fail, chunk.first_line))?
241 == Flow::Stop
242 {
243 return Ok(Flow::Stop);
244 }
245 }
246 }
247 if guarded.event(JsonEvent::ArrayEnd)? == Flow::Stop {
248 return Ok(Flow::Stop);
249 }
250 guarded.event(JsonEvent::End)
251 }
252 }
253 }
254
255 fn jsonl_incremental<S: Sink + Send + 'static>(self, sink: S) -> (Result<Flow, Fail>, S) {
258 let LinesSource {
259 reader,
260 limits,
261 abort,
262 metrics,
263 ..
264 } = self;
265 let stop = AbortFlag::new();
266 let adapter = Adapter::new(
267 sink,
268 &limits,
269 abort.clone(),
270 metrics,
271 &Prune::Never,
272 stop.clone(),
273 );
274 let shared = Arc::new(Mutex::new(adapter));
275 let mut parser = tabnas_json::make();
276 Adapter::install(
277 &mut parser,
278 Arc::downgrade(&shared),
279 abort.clone(),
280 stop.clone(),
281 );
282 let mut records = JsonRecords::new(reader, &parser, limits.max_record_bytes);
283 let mut reading = None;
285 let mut outcome = rule_events::lock(&shared).send(JsonEvent::ArrayStart);
286 if outcome.as_ref().is_ok_and(|flow| *flow == Flow::Continue) {
287 outcome = loop {
288 let (number, record) = match records.next_record() {
289 Ok(Some(next)) => next,
290 Ok(None) => break Ok(Flow::Continue),
291 Err(fail) => break Err(fail),
292 };
293 reading = Some(number);
294 let parsed = parser.parse(record);
295 let mut adapter = rule_events::lock(&shared);
296 match adapter.status() {
297 Status::Running => {}
298 Status::Stopped => break Ok(Flow::Stop),
299 Status::Failed(_) => break Ok(Flow::Continue),
301 }
302 match parsed {
303 Ok(_) if adapter.complete() => adapter.reset(),
304 Ok(value) if adapter.idle() => match adapter.walk_whole(&value) {
305 Ok(Flow::Continue) => adapter.reset(),
306 Ok(Flow::Stop) => break Ok(Flow::Stop),
307 Err(fail) => break Err(at_record(fail, number)),
308 },
309 Ok(_) => break Err(rule_events::not_streamable()),
310 Err(e) => break Err(line_failure(&e, number, number, &abort)),
311 }
312 };
313 }
314 drop(parser);
315 let mut adapter = rule_events::take(shared);
316 if let (Ok(Flow::Continue), Status::Running) = (&outcome, adapter.status()) {
317 outcome = adapter
318 .send(JsonEvent::ArrayEnd)
319 .and_then(|flow| match flow {
320 Flow::Continue => adapter.send(JsonEvent::End),
321 Flow::Stop => Ok(Flow::Stop),
322 });
323 }
324 let (status, sink) = adapter.finish();
325 let outcome = match status {
326 Status::Failed(fail) => Err(match reading {
327 Some(number) => at_record(fail, number),
328 None => fail,
329 }),
330 Status::Stopped => Ok(Flow::Stop),
331 Status::Running => outcome,
332 };
333 (outcome, sink)
334 }
335}
336
337impl<R: BufRead> Source for LinesSource<R> {
338 fn run(self, sink: &mut dyn Sink) -> Result<Flow, Fail> {
341 let mut guarded =
342 Guarded::new(sink, &self.limits, self.abort.clone(), self.metrics.clone());
343 let outcome = self.drive(&mut guarded);
344 guarded.flush();
345 outcome
346 }
347}
348
349fn install_guard(parser: &mut Tabnas, abort: &AbortFlag) {
350 let flag = abort.clone();
351 parser.parse_guard(GUARD, move |_ctx| !flag.is_aborted());
352}
353
354fn line_failure(error: &tabnas::TabnasError, line: u64, start: u64, abort: &AbortFlag) -> Fail {
360 let mut fail = engine_failure(error, abort);
361 if fail.code == Code::Aborted {
362 fail.row = Some(start);
363 } else {
364 fail.row = Some(line);
365 fail.column = Some(error.col as u64);
366 }
367 fail
368}
369
370fn at_record(mut fail: Fail, start: u64) -> Fail {
373 if fail.code == Code::Aborted && fail.row.is_none() {
374 fail.row = Some(start);
375 }
376 fail
377}
378
379fn records_of(value: &tabnas::Value) -> &[tabnas::Value] {
381 match value {
382 tabnas::Value::Array(items) => items,
383 tabnas::Value::ListRef(list) => &list.value,
384 _ => &[],
386 }
387}
388
389struct JsonLexis {
395 line: Vec<char>,
396 space: Vec<char>,
397 quotes: Vec<char>,
398 escape: char,
399}
400
401impl JsonLexis {
402 fn of(parser: &Tabnas) -> JsonLexis {
403 let config = parser.config();
404 let mut line = lexed(config.line.lex, &config.line.chars);
405 if config.line.lex {
406 line.extend(&config.line.fixed);
407 }
408 JsonLexis {
409 line,
410 space: lexed(config.space.lex, &config.space.chars),
411 quotes: lexed(config.string.lex, &config.string.chars),
412 escape: config.string.escape_char,
413 }
414 }
415}
416
417struct JsonRecords<R: BufRead> {
424 pieces: Pieces<R>,
425 lexis: JsonLexis,
426 record: String,
427 max_bytes: usize,
428}
429
430impl<R: BufRead> JsonRecords<R> {
431 fn new(reader: R, parser: &Tabnas, max_bytes: usize) -> JsonRecords<R> {
432 let lexis = JsonLexis::of(parser);
433 let config = parser.config();
434 let fixed: Vec<String> = config
435 .fixed
436 .tokens
437 .values()
438 .filter(|token| config.fixed.lex && !token.source.is_empty())
439 .map(|token| token.source.clone())
440 .collect();
441 let pieces = Pieces::new(
442 reader,
443 lexis.line.clone(),
444 config.line.row_chars.chars().collect(),
445 config.line.single,
446 &fixed,
447 );
448 JsonRecords {
449 pieces,
450 lexis,
451 record: String::new(),
452 max_bytes,
453 }
454 }
455
456 fn next_record(&mut self) -> Result<Option<(u64, &str)>, Fail> {
460 let (start, end) = loop {
461 self.record.clear();
462 let mut start = None;
463 let mut quote = None;
464 let mut escaped = false;
465 let mut end = None;
466 while end.is_none() {
467 let first = start.unwrap_or(self.pieces.row);
468 let max = self.max_bytes;
469 let over = || {
470 Fail::limit(
471 "max_record_bytes",
472 max as u64,
473 format!("line {first} is longer than {max} bytes"),
474 )
475 .at(first, 1)
476 };
477 let budget = max.saturating_sub(self.record.len());
478 let Some((row, piece)) = self.pieces.next_piece(budget, over)? else {
479 break;
480 };
481 start.get_or_insert(row);
482 let held = self.record.len();
483 for (at, c) in piece.char_indices() {
484 if let Some(open) = quote {
485 if escaped {
486 escaped = false;
487 } else if c == self.lexis.escape {
488 escaped = true;
489 } else if c == open {
490 quote = None;
491 }
492 } else if self.lexis.quotes.contains(&c) {
493 quote = Some(c);
494 } else if self.lexis.line.contains(&c) {
495 end = Some(held + at);
496 break;
497 }
498 }
499 self.record.push_str(piece);
500 }
501 let Some(start) = start else {
502 return Ok(None);
503 };
504 let end = end.unwrap_or(self.record.len());
505 if !self.record[..end]
506 .chars()
507 .all(|c| self.lexis.space.contains(&c))
508 {
509 break (start, end);
510 }
511 };
512 Ok(Some((start, &self.record[..end])))
513 }
514}
515
516fn lexed(on: bool, chars: &str) -> Vec<char> {
518 if on {
519 chars.chars().collect()
520 } else {
521 Vec::new()
522 }
523}
524
525struct Pieces<R: BufRead> {
537 reader: R,
538 pending: Vec<u8>,
541 start: usize,
542 scanned: usize,
543 eof: bool,
544 line: Vec<char>,
547 forms: Vec<Vec<u8>>,
548 ends: [bool; 256],
549 single: bool,
550 spanning: Vec<Vec<u8>>,
552 following: Option<(usize, Vec<char>)>,
555 rows: Vec<char>,
556 row: u64,
558 offset: u64,
559}
560
561impl<R: BufRead> Pieces<R> {
562 fn new(
563 reader: R,
564 line: Vec<char>,
565 rows: Vec<char>,
566 single: bool,
567 fixed: &[String],
568 ) -> Pieces<R> {
569 let forms: Vec<Vec<u8>> = line.iter().map(|c| c.to_string().into_bytes()).collect();
570 let mut ends = [false; 256];
571 for form in &forms {
572 ends[usize::from(form[form.len() - 1])] = true;
573 }
574 let spanning = fixed
575 .iter()
576 .filter(|token| token.chars().any(|c| line.contains(&c)))
577 .map(|token| token.as_bytes().to_vec())
578 .collect();
579 Pieces {
580 reader,
581 pending: Vec::new(),
582 start: 0,
583 scanned: 0,
584 eof: false,
585 line,
586 forms,
587 ends,
588 single,
589 spanning,
590 following: None,
591 rows,
592 row: 1,
593 offset: 0,
594 }
595 }
596
597 fn next_piece(
603 &mut self,
604 budget: usize,
605 over: impl Fn() -> Fail,
606 ) -> Result<Option<(u64, &str)>, Fail> {
607 let end = loop {
608 if let Some(end) = self.ending() {
609 break end;
610 }
611 if self.pending.len() - self.start > budget {
612 return Err(over());
613 }
614 if !self.more()? {
615 if let Some(end) = self.ending() {
617 break end;
618 }
619 if self.pending.len() == self.start {
620 return Ok(None);
621 }
622 break self.pending.len();
623 }
624 };
625 if end - self.start > budget {
626 return Err(over());
627 }
628 let (from, row) = (self.start, self.row);
629 self.start = end;
630 self.scanned = end;
631 let piece = match std::str::from_utf8(&self.pending[from..end]) {
632 Ok(piece) => piece,
633 Err(e) => {
634 let valid = std::str::from_utf8(&self.pending[from..from + e.valid_up_to()])
635 .unwrap_or_default();
636 let (row, offset) = advance(&self.rows, self.row, self.offset, valid);
637 return Err(Fail::input(format!(
638 "line {row} is not UTF-8 from its byte {}",
639 offset + 1
640 ))
641 .at(row, offset + 1));
642 }
643 };
644 (self.row, self.offset) = advance(&self.rows, self.row, self.offset, piece);
645 Ok(Some((row, piece)))
646 }
647
648 fn more(&mut self) -> Result<bool, Fail> {
651 if self.eof {
652 return Ok(false);
653 }
654 if self.start > 0 {
655 self.pending.drain(..self.start);
656 self.scanned -= self.start;
657 if let Some((end, _)) = &mut self.following {
658 *end -= self.start;
659 }
660 self.start = 0;
661 }
662 loop {
663 match self.reader.fill_buf() {
664 Ok([]) => {
665 self.eof = true;
666 return Ok(false);
667 }
668 Ok(available) => {
669 let n = available.len();
670 self.pending.extend_from_slice(available);
671 self.reader.consume(n);
672 return Ok(true);
673 }
674 Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
675 Err(e) => return Err(Fail::input(format!("reading line {}: {e}", self.row))),
676 }
677 }
678 }
679
680 fn ending(&mut self) -> Option<usize> {
684 if let Some((end, token)) = self.following.take() {
685 return self.follow(end, token);
686 }
687 let mut from = self.scanned.max(self.start);
688 while let Some(at) = self.pending[from..]
689 .iter()
690 .position(|&b| self.ends[usize::from(b)])
691 {
692 let end = from + at + 1;
693 let Some(k) = self
694 .forms
695 .iter()
696 .position(|form| self.pending[self.start..end].ends_with(form))
697 else {
698 from = end;
699 continue;
700 };
701 let at = end - self.forms[k].len();
702 match self.in_fixed(at, k) {
703 Some(true) => {
704 from = end;
705 continue;
706 }
707 Some(false) => {}
708 None => {
709 self.scanned = at;
710 return None;
711 }
712 }
713 return self.follow(end, vec![self.line[k]]);
714 }
715 self.scanned = self.pending.len();
716 None
717 }
718
719 fn in_fixed(&self, at: usize, k: usize) -> Option<bool> {
723 let form = &self.forms[k];
724 let mut unsure = false;
725 for token in &self.spanning {
726 for j in 0..token.len() {
727 if !token[j..].starts_with(form) {
728 continue;
729 }
730 let Some(token_start) = at.checked_sub(j).filter(|&s| s >= self.start) else {
731 continue;
732 };
733 if self.pending[token_start..at] != token[..j] {
734 continue;
735 }
736 let rest = &token[j + form.len()..];
737 let after = at + form.len();
738 let have = &self.pending[after..(after + rest.len()).min(self.pending.len())];
739 if !rest.starts_with(have) {
740 continue;
741 }
742 if have.len() == rest.len() {
743 return Some(true);
744 }
745 unsure |= !self.eof;
746 }
747 }
748 if unsure {
749 None
750 } else {
751 Some(false)
752 }
753 }
754
755 fn follow(&mut self, mut end: usize, mut token: Vec<char>) -> Option<usize> {
764 'token: loop {
765 let mut unsure = false;
766 for (i, form) in self.forms.iter().enumerate() {
767 let c = self.line[i];
768 let goes_on = if self.single {
769 !token.contains(&c)
770 } else {
771 !self.spanning.is_empty() || (token == ['\r'] && c == '\n')
772 };
773 if !goes_on {
774 continue;
775 }
776 let have = &self.pending[end..(end + form.len()).min(self.pending.len())];
777 if !form.starts_with(have) {
778 continue;
779 }
780 if have.len() == form.len() {
781 end += form.len();
782 token.push(c);
783 continue 'token;
784 }
785 unsure |= !self.eof;
786 }
787 if unsure {
788 self.following = Some((end, token));
789 return None;
790 }
791 return Some(end);
792 }
793 }
794}
795
796fn advance(rows: &[char], row: u64, offset: u64, text: &str) -> (u64, u64) {
799 let mut count = 0;
800 let mut last = None;
801 for (at, c) in text.char_indices() {
802 if rows.contains(&c) {
803 count += 1;
804 last = Some(at + c.len_utf8());
805 }
806 }
807 match last {
808 Some(end) => (row + count, (text.len() - end) as u64),
809 None => (row, offset + text.len() as u64),
810 }
811}
812
813struct Chunk {
816 text: String,
817 first_line: u64,
819 prefix_lines: u64,
823}
824
825impl Chunk {
826 fn failure(&self, error: &tabnas::TabnasError, abort: &AbortFlag) -> Fail {
828 let row_in_chunk = (error.row as u64).max(1);
829 let line = if row_in_chunk > self.prefix_lines {
830 self.first_line + row_in_chunk - self.prefix_lines - 1
831 } else {
832 1
834 };
835 line_failure(error, line, self.first_line, abort)
836 }
837}
838
839struct Chunks<R: BufRead> {
842 pieces: Pieces<R>,
843 scanner: Scanner,
844 want_header: bool,
847 record_empty: bool,
848 header: Option<String>,
851 chunk_bytes: usize,
852 max_record_bytes: usize,
853 started: bool,
854 done: bool,
855}
856
857impl<R: BufRead> Chunks<R> {
858 fn new(
859 reader: R,
860 parser: &Tabnas,
861 options: &CsvOptions,
862 chunk_bytes: usize,
863 max_record_bytes: usize,
864 ) -> Chunks<R> {
865 let lexis = Lexis::csv(parser, options);
866 let config = parser.config();
867 let pieces = Pieces::new(
868 reader,
869 lexis.line.clone(),
870 config.line.row_chars.chars().collect(),
871 config.line.single,
872 &lexis.fixed,
873 );
874 Chunks {
875 pieces,
876 scanner: Scanner::new(lexis),
877 want_header: options.header,
878 record_empty: options.record.empty,
879 header: None,
880 chunk_bytes,
881 max_record_bytes,
882 started: false,
883 done: false,
884 }
885 }
886
887 fn next_chunk(&mut self) -> Result<Option<Chunk>, Fail> {
891 if self.done {
892 return Ok(None);
893 }
894 let mut text = String::new();
895 if self.started {
896 text.push_str(self.header.as_deref().unwrap_or_default());
897 }
898 self.started = true;
899 let prefix_lines = advance(&self.pieces.rows, 0, 0, &text).0;
900 let mut body = text.len();
903 let mut first_line = None;
904 let mut open = (text.len(), None);
907 let mut last_end = 0;
909 loop {
910 let first = open.1.unwrap_or(self.pieces.row);
911 let max = self.max_record_bytes;
912 let over = || {
913 Fail::limit(
914 "max_record_bytes",
915 max as u64,
916 format!("the record starting at line {first} is longer than {max} bytes"),
917 )
918 .at(first, 1)
919 };
920 let budget = max.saturating_sub(text.len() - open.0);
921 let Some((number, piece)) = self.pieces.next_piece(budget, over)? else {
922 self.done = true;
923 break;
924 };
925 first_line.get_or_insert(number);
926 open.1.get_or_insert(number);
927 let at = text.len();
928 text.push_str(piece);
929 let seeking = self.want_header && self.header.is_none();
930 let record_empty = self.record_empty;
931 let mut found = None;
932 let ended = self.scanner.piece(piece, |end, content| {
933 if seeking && found.is_none() && (content || record_empty) {
934 found = Some((last_end, at + end));
935 }
936 last_end = at + end;
937 });
938 if let Some((from, to)) = found {
939 self.header = Some(text[from..to].to_string());
940 body = to;
941 }
942 if ended && !(self.want_header && self.header.is_none()) {
945 open = (text.len(), None);
946 if text.len() > body && text.len() >= self.chunk_bytes {
947 break;
948 }
949 }
950 }
951 let Some(first_line) = first_line else {
952 return Ok(None);
954 };
955 Ok(Some(Chunk {
956 text,
957 first_line,
958 prefix_lines,
959 }))
960 }
961}
962
963struct Lexis {
969 line: Vec<char>,
971 single: bool,
974 space: Vec<char>,
975 fixed: Vec<String>,
978 quote: Option<char>,
981 strings: Vec<char>,
985 multi: Vec<char>,
986 escape: char,
987 comments: Vec<(String, Option<String>)>,
990 stops: Vec<char>,
992 stop_prefixes: Vec<String>,
993 space_ignored: bool,
996 comment_ignored: bool,
997}
998
999impl Lexis {
1000 fn csv(parser: &Tabnas, options: &CsvOptions) -> Lexis {
1001 let config = parser.config();
1002 let mut line = lexed(config.line.lex, &config.line.chars);
1003 if config.line.lex {
1004 line.extend(&config.line.fixed);
1005 }
1006 let space = lexed(config.space.lex, &config.space.chars);
1007 let fixed: Vec<String> = config
1008 .fixed
1009 .tokens
1010 .values()
1011 .filter(|token| config.fixed.lex && !token.source.is_empty())
1012 .map(|token| token.source.clone())
1013 .collect();
1014 let mut comments: Vec<_> = config
1016 .comment
1017 .definitions
1018 .iter()
1019 .filter(|(_, comment)| config.comment.lex && comment.lex && !comment.start.is_empty())
1020 .collect();
1021 comments.sort_by(|(name, comment), (other_name, other)| {
1022 other
1023 .start
1024 .len()
1025 .cmp(&comment.start.len())
1026 .then_with(|| name.cmp(other_name))
1027 });
1028 let comments: Vec<(String, Option<String>)> = comments
1029 .into_iter()
1030 .map(|(_, comment)| {
1031 (
1032 comment.start.clone(),
1033 (!comment.line).then(|| comment.end.clone()),
1034 )
1035 })
1036 .collect();
1037 let matcher = if options.strict {
1041 options.string.csv != Some(false)
1042 } else {
1043 options.string.csv == Some(true)
1044 };
1045 let mut quote = options.string.quote.chars();
1046 let quote = match (quote.next(), quote.next()) {
1047 (Some(quote), None) if matcher && quote.len_utf16() == 1 => Some(quote),
1048 _ => None,
1049 };
1050 let ignored = parser.token_set("IGNORE").unwrap_or_default();
1051 let mut stops = space.clone();
1054 stops.extend(&line);
1055 if config.line.lex {
1056 stops.extend(['\u{2028}', '\u{2029}']);
1057 }
1058 let mut stop_prefixes = fixed.clone();
1059 stop_prefixes.extend(comments.iter().map(|(start, _)| start.clone()));
1060 stop_prefixes.extend(config.ender.iter().filter(|e| !e.is_empty()).cloned());
1061 Lexis {
1062 line,
1063 single: config.line.single,
1064 space,
1065 fixed,
1066 quote,
1067 strings: lexed(config.string.lex, &config.string.chars),
1068 multi: config.string.multi_chars.chars().collect(),
1069 escape: config.string.escape_char,
1070 comments,
1071 stops,
1072 stop_prefixes,
1073 space_ignored: ignored.contains(&tabnas::TIN_SP),
1074 comment_ignored: ignored.contains(&tabnas::TIN_CM),
1075 }
1076 }
1077
1078 fn fixed_at(&self, rest: &str) -> Option<usize> {
1080 self.fixed
1081 .iter()
1082 .filter(|source| rest.starts_with(source.as_str()))
1083 .map(String::len)
1084 .max()
1085 }
1086
1087 fn comment_at(&self, rest: &str) -> Option<usize> {
1089 self.comments
1090 .iter()
1091 .position(|(start, _)| rest.starts_with(start.as_str()))
1092 }
1093
1094 fn line_run(&self, rest: &str) -> usize {
1096 let mut len = 0;
1097 for (at, c) in rest.char_indices() {
1098 if !self.line.contains(&c) || (self.single && rest[..at].contains(c)) {
1099 break;
1100 }
1101 len = at + c.len_utf8();
1102 }
1103 len
1104 }
1105
1106 fn ends_text(&self, rest: &str, c: char) -> bool {
1109 self.stops.contains(&c)
1110 || self
1111 .stop_prefixes
1112 .iter()
1113 .any(|prefix| rest.starts_with(prefix.as_str()))
1114 }
1115}
1116
1117#[derive(Clone, Copy)]
1119enum At {
1120 Start,
1122 Text,
1125 Quoted,
1127 Str { quote: char, multi: bool },
1129 Comment(usize),
1131}
1132
1133struct Scanner {
1136 lexis: Lexis,
1137 at: At,
1138 content: bool,
1141}
1142
1143impl Scanner {
1144 fn new(lexis: Lexis) -> Scanner {
1145 Scanner {
1146 lexis,
1147 at: At::Start,
1148 content: false,
1149 }
1150 }
1151
1152 fn piece(&mut self, line: &str, mut end: impl FnMut(usize, bool)) -> bool {
1159 let lexis = &self.lexis;
1160 let mut ended = false;
1161 let mut i = 0;
1162 while let Some(c) = line[i..].chars().next() {
1163 let rest = &line[i..];
1164 ended = false;
1165 match self.at {
1166 At::Start => {
1169 if lexis.quote == Some(c) {
1170 self.at = At::Quoted;
1171 self.content = true;
1172 i += c.len_utf8();
1173 } else if let Some(len) = lexis.fixed_at(rest) {
1174 self.content = true;
1175 i += len;
1176 } else if lexis.space.contains(&c) {
1177 self.content |= !lexis.space_ignored;
1178 i += c.len_utf8();
1179 } else if lexis.line.contains(&c) {
1180 i += lexis.line_run(rest);
1181 end(i, self.content);
1182 self.content = false;
1183 ended = true;
1184 } else if lexis.strings.contains(&c) {
1185 self.at = At::Str {
1186 quote: c,
1187 multi: lexis.multi.contains(&c),
1188 };
1189 self.content = true;
1190 i += c.len_utf8();
1191 } else if let Some(comment) = lexis.comment_at(rest) {
1192 self.at = At::Comment(comment);
1193 self.content |= !lexis.comment_ignored;
1194 i += lexis.comments[comment].0.len();
1195 } else {
1196 if !lexis.ends_text(rest, c) {
1199 self.at = At::Text;
1200 }
1201 self.content = true;
1202 i += c.len_utf8();
1203 }
1204 }
1205 At::Text => {
1206 if lexis.ends_text(rest, c) {
1207 self.at = At::Start;
1208 } else {
1209 i += c.len_utf8();
1210 }
1211 }
1212 At::Quoted => {
1213 i += c.len_utf8();
1214 if lexis.quote == Some(c) {
1215 if line[i..].starts_with(c) {
1216 i += c.len_utf8();
1217 } else {
1218 self.at = At::Start;
1219 }
1220 }
1221 }
1222 At::Str { quote, multi } => {
1223 if c == quote {
1224 self.at = At::Start;
1225 i += c.len_utf8();
1226 } else if c == lexis.escape {
1227 i += c.len_utf8();
1228 i += line[i..].chars().next().map_or(0, char::len_utf8);
1229 } else if !multi && lexis.line.contains(&c) {
1230 self.at = At::Start;
1233 } else {
1234 i += c.len_utf8();
1235 }
1236 }
1237 At::Comment(comment) => match &lexis.comments[comment].1 {
1238 Some(close) if !close.is_empty() && rest.starts_with(close.as_str()) => {
1239 self.at = At::Start;
1240 i += close.len();
1241 }
1242 None if lexis.line.contains(&c) => self.at = At::Start,
1243 _ => i += c.len_utf8(),
1244 },
1245 }
1246 }
1247 ended
1248 }
1249}
1250
1251#[cfg(test)]
1252mod tests {
1253 use super::*;
1254 use crate::event::OwnedJsonEvent;
1255 use crate::sink::FnSink;
1256 use crate::source::ValueSource;
1257 use std::io::{self, Cursor, Read};
1258
1259 struct Trickle {
1262 data: Vec<u8>,
1263 pos: usize,
1264 step: usize,
1265 }
1266
1267 impl Read for Trickle {
1268 fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
1269 let n = self.step.min(out.len()).min(self.data.len() - self.pos);
1270 out[..n].copy_from_slice(&self.data[self.pos..self.pos + n]);
1271 self.pos += n;
1272 Ok(n)
1273 }
1274 }
1275
1276 fn trickle(data: &str, step: usize) -> io::BufReader<Trickle> {
1277 io::BufReader::with_capacity(
1278 step.max(1),
1279 Trickle {
1280 data: data.as_bytes().to_vec(),
1281 pos: 0,
1282 step: step.max(1),
1283 },
1284 )
1285 }
1286
1287 fn without_lexemes(events: &[OwnedJsonEvent]) -> Vec<OwnedJsonEvent> {
1288 events
1289 .iter()
1290 .map(|e| match e {
1291 OwnedJsonEvent::Number { value, .. } => OwnedJsonEvent::Number {
1292 value: *value,
1293 lexeme: None,
1294 },
1295 other => other.clone(),
1296 })
1297 .collect()
1298 }
1299
1300 fn walked(value: &tabnas::Value) -> Vec<OwnedJsonEvent> {
1301 let mut rec = Vec::new();
1302 ValueSource(value).run(&mut rec).unwrap();
1303 rec
1304 }
1305
1306 const JSONL: &str = "{\"a\":1.50,\"b\":[true,null]}\r\n\n \n{\"a\":2,\"b\":\"x\"}\n[3]\n\"s\"";
1307
1308 #[test]
1312 fn the_line_sources_grammar_is_verified() {
1313 assert!(crate::source::capability::incremental("json"));
1314 }
1315
1316 #[test]
1317 fn jsonl_matches_the_whole_file_parse_at_every_reader_boundary() {
1318 let want = walked(&tabnas_jsonl::parse(JSONL).unwrap());
1319 for step in 1..=JSONL.len() {
1320 let mut rec: Vec<OwnedJsonEvent> = Vec::new();
1321 LinesSource::new(trickle(JSONL, step), LineFormat::Jsonl)
1322 .run(&mut rec)
1323 .unwrap();
1324 assert_eq!(rec, want, "borrowed, step {step}");
1325
1326 let (r, rec) = LinesSource::new(trickle(JSONL, step), LineFormat::Jsonl)
1327 .run_owned(Vec::<OwnedJsonEvent>::new());
1328 assert_eq!(r.unwrap(), Flow::Continue);
1329 assert_eq!(without_lexemes(&rec), want, "owned, step {step}");
1330 assert!(
1331 rec.contains(&OwnedJsonEvent::Number {
1332 value: 1.5,
1333 lexeme: Some("1.50".into())
1334 }),
1335 "the owned path keeps lexemes"
1336 );
1337 }
1338 }
1339
1340 #[test]
1341 fn a_bad_jsonl_line_names_its_line_number() {
1342 let text = "{\"a\":1}\n\n{\"a\": }\n{\"a\":2}\n";
1343 let mut rec: Vec<OwnedJsonEvent> = Vec::new();
1344 let err = LinesSource::new(Cursor::new(text), LineFormat::Jsonl)
1345 .run(&mut rec)
1346 .unwrap_err();
1347 assert_eq!(err.code, Code::InputInvalid);
1348 assert_eq!(err.row, Some(3));
1349 assert_eq!(err.column, Some(7));
1350 let (r, _) = LinesSource::new(Cursor::new(text), LineFormat::Jsonl)
1351 .run_owned(Vec::<OwnedJsonEvent>::new());
1352 let err = r.unwrap_err();
1353 assert_eq!(err.code, Code::InputInvalid);
1354 assert_eq!(err.row, Some(3));
1355 }
1356
1357 #[test]
1358 fn empty_input_is_an_empty_array_for_both_formats() {
1359 for format in [LineFormat::Jsonl, LineFormat::csv()] {
1360 let mut rec: Vec<OwnedJsonEvent> = Vec::new();
1361 LinesSource::new(Cursor::new(""), format.clone())
1362 .run(&mut rec)
1363 .unwrap();
1364 assert_eq!(
1365 rec,
1366 vec![
1367 OwnedJsonEvent::ArrayStart,
1368 OwnedJsonEvent::ArrayEnd,
1369 OwnedJsonEvent::End
1370 ]
1371 );
1372 let (r, rec) = LinesSource::new(Cursor::new("\n\n"), format)
1373 .run_owned(Vec::<OwnedJsonEvent>::new());
1374 r.unwrap();
1375 assert_eq!(rec.len(), 3);
1376 }
1377 }
1378
1379 const CSV: &str =
1380 "id,note,n\r\n1,\"multi\r\nline, with \"\"quotes\"\"\",2.50\r\n3,plain,4\r\n\r\n5,\"a\",6";
1381
1382 #[test]
1383 fn csv_matches_the_whole_file_parse_at_every_chunk_size_and_reader_boundary() {
1384 let want = walked(&tabnas_csv::parse(CSV).unwrap());
1385 assert_eq!(
1386 want.iter()
1387 .filter(|e| matches!(e, OwnedJsonEvent::ObjectStart))
1388 .count(),
1389 3
1390 );
1391 for chunk_bytes in 0..=CSV.len() + 1 {
1392 let mut rec: Vec<OwnedJsonEvent> = Vec::new();
1393 LinesSource::new(Cursor::new(CSV), LineFormat::csv())
1394 .chunk_bytes(chunk_bytes)
1395 .run(&mut rec)
1396 .unwrap();
1397 assert_eq!(rec, want, "chunk {chunk_bytes}");
1398 }
1399 for step in 1..=CSV.len() {
1400 let (r, rec) = LinesSource::new(trickle(CSV, step), LineFormat::csv())
1401 .chunk_bytes(7)
1402 .run_owned(Vec::<OwnedJsonEvent>::new());
1403 r.unwrap();
1404 assert_eq!(rec, want, "step {step}");
1405 }
1406 }
1407
1408 #[test]
1409 fn csv_without_a_header_yields_the_grammars_records() {
1410 let text = "1,2\n3,\"4\n5\"\n";
1411 for object in [true, false] {
1412 let options = CsvOptions {
1413 header: false,
1414 object,
1415 ..CsvOptions::default()
1416 };
1417 let want = walked(&tabnas_csv::make_with(options.clone()).parse(text).unwrap());
1418 for chunk_bytes in 0..=text.len() {
1419 let mut rec: Vec<OwnedJsonEvent> = Vec::new();
1420 LinesSource::new(
1421 Cursor::new(text),
1422 LineFormat::csv_with(false, options.clone()),
1423 )
1424 .chunk_bytes(chunk_bytes)
1425 .run(&mut rec)
1426 .unwrap();
1427 assert_eq!(rec, want, "object {object}, chunk {chunk_bytes}");
1428 }
1429 }
1430 }
1431
1432 #[test]
1433 fn a_header_only_file_is_an_empty_table() {
1434 let mut rec: Vec<OwnedJsonEvent> = Vec::new();
1435 LinesSource::new(Cursor::new("a,b\n"), LineFormat::csv())
1436 .run(&mut rec)
1437 .unwrap();
1438 assert_eq!(rec.len(), 3);
1439 }
1440
1441 #[test]
1442 fn a_bad_csv_record_names_its_file_line() {
1443 let text = "a,b\n1,2\n3,\"x\n4,5\n";
1444 let mut rec: Vec<OwnedJsonEvent> = Vec::new();
1445 let err = LinesSource::new(Cursor::new(text), LineFormat::csv())
1446 .chunk_bytes(0)
1447 .run(&mut rec)
1448 .unwrap_err();
1449 assert_eq!(err.code, Code::InputInvalid);
1450 assert_eq!(err.row, Some(3), "{err}");
1451 let err = LinesSource::new(Cursor::new(text), LineFormat::csv())
1452 .run(&mut Vec::<OwnedJsonEvent>::new())
1453 .unwrap_err();
1454 assert_eq!(err.row, Some(3), "{err}");
1455 }
1456
1457 struct Counting<R> {
1460 inner: R,
1461 read: usize,
1462 }
1463
1464 impl<R: Read> Read for Counting<R> {
1465 fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
1466 let n = self.inner.read(out)?;
1467 self.read += n;
1468 Ok(n)
1469 }
1470 }
1471
1472 struct Endless(u8);
1474
1475 impl Read for Endless {
1476 fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
1477 out.fill(self.0);
1478 Ok(out.len())
1479 }
1480 }
1481
1482 #[test]
1483 fn an_unterminated_line_is_refused_at_the_limit_not_after_being_read_whole() {
1484 const BUFFER: usize = 4096;
1485 let limits = Limits {
1486 max_record_bytes: 8,
1487 ..Limits::default()
1488 };
1489 for format in [LineFormat::Jsonl, LineFormat::csv()] {
1490 let mut reader = io::BufReader::with_capacity(
1491 BUFFER,
1492 Counting {
1493 inner: Endless(b'a'),
1494 read: 0,
1495 },
1496 );
1497 let err = LinesSource::new(&mut reader, format.clone())
1498 .limits(limits.clone())
1499 .run(&mut Vec::<OwnedJsonEvent>::new())
1500 .unwrap_err();
1501 assert_eq!(err.limit.as_ref().unwrap().name, "max_record_bytes");
1502 assert_eq!(err.row, Some(1));
1503 let read = reader.into_inner().read;
1504 assert!(
1505 read <= limits.max_record_bytes + BUFFER,
1506 "{format:?}: {read} bytes were pulled from an endless line"
1507 );
1508 }
1509 }
1510
1511 #[test]
1512 fn an_oversized_record_names_max_record_bytes() {
1513 let limits = Limits {
1514 max_record_bytes: 8,
1515 ..Limits::default()
1516 };
1517 let err = LinesSource::new(
1518 Cursor::new("{\"a\":1}\n{\"a\":123456}\n"),
1519 LineFormat::Jsonl,
1520 )
1521 .limits(limits.clone())
1522 .run(&mut Vec::<OwnedJsonEvent>::new())
1523 .unwrap_err();
1524 assert_eq!(err.limit.as_ref().unwrap().name, "max_record_bytes");
1525 assert_eq!(err.row, Some(2));
1526 let err = LinesSource::new(
1527 Cursor::new("a\n\"long\nquoted\nfield\"\n"),
1528 LineFormat::csv(),
1529 )
1530 .limits(limits)
1531 .run(&mut Vec::<OwnedJsonEvent>::new())
1532 .unwrap_err();
1533 assert_eq!(err.limit.as_ref().unwrap().name, "max_record_bytes");
1534 assert_eq!(err.row, Some(2));
1535 }
1536
1537 #[test]
1538 fn a_stop_and_an_abort_end_the_run_on_both_paths() {
1539 let text = "{\"a\":1}\n{\"a\":2}\n{\"a\":3}\n";
1540 let stopper = || {
1541 let mut n = 0;
1542 FnSink(move |_ev: JsonEvent<'_>| {
1543 n += 1;
1544 Ok(if n == 4 { Flow::Stop } else { Flow::Continue })
1545 })
1546 };
1547 let mut sink = stopper();
1548 assert_eq!(
1549 LinesSource::new(Cursor::new(text), LineFormat::Jsonl)
1550 .run(&mut sink)
1551 .unwrap(),
1552 Flow::Stop
1553 );
1554 let (r, _) = LinesSource::new(Cursor::new(text), LineFormat::Jsonl).run_owned(stopper());
1555 assert_eq!(r.unwrap(), Flow::Stop);
1556
1557 let abort = AbortFlag::new();
1558 abort.abort();
1559 let err = LinesSource::new(Cursor::new(text), LineFormat::Jsonl)
1560 .abort(abort.clone())
1561 .run(&mut Vec::<OwnedJsonEvent>::new())
1562 .unwrap_err();
1563 assert_eq!(err.code, Code::Aborted);
1564 let (r, _) = LinesSource::new(Cursor::new(text), LineFormat::Jsonl)
1565 .abort(abort)
1566 .run_owned(Vec::<OwnedJsonEvent>::new());
1567 assert_eq!(r.unwrap_err().code, Code::Aborted);
1568 }
1569
1570 #[test]
1571 fn an_abort_names_the_row_the_record_or_chunk_it_was_reading_starts_on() {
1572 let aborter = |abort: AbortFlag, after: usize| {
1576 let mut n = 0;
1577 FnSink(move |_ev: JsonEvent<'_>| {
1578 n += 1;
1579 if n == after {
1580 abort.abort();
1581 }
1582 Ok(Flow::Continue)
1583 })
1584 };
1585 let raised = |already: bool| {
1586 let abort = AbortFlag::new();
1587 if already {
1588 abort.abort();
1589 }
1590 abort
1591 };
1592 let text = "{\"a\":1}\n\n{\"a\":2}\n{\"a\":3}\n";
1598 for (after, row) in [(6, Some(3)), (0, None)] {
1599 let abort = raised(after == 0);
1600 let err = LinesSource::new(Cursor::new(text), LineFormat::Jsonl)
1601 .abort(abort.clone())
1602 .run(&mut aborter(abort, after))
1603 .unwrap_err();
1604 assert_eq!(
1605 (err.code, err.row, err.column),
1606 (Code::Aborted, row, None),
1607 "{err}"
1608 );
1609 let abort = raised(after == 0);
1610 let (r, _) = LinesSource::new(Cursor::new(text), LineFormat::Jsonl)
1611 .abort(abort.clone())
1612 .run_owned(aborter(abort, after));
1613 let err = r.unwrap_err();
1614 assert_eq!(
1615 (err.code, err.row, err.column),
1616 (Code::Aborted, row, None),
1617 "{err}"
1618 );
1619 }
1620 for (chunk_bytes, row) in [(0, 3), (DEFAULT_CHUNK_BYTES, 1)] {
1623 let abort = AbortFlag::new();
1624 let err = LinesSource::new(Cursor::new("a\n1\n2\n3\n"), LineFormat::csv())
1625 .abort(abort.clone())
1626 .chunk_bytes(chunk_bytes)
1627 .run(&mut aborter(abort, 5))
1628 .unwrap_err();
1629 assert_eq!(
1630 (err.code, err.row, err.column),
1631 (Code::Aborted, Some(row), None),
1632 "chunk {chunk_bytes}: {err}"
1633 );
1634 }
1635 }
1636
1637 #[test]
1638 fn invalid_utf8_is_invalid_input_at_its_line() {
1639 let bytes: &[u8] = b"{\"a\":1}\n{\"a\":\"\xff\"}\n";
1640 let err = LinesSource::new(Cursor::new(bytes), LineFormat::Jsonl)
1641 .run(&mut Vec::<OwnedJsonEvent>::new())
1642 .unwrap_err();
1643 assert_eq!(err.code, Code::InputInvalid);
1644 assert_eq!(err.row, Some(2));
1645 }
1646
1647 type Reading = Result<Vec<OwnedJsonEvent>, (String, Option<u64>, Option<u64>)>;
1650
1651 fn whole(parsed: Result<tabnas::Value, tabnas::TabnasError>) -> Reading {
1653 match parsed {
1654 Ok(value) => Ok(walked(&value)),
1655 Err(e) => Err((e.code.to_string(), Some(e.row as u64), Some(e.col as u64))),
1656 }
1657 }
1658
1659 fn reading(outcome: Result<Flow, Fail>, events: Vec<OwnedJsonEvent>) -> Reading {
1662 match outcome {
1663 Ok(flow) => {
1664 assert_eq!(flow, Flow::Continue);
1665 Ok(events)
1666 }
1667 Err(fail) => {
1668 assert_eq!(fail.code, Code::InputInvalid, "{fail}");
1669 let code = fail.message.split(':').next().unwrap_or_default();
1670 Err((code.to_string(), fail.row, fail.column))
1671 }
1672 }
1673 }
1674
1675 fn csv_streams_as_whole(text: &str, options: &CsvOptions) -> Reading {
1680 let want = whole(tabnas_csv::make_with(options.clone()).parse(text));
1681 let format = LineFormat::csv_with(options.header, options.clone());
1682 let sizes =
1683 (0..=text.len() + 1).filter(|&n| n < 4 || n.is_power_of_two() || n >= text.len());
1684 for chunk_bytes in sizes {
1685 let mut rec: Vec<OwnedJsonEvent> = Vec::new();
1686 let outcome = LinesSource::new(Cursor::new(text), format.clone())
1687 .chunk_bytes(chunk_bytes)
1688 .run(&mut rec);
1689 assert_eq!(reading(outcome, rec), want, "{text:?}, chunk {chunk_bytes}");
1690 }
1691 for step in [1, 3] {
1692 let (outcome, rec) = LinesSource::new(trickle(text, step), format.clone())
1693 .chunk_bytes(0)
1694 .run_owned(Vec::<OwnedJsonEvent>::new());
1695 assert_eq!(reading(outcome, rec), want, "{text:?}, step {step}");
1696 }
1697 want
1698 }
1699
1700 fn jsonl_streams_as_whole(text: &str) -> Reading {
1703 let want = whole(tabnas_jsonl::parse(text));
1704 let mut rec: Vec<OwnedJsonEvent> = Vec::new();
1705 let outcome = LinesSource::new(Cursor::new(text), LineFormat::Jsonl).run(&mut rec);
1706 assert_eq!(reading(outcome, rec), want, "borrowed, {text:?}");
1707 let (outcome, rec) = LinesSource::new(Cursor::new(text), LineFormat::Jsonl)
1708 .run_owned(Vec::<OwnedJsonEvent>::new());
1709 let rec = without_lexemes(&rec);
1710 assert_eq!(reading(outcome, rec), want, "owned, {text:?}");
1711 want
1712 }
1713
1714 fn objects(events: &[OwnedJsonEvent]) -> usize {
1715 events
1716 .iter()
1717 .filter(|e| matches!(e, OwnedJsonEvent::ObjectStart))
1718 .count()
1719 }
1720
1721 const MISPLACED_QUOTES: &str = "A,B\",C\nD,E\",F";
1729
1730 #[test]
1731 fn quotes_inside_a_field_are_read_as_the_grammar_reads_them() {
1732 let header = CsvOptions::default();
1733 let want = csv_streams_as_whole(MISPLACED_QUOTES, &header).unwrap();
1734 assert_eq!(objects(&want), 1, "the header and one record: {want:?}");
1735 let corpus = CsvOptions {
1737 header: false,
1738 object: false,
1739 ..CsvOptions::default()
1740 };
1741 csv_streams_as_whole(MISPLACED_QUOTES, &corpus).unwrap();
1742 let more = format!("{MISPLACED_QUOTES}\nG,H,I\nJ\"K,L,\"M\nN\"\n");
1747 let want = csv_streams_as_whole(&more, &header).unwrap();
1748 assert_eq!(objects(&want), 3, "{want:?}");
1749 csv_streams_as_whole(&more, &corpus).unwrap();
1750 }
1751
1752 #[test]
1753 fn an_input_ending_inside_a_quoted_field_fails_as_the_grammar_does() {
1754 for text in [
1757 "a,\"b\n",
1758 "a,\"b",
1759 "a,\"b\nc,d\n",
1760 "\"\n",
1761 "a\n\"x\n",
1762 "a\n1\n\"x",
1763 ] {
1764 let want = csv_streams_as_whole(text, &CsvOptions::default());
1765 assert!(
1766 matches!(&want, Err((code, ..)) if code == "unterminated_string"),
1767 "{text:?}: {want:?}"
1768 );
1769 }
1770 }
1771
1772 #[test]
1773 fn a_last_record_without_a_newline_is_read() {
1774 for text in ["a,b\n1,2\n3,4", "a,b\n1,2\n3,\"x\ny\"", "a,b\r\n1,2\r\n3,4"] {
1775 let want = csv_streams_as_whole(text, &CsvOptions::default()).unwrap();
1776 assert_eq!(objects(&want), 2, "{text:?}");
1777 }
1778 }
1779
1780 fn csv_options(edit: impl FnOnce(&mut CsvOptions)) -> CsvOptions {
1781 let mut options = CsvOptions::default();
1782 edit(&mut options);
1783 options
1784 }
1785
1786 #[test]
1787 fn the_header_is_the_first_record_the_grammar_reads() {
1788 let empty = csv_options(|o| o.record.empty = true);
1789 let relaxed = csv_options(|o| o.strict = false);
1790 let comment = csv_options(|o| o.comment = Some(true));
1791 let cases: &[(&str, &CsvOptions, usize)] = &[
1792 ("\n\na,b\n1,2\n3,4\n", &CsvOptions::default(), 2),
1794 ("\r\na,b\r\n1,2\r\n3,4\r\n", &CsvOptions::default(), 2),
1795 ("\na,b\n\n1,2\n", &empty, 3),
1797 (" \na,b\n1,2\n3,4\n", &CsvOptions::default(), 3),
1799 (" \na,b\n1,2\n3,4\n", &relaxed, 2),
1800 ("# x \"\na,b\n1,2\n3,4\n", &comment, 2),
1802 ("// x \"\na,b\n1,2\n3,4\n", &relaxed, 2),
1803 ("a,b # c \"\n1,2\n3,4\n", &comment, 2),
1804 ];
1805 for (text, options, records) in cases {
1806 let want = csv_streams_as_whole(text, options).unwrap();
1807 assert_eq!(objects(&want), *records, "{text:?}");
1808 }
1809 }
1810
1811 #[test]
1812 fn a_lone_carriage_return_ends_a_csv_record() {
1813 for text in ["a,b\r1,2\r3,4\r", "a,b\r1,2\n3,4\n5,6\n"] {
1814 let want = csv_streams_as_whole(text, &CsvOptions::default()).unwrap();
1815 assert!(objects(&want) >= 2, "{text:?}");
1816 }
1817 }
1818
1819 #[test]
1820 fn a_field_spans_lines_only_where_the_grammar_reads_one_that_does() {
1821 let quote = csv_options(|o| o.string.quote = "'".into());
1822 let tildes = csv_options(|o| o.field.separation = Some("~~".into()));
1823 let comment = csv_options(|o| o.comment = Some(true));
1824 let cases: &[(&str, &CsvOptions)] = &[
1825 ("a,b\n`x\ny`,z\n1,2\n", &CsvOptions::default()),
1829 ("a,b\n'x\\\ny',z\n1,2\n", &CsvOptions::default()),
1830 ("a,b\n'x,\"y',z\n\"p\nq\",r\n", &CsvOptions::default()),
1831 ("a,b\nx, \"p\nq\"\n1,2\n", &CsvOptions::default()),
1833 ("a~~b\nx~~\"p\nq\"\n1~~2\n", &tildes),
1834 ("a,b\n'x\ny',z\n1,2\n", "e),
1837 ("a,b\n\"x,y\n1,2\n", "e),
1838 ("a,b\n/* x\ny */1,2\n3,4\n", &comment),
1840 ];
1841 for (text, options) in cases {
1842 let _ = csv_streams_as_whole(text, options);
1843 }
1844 }
1845
1846 #[test]
1847 fn configured_record_separators_end_records() {
1848 let semicolons = csv_options(|o| o.record.separators = Some(";".into()));
1850 let want = csv_streams_as_whole("a,b;1,x\ny;3,4\nz;5,6", &semicolons).unwrap();
1851 assert_eq!(objects(&want), 3);
1852 }
1853
1854 #[test]
1855 fn a_jsonl_line_is_blank_only_when_the_grammar_reads_it_so() {
1856 for blank in ["\u{c}", "\u{b}", "\u{a0}", "\u{85}", "\u{2028}", "\u{3000}"] {
1860 let text = format!("{{\"a\":1}}\n{blank}\n{{\"b\":2}}\n");
1861 let want = jsonl_streams_as_whole(&text);
1862 assert_eq!(
1863 want.unwrap_err().1,
1864 Some(2),
1865 "{blank:?} is refused on its line"
1866 );
1867 }
1868 let want = jsonl_streams_as_whole("{\"a\":1}\n \t\n\r\n{\"b\":2}\n ").unwrap();
1869 assert_eq!(objects(&want), 2);
1870 }
1871
1872 #[test]
1873 fn a_lone_carriage_return_ends_a_jsonl_record() {
1874 let want = jsonl_streams_as_whole("{\"a\":1}\r{\"b\":2}\r\r \r{\"c\":3}\n").unwrap();
1875 assert_eq!(objects(&want), 3);
1876 for text in ["{\"a\":\r1}\n", "{\"a\":1}\r{\"b\": }\n"] {
1879 jsonl_streams_as_whole(text).unwrap_err();
1880 }
1881 jsonl_streams_as_whole("{\"a\":\"x\ry\"}\n").unwrap_err();
1884 }
1885
1886 #[test]
1887 fn records_one_line_holds_are_bounded_and_cut_one_by_one() {
1888 let limits = Limits {
1894 max_record_bytes: 8,
1895 ..Limits::default()
1896 };
1897 let crs = format!("a,b\r{}", "1,2\r".repeat(200));
1898 let semicolons = csv_options(|o| o.record.separators = Some(";".into()));
1899 let separated = format!("a,b;{}", "1,x;".repeat(200));
1900 for (text, options) in [(&crs, CsvOptions::default()), (&separated, semicolons)] {
1901 let want = whole(tabnas_csv::make_with(options.clone()).parse(text));
1902 assert_eq!(objects(want.as_ref().unwrap()), 200, "{text:?}");
1903 let format = LineFormat::csv_with(options.header, options.clone());
1904 for chunk_bytes in [0, 64, DEFAULT_CHUNK_BYTES] {
1905 let mut rec: Vec<OwnedJsonEvent> = Vec::new();
1906 let outcome = LinesSource::new(Cursor::new(text.as_str()), format.clone())
1907 .limits(limits.clone())
1908 .chunk_bytes(chunk_bytes)
1909 .run(&mut rec);
1910 assert_eq!(reading(outcome, rec), want, "{text:?}, chunk {chunk_bytes}");
1911 }
1912 let parser = tabnas_csv::make_with(options.clone());
1914 let mut chunks = Chunks::new(
1915 Cursor::new(text.as_bytes()),
1916 &parser,
1917 &options,
1918 64,
1919 limits.max_record_bytes,
1920 );
1921 let mut count = 0;
1922 while let Some(chunk) = chunks.next_chunk().unwrap() {
1923 assert!(chunk.text.len() <= 64 + 8, "{:?}", chunk.text);
1924 count += 1;
1925 }
1926 assert!(count > 10, "{text:?}: {count} chunks");
1927 }
1928 let records = "{\"a\":1}\r".repeat(200);
1929 let want = whole(tabnas_jsonl::parse(&records));
1930 assert_eq!(objects(want.as_ref().unwrap()), 200);
1931 let mut rec: Vec<OwnedJsonEvent> = Vec::new();
1932 let outcome = LinesSource::new(Cursor::new(records.as_str()), LineFormat::Jsonl)
1933 .limits(limits.clone())
1934 .run(&mut rec);
1935 assert_eq!(reading(outcome, rec), want);
1936 let (outcome, rec) = LinesSource::new(Cursor::new(records.as_str()), LineFormat::Jsonl)
1937 .limits(limits.clone())
1938 .run_owned(Vec::<OwnedJsonEvent>::new());
1939 assert_eq!(reading(outcome, without_lexemes(&rec)), want);
1940 let semicolons = csv_options(|o| o.record.separators = Some(";".into()));
1944 for (text, options, row) in [
1945 ("a,b\r1,2\r123456789,x\r", CsvOptions::default(), 1),
1946 ("a,b;1,x;123456789,x;", semicolons, 3),
1947 ] {
1948 let err = LinesSource::new(
1949 Cursor::new(text),
1950 LineFormat::csv_with(options.header, options),
1951 )
1952 .limits(limits.clone())
1953 .run(&mut Vec::<OwnedJsonEvent>::new())
1954 .unwrap_err();
1955 assert_eq!(err.limit.as_ref().unwrap().name, "max_record_bytes");
1956 assert_eq!(err.row, Some(row), "{text:?}: {err}");
1957 }
1958 }
1959
1960 #[test]
1961 fn a_line_token_of_two_characters_is_never_cut() {
1962 for header in [false, true] {
1967 let options = csv_options(|o| {
1968 o.record.empty = true;
1969 o.header = header;
1970 });
1971 for text in [
1972 "a,b\r\n1,2\r\n\r\n3,4\r\n",
1973 "a,b\n\r1,2\n\r\n\r3,4",
1974 "a\r\n\r\r\nb\r\n",
1975 ] {
1976 csv_streams_as_whole(text, &options).unwrap();
1977 }
1978 }
1979 }
1980
1981 #[test]
1982 fn a_line_character_inside_a_jsonl_string_is_the_strings() {
1983 for text in [
1988 "{\"a\":\"x\ny\"}\n{\"b\":1}\n",
1989 "[\"\\\n\"]\n",
1990 "{\"a\":\"x\r\ny\"}\r\n",
1991 ] {
1992 jsonl_streams_as_whole(text).unwrap_err();
1993 }
1994 }
1995
1996 #[test]
1997 fn a_line_character_inside_a_fixed_token_ends_no_piece() {
1998 let starts = csv_options(|o| o.field.separation = Some("\n~".into()));
2004 let inside = csv_options(|o| o.field.separation = Some("~\n~".into()));
2005 for (text, options) in [
2006 ("a\n~b\nx\n~y\n", &starts),
2007 ("a\n~b\n\n~c\nx\n~y\n", &starts),
2008 ("a\n~b\r\n~c\nx\n~y", &starts),
2009 ("a~\n~b\nx~\n~y\n", &inside),
2010 ] {
2011 let _ = csv_streams_as_whole(text, options);
2012 }
2013 }
2014
2015 #[test]
2016 fn a_long_line_token_is_followed_once() {
2017 let run = 100_000;
2023 let text = format!("a\n~b\nc\n~d{}x\n~y\n", "\n".repeat(run));
2024 let mut pieces = Pieces::new(
2025 trickle(&text, 1),
2026 vec!['\r', '\n'],
2027 vec!['\n'],
2028 false,
2029 &["\n~".to_string()],
2030 );
2031 let mut got = Vec::new();
2032 while let Some((row, piece)) = pieces.next_piece(usize::MAX, || unreachable!()).unwrap() {
2033 got.push((row, piece.len()));
2034 }
2035 let run = run as u64;
2036 assert_eq!(got, [(1, 5), (3, 4 + run as usize), (4 + run, 5)]);
2037 }
2038
2039 #[test]
2040 fn a_separator_of_several_bytes_is_found_across_reads() {
2041 let one = csv_options(|o| o.record.separators = Some("␞".into()));
2046 csv_streams_as_whole("a,b␞1,é␞3,4␞", &one).unwrap();
2047 let astral = csv_options(|o| o.record.separators = Some("😀".into()));
2050 csv_streams_as_whole("a,b😀1,é😀3,4😀", &astral).unwrap();
2051 csv_streams_as_whole("a,b😀1,2😀3,\"x", &astral).unwrap_err();
2052 let two = csv_options(|o| {
2053 o.record.separators = Some("␞¶".into());
2054 o.record.empty = true;
2055 o.header = false;
2056 });
2057 csv_streams_as_whole("a,b␞¶£,é␞£,2¶␞3,4", &two).unwrap();
2058 }
2059}