| 477 | } |
| 478 | |
| 479 | fn split_line(pos: usize, line: &str) -> Result<Vec<String>, PosError> { |
| 480 | let mut out = Vec::new(); |
| 481 | let mut field = String::new(); |
| 482 | let mut in_quotes = None; |
| 483 | let mut escaping = false; |
| 484 | for (i, c) in line.char_indices() { |
| 485 | if in_quotes.is_none() && c.is_whitespace() { |
| 486 | if !field.is_empty() { |
| 487 | out.push(field); |
| 488 | field = String::new(); |
| 489 | } |
| 490 | } else if c == '"' && !escaping { |
| 491 | if in_quotes.is_none() { |
| 492 | in_quotes = Some(i) |
| 493 | } else { |
| 494 | in_quotes = None; |
| 495 | out.push(field); |
| 496 | field = String::new(); |
| 497 | } |
| 498 | } else if c == '\\' && !escaping && in_quotes.is_some() { |
| 499 | escaping = true; |
| 500 | } else if escaping { |
| 501 | field.push(match c { |
| 502 | 'n' => '\n', |
| 503 | 't' => '\t', |
| 504 | 'r' => '\r', |
| 505 | '0' => '\0', |
| 506 | c => c, |
| 507 | }); |
| 508 | escaping = false; |
| 509 | } else { |
| 510 | field.push(c); |
| 511 | } |
| 512 | } |
| 513 | if let Some(i) = in_quotes { |
| 514 | return Err(PosError { |
| 515 | source: anyhow!("unterminated quote"), |
| 516 | pos: Some(pos + i), |
| 517 | }); |
| 518 | } |
| 519 | if !field.is_empty() { |
| 520 | out.push(field); |
| 521 | } |
| 522 | Ok(out) |
| 523 | } |
| 524 | |
| 525 | fn slurp_all(line_reader: &mut LineReader) -> Vec<String> { |
| 526 | let mut out = Vec::new(); |