Skip to main content

windjammer_runtime/
csv_mod.rs

1//! CSV parsing and writing
2//!
3//! Windjammer's `std::csv` module maps to these functions.
4
5use csv::Writer;
6use std::io::Cursor;
7
8/// Parse CSV string into rows
9pub fn parse(data: &str) -> Result<Vec<Vec<String>>, String> {
10    let mut reader = csv::ReaderBuilder::new()
11        .has_headers(false) // Treat all rows as data, not headers
12        .from_reader(Cursor::new(data));
13    let mut rows = Vec::new();
14
15    for result in reader.records() {
16        let record = result.map_err(|e| e.to_string())?;
17        let row: Vec<String> = record.iter().map(|s| s.to_string()).collect();
18        rows.push(row);
19    }
20
21    Ok(rows)
22}
23
24/// Write rows to CSV string
25pub fn write(rows: &[Vec<String>]) -> Result<String, String> {
26    let mut writer = Writer::from_writer(vec![]);
27
28    for row in rows {
29        writer.write_record(row).map_err(|e| e.to_string())?;
30    }
31
32    let data = writer.into_inner().map_err(|e| e.to_string())?;
33    String::from_utf8(data).map_err(|e| e.to_string())
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    #[test]
41    fn test_parse() {
42        let csv = "a,b,c\n1,2,3\n4,5,6";
43        let rows = parse(csv).unwrap();
44        // CSV reader treats first row as data, not headers (unless configured)
45        assert_eq!(rows.len(), 3);
46        assert_eq!(rows[0], vec!["a", "b", "c"]);
47        assert_eq!(rows[1], vec!["1", "2", "3"]);
48        assert_eq!(rows[2], vec!["4", "5", "6"]);
49    }
50
51    #[test]
52    fn test_write() {
53        let rows = vec![
54            vec!["a".to_string(), "b".to_string()],
55            vec!["1".to_string(), "2".to_string()],
56        ];
57        let csv = write(&rows).unwrap();
58        assert!(csv.contains("a,b"));
59        assert!(csv.contains("1,2"));
60    }
61}