pub struct CsvParser {
delimiter: u8,
quote_char: u8,
}
impl CsvParser {
pub fn new(delimiter: u8, quote_char: u8) -> Self {
Self {
delimiter,
quote_char,
}
}
pub fn parse_line(&self, line: &str) -> Vec<String> {
let mut fields = Vec::with_capacity(16); let mut current_field = String::with_capacity(64);
let mut in_quotes = false;
let mut chars = line.chars().peekable();
while let Some(ch) = chars.next() {
if ch == self.quote_char as char {
if in_quotes {
if chars.peek() == Some(&(self.quote_char as char)) {
current_field.push(self.quote_char as char);
chars.next(); } else {
in_quotes = false;
}
} else {
in_quotes = true;
}
} else if ch == self.delimiter as char && !in_quotes {
fields.push(current_field.clone());
current_field.clear();
} else {
current_field.push(ch);
}
}
fields.push(current_field);
fields
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple() {
let parser = CsvParser::new(b',', b'"');
assert_eq!(parser.parse_line("a,b,c"), vec!["a", "b", "c"]);
}
#[test]
fn test_quoted() {
let parser = CsvParser::new(b',', b'"');
assert_eq!(parser.parse_line(r#""a,b",c"#), vec!["a,b", "c"]);
}
#[test]
fn test_escaped_quotes() {
let parser = CsvParser::new(b',', b'"');
assert_eq!(
parser.parse_line(r#""Say ""Hello""",world"#),
vec![r#"Say "Hello""#, "world"]
);
}
#[test]
fn test_empty_fields() {
let parser = CsvParser::new(b',', b'"');
assert_eq!(parser.parse_line("a,,c"), vec!["a", "", "c"]);
}
#[test]
fn test_all_empty() {
let parser = CsvParser::new(b',', b'"');
assert_eq!(parser.parse_line(",,"), vec!["", "", ""]);
}
#[test]
fn test_quoted_with_newline() {
let parser = CsvParser::new(b',', b'"');
assert_eq!(
parser.parse_line("\"Line 1\nLine 2\",normal"),
vec!["Line 1\nLine 2", "normal"]
);
}
#[test]
fn test_mixed_quoted_unquoted() {
let parser = CsvParser::new(b',', b'"');
assert_eq!(parser.parse_line(r#"a,"b,c",d"#), vec!["a", "b,c", "d"]);
}
#[test]
fn test_custom_delimiter() {
let parser = CsvParser::new(b';', b'"');
assert_eq!(parser.parse_line(r#"a;"b;c";d"#), vec!["a", "b;c", "d"]);
}
#[test]
fn test_empty_line() {
let parser = CsvParser::new(b',', b'"');
assert_eq!(parser.parse_line(""), vec![""]);
}
#[test]
fn test_single_field() {
let parser = CsvParser::new(b',', b'"');
assert_eq!(parser.parse_line("hello"), vec!["hello"]);
}
#[test]
fn test_quoted_empty() {
let parser = CsvParser::new(b',', b'"');
assert_eq!(parser.parse_line(r#""","""#), vec!["", ""]);
}
}