matten_data/error.rs
1//! Crate-local error type for `matten-data` (RFC-034 §6).
2//!
3//! `MattenDataError` is the single error type returned by every fallible
4//! `matten-data` API. It is crate-local: core `matten::MattenError` is wrapped
5//! (see [`MattenDataError::Matten`]) but never extended for table-specific
6//! failures (RFC-014, RFC-033 §10).
7
8use std::fmt;
9use std::path::PathBuf;
10
11/// Errors produced by `matten-data` table ingestion and conversion.
12///
13/// All external-input APIs return this type; malformed input never panics
14/// (RFC-035 §1). Error messages include row/column context where practical.
15/// Row numbers are **one-based CSV line numbers** (the header is line 1, so the
16/// first data row is line 2).
17#[derive(Debug)]
18#[non_exhaustive]
19pub enum MattenDataError {
20 /// A CSV structural problem reported by the parser or by header validation
21 /// (for example an empty header name).
22 Csv {
23 /// Human-readable description of the problem.
24 message: String,
25 },
26 /// An I/O error while reading a CSV path.
27 Io {
28 /// The path that failed to read.
29 path: PathBuf,
30 /// The underlying I/O error.
31 source: std::io::Error,
32 },
33 /// The input was empty (no header row).
34 EmptyInput,
35 /// A requested column name does not exist in the table.
36 MissingColumn {
37 /// The requested column name.
38 name: String,
39 },
40 /// The CSV header contains a duplicate column name.
41 DuplicateColumn {
42 /// The duplicated column name.
43 name: String,
44 },
45 /// The same column name was requested more than once in a selection.
46 DuplicateSelection {
47 /// The duplicated selection name.
48 name: String,
49 },
50 /// A data row has a different number of cells than the header.
51 RaggedRow {
52 /// One-based CSV line number of the offending row.
53 row: usize,
54 /// Number of columns expected (from the header).
55 expected: usize,
56 /// Number of cells actually found.
57 actual: usize,
58 },
59 /// A cell could not be converted to `f64` during numeric conversion.
60 NonNumericValue {
61 /// Column name.
62 column: String,
63 /// One-based CSV line number.
64 row: usize,
65 /// The offending cell value, rendered as text.
66 value: String,
67 },
68 /// A missing cell remained during numeric conversion (fill it first).
69 MissingValue {
70 /// Column name.
71 column: String,
72 /// One-based CSV line number.
73 row: usize,
74 },
75 /// A column selection or conversion was attempted with no columns.
76 EmptySelection,
77 /// `CsvBatchReader::open` was called with `batch_rows == 0` (RFC-082 §4.5,
78 /// `streaming` feature only).
79 InvalidBatchSize,
80 /// A wrapped core `matten` error (for example from `Tensor` construction).
81 Matten(matten::MattenError),
82}
83
84impl fmt::Display for MattenDataError {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 match self {
87 MattenDataError::Csv { message } => {
88 write!(f, "matten-data CSV error: {message}")
89 }
90 MattenDataError::Io { path, source } => {
91 write!(
92 f,
93 "matten-data I/O error reading {}: {source}",
94 path.display()
95 )
96 }
97 MattenDataError::EmptyInput => {
98 write!(
99 f,
100 "matten-data error: input is empty (a header row is required)"
101 )
102 }
103 MattenDataError::MissingColumn { name } => {
104 write!(f, "matten-data error: column \"{name}\" does not exist")
105 }
106 MattenDataError::DuplicateColumn { name } => {
107 write!(f, "matten-data error: duplicate header column \"{name}\"")
108 }
109 MattenDataError::DuplicateSelection { name } => {
110 write!(
111 f,
112 "matten-data error: column \"{name}\" was selected more than once"
113 )
114 }
115 MattenDataError::RaggedRow {
116 row,
117 expected,
118 actual,
119 } => write!(
120 f,
121 "matten-data error: row {row} has {actual} cells but the header has {expected} columns"
122 ),
123 MattenDataError::NonNumericValue { column, row, value } => write!(
124 f,
125 "matten-data numeric conversion error: column \"{column}\", row {row} contains \"{value}\", \
126 which cannot be converted to f64. Fill or clean the column before calling try_numeric()."
127 ),
128 MattenDataError::MissingValue { column, row } => write!(
129 f,
130 "matten-data numeric conversion error: column \"{column}\", row {row} is missing. \
131 Fill missing values (e.g. with fill_missing) before calling try_numeric()."
132 ),
133 MattenDataError::EmptySelection => {
134 write!(f, "matten-data error: no columns were selected")
135 }
136 MattenDataError::InvalidBatchSize => {
137 write!(f, "matten-data error: batch_rows must be greater than zero")
138 }
139 MattenDataError::Matten(e) => write!(f, "matten-data error: {e}"),
140 }
141 }
142}
143
144impl std::error::Error for MattenDataError {
145 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
146 match self {
147 MattenDataError::Io { source, .. } => Some(source),
148 MattenDataError::Matten(e) => Some(e),
149 _ => None,
150 }
151 }
152}
153
154impl From<matten::MattenError> for MattenDataError {
155 fn from(e: matten::MattenError) -> Self {
156 MattenDataError::Matten(e)
157 }
158}