CSV Streaming Reader Deserializer
Stream CSV files record by record with the csv crate, validating deserialization per row and quarantining malformed lines.
Code
use serde::Deserialize;
use std::io::Cursor;
#[derive(Debug, Deserialize)]
struct Subscriber {
#[serde(rename = "Email")]
email: String,
#[serde(rename = "Plan")]
plan: String,
#[serde(rename = "Seats")]
seats: u32,
}
fn stream_subscribers(
raw: &str,
) -> Result<(Vec<Subscriber>, Vec<(usize, String)>), csv::Error> {
let mut reader = csv::ReaderBuilder::new()
.has_headers(true)
.trim(csv::Trim::All)
.flexible(false)
.from_reader(Cursor::new(raw));
let mut valid = Vec::new();
let mut rejected: Vec<(usize, String)> = Vec::new();
let mut line = 1;
for outcome in reader.deserialize::<Subscriber>() {
line += 1;
match outcome {
Ok(record) => valid.push(record),
Err(err) => {
// 单行结构或类型错误:隔离并保留行号
rejected.push((line, err.to_string()));
}
}
}
Ok((valid, rejected))
}
fn main() {
let payload = "Email,Plan,Seats\n\
[email protected],pro,10\n\
[email protected],basic,not-a-number\n\
[email protected],enterprise,250\n\
broken-line-only-one-field\n";
match stream_subscribers(payload) {
Ok((valid, rejected)) => {
for record in &valid {
println!("{} on {} x{}", record.email, record.plan, record.seats);
}
for (line, reason) in &rejected {
println!("line {line} rejected: {reason}");
}
println!("valid: {}, rejected: {}", valid.len(), rejected.len());
}
Err(err) => println!("fatal csv error: {err}"),
}
}Report a problem with this exampleOpens a pre-filled issue on GitHub.