Skip to main content

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}