numeric_csv_parsing/
numeric_csv_parsing.rs

1use std::{num::ParseFloatError, vec};
2
3use validiter::{AtLeast, ConstOver, Ensure};
4
5fn main() {
6    // In this example we will use the 'cast_errs' method to
7    // create a 'Vec<Vec<f64>>' collection, while ensuring
8    // the mathematical validity if this collection as a numerical
9    // matrix. To exercise the 'ensure' adapter, we'll force all
10    // elements to be non-negative as well
11
12    // Here we define the errors we expect to encounter in
13    // the parsing process:
14    #[derive(Debug)]
15    enum MatParseErr {
16        NotAFloat(usize, usize, ParseFloatError),
17        NoColumns(usize),
18        Negative(usize, usize, f64),
19        NoRows,
20        JaggedArray(usize, Vec<f64>, usize, usize),
21    }
22
23    // this is a CSV format str, with 2 rows and 2 columns
24    let csv = "1.2, 3.0
25                4.2, 0.5";
26
27    // we'll use iterator methods on the CSV to build an actual matrix over f64
28    let mat = csv
29        .lines()
30        .enumerate()
31        .map(|(i, line)| {
32            line.split(",")
33                .map(|s| s.trim())
34                .enumerate()
35                .map(|(j, s)| {
36                    s.parse::<f64>()
37                        .map_err(|parse_err| MatParseErr::NotAFloat(i, j, parse_err))
38                })
39                .ensure(|val| *val >= 0.0, |j, val| MatParseErr::Negative(i, j, val))
40                .at_least(1, |_| MatParseErr::NoColumns(i))
41                .collect::<Result<Vec<f64>, MatParseErr>>()
42        })
43        .at_least(1, |_| MatParseErr::NoRows)
44        .const_over(
45            |vec| vec.len(),
46            |i, vec, len, expected_len| MatParseErr::JaggedArray(i, vec, len, *expected_len),
47        )
48        .collect::<Result<Vec<_>, _>>();
49
50    match mat {
51        Ok(mat) => {
52            assert_eq!(mat, vec![vec![1.2, 3.0], vec![4.2, 0.5]]);
53            println!("{mat:?}")
54        }
55        Err(mperr) => match mperr {
56            MatParseErr::NotAFloat(i, j, err) => println!("Got {err} at pos [{i}, {j}]"),
57            MatParseErr::NoColumns(i) => {
58                println!("Row {i} is without any data, which would force the matrix to be empty")
59            }
60            MatParseErr::Negative(i, j, val) => {
61                println!("value {val} at pos [{i}, {j}] is negative")
62            }
63            MatParseErr::NoRows => println!("There are no rows in the matrix"),
64            MatParseErr::JaggedArray(i, _vec, len, expected_len) => {
65                println!("Row {i} has len {len}, when all rows should have length {expected_len}")
66            }
67        },
68    }
69}