Skip to main content

data_05_errors/
data_05_errors.rs

1//! # data_05 — Boundary errors are explicit and precise
2//!
3//! Run: `cargo run -p matten-data --example data_05_errors`
4//!
5//! ## What this shows
6//! The common ways table input can be wrong, and the structured error each one
7//! produces. Malformed input is always a returned `Result`, never a panic.
8//!
9//! ## Teaching points
10//! - a duplicate header column is rejected up front;
11//! - a row with the wrong number of cells is a `RaggedRow` with the CSV line;
12//! - non-numeric text is never coerced — it is a `NonNumericValue` error;
13//! - a missing cell at conversion time is a `MissingValue` error (fill first).
14//!
15//! Row numbers are 1-based CSV line numbers: the header is line 1, so the first
16//! data row is line 2.
17
18use matten_data::{MattenDataError, Table};
19
20fn main() {
21    // 1. Duplicate header column.
22    match Table::from_csv_str("sales,sales\n1,2") {
23        Err(MattenDataError::DuplicateColumn { name }) => {
24            println!("duplicate header : {name}");
25            assert_eq!(name, "sales");
26        }
27        other => panic!("expected DuplicateColumn, got {other:?}"),
28    }
29
30    // 2. Ragged row: the second data row (CSV line 3) has too few cells.
31    match Table::from_csv_str("a,b,c\n1,2,3\n4,5") {
32        Err(MattenDataError::RaggedRow {
33            row,
34            expected,
35            actual,
36        }) => {
37            println!("ragged row       : line {row}, expected {expected}, got {actual}");
38            assert_eq!((row, expected, actual), (3, 3, 2));
39        }
40        other => panic!("expected RaggedRow, got {other:?}"),
41    }
42
43    // 3. Non-numeric value during conversion.
44    let with_text = Table::from_csv_str("label,value\nok,10\nbad,oops")
45        .expect("parses fine; the text only fails at numeric conversion");
46    match with_text
47        .select_columns(["value"])
48        .and_then(|t| t.try_numeric())
49    {
50        Err(MattenDataError::NonNumericValue { column, row, value }) => {
51            println!("non-numeric value: column={column}, line={row}, value={value:?}");
52            assert_eq!((column.as_str(), row, value.as_str()), ("value", 3, "oops"));
53        }
54        other => panic!("expected NonNumericValue, got {other:?}"),
55    }
56
57    // 4. Missing value during conversion (fill it first to proceed).
58    let with_missing =
59        Table::from_csv_str("a,b\n1,2\n3,").expect("missing cell is allowed in a Table");
60    match with_missing.try_numeric() {
61        Err(MattenDataError::MissingValue { column, row }) => {
62            println!("missing value    : column={column}, line={row}");
63            assert_eq!((column.as_str(), row), ("b", 3));
64        }
65        other => panic!("expected MissingValue, got {other:?}"),
66    }
67
68    println!("data_05_errors: OK");
69}