1use num_derive::FromPrimitive;
8
9#[derive(Debug, FromPrimitive)]
15#[allow(non_camel_case_types)]
16pub enum ReadStatCError {
17 READSTAT_OK = 0,
19 READSTAT_ERROR_OPEN = 1,
21 READSTAT_ERROR_READ = 2,
23 READSTAT_ERROR_MALLOC = 3,
25 READSTAT_ERROR_USER_ABORT = 4,
27 READSTAT_ERROR_PARSE = 5,
29 READSTAT_ERROR_UNSUPPORTED_COMPRESSION = 6,
31 READSTAT_ERROR_UNSUPPORTED_CHARSET = 7,
33 READSTAT_ERROR_COLUMN_COUNT_MISMATCH = 8,
35 READSTAT_ERROR_ROW_COUNT_MISMATCH = 9,
37 READSTAT_ERROR_ROW_WIDTH_MISMATCH = 10,
39 READSTAT_ERROR_BAD_FORMAT_STRING = 11,
41 READSTAT_ERROR_VALUE_TYPE_MISMATCH = 12,
43 READSTAT_ERROR_WRITE = 13,
45 READSTAT_ERROR_WRITER_NOT_INITIALIZED = 14,
47 READSTAT_ERROR_SEEK = 15,
49 READSTAT_ERROR_CONVERT = 16,
51 READSTAT_ERROR_CONVERT_BAD_STRING = 17,
53 READSTAT_ERROR_CONVERT_SHORT_STRING = 18,
55 READSTAT_ERROR_CONVERT_LONG_STRING = 19,
57 READSTAT_ERROR_NUMERIC_VALUE_IS_OUT_OF_RANGE = 20,
59 READSTAT_ERROR_TAGGED_VALUE_IS_OUT_OF_RANGE = 21,
61 READSTAT_ERROR_STRING_VALUE_IS_TOO_LONG = 22,
63 READSTAT_ERROR_TAGGED_VALUES_NOT_SUPPORTED = 23,
65 READSTAT_ERROR_UNSUPPORTED_FILE_FORMAT_VERSION = 24,
67 READSTAT_ERROR_NAME_BEGINS_WITH_ILLEGAL_CHARACTER = 25,
69 READSTAT_ERROR_NAME_CONTAINS_ILLEGAL_CHARACTER = 26,
71 READSTAT_ERROR_NAME_IS_RESERVED_WORD = 27,
73 READSTAT_ERROR_NAME_IS_TOO_LONG = 28,
75 READSTAT_ERROR_BAD_TIMESTAMP_STRING = 29,
77 READSTAT_ERROR_BAD_FREQUENCY_WEIGHT = 30,
79 READSTAT_ERROR_TOO_MANY_MISSING_VALUE_DEFINITIONS = 31,
81 READSTAT_ERROR_NOTE_IS_TOO_LONG = 32,
83 READSTAT_ERROR_STRING_REFS_NOT_SUPPORTED = 33,
85 READSTAT_ERROR_STRING_REF_IS_REQUIRED = 34,
87 READSTAT_ERROR_ROW_IS_TOO_WIDE_FOR_PAGE = 35,
89 READSTAT_ERROR_TOO_FEW_COLUMNS = 36,
91 READSTAT_ERROR_TOO_MANY_COLUMNS = 37,
93 READSTAT_ERROR_NAME_IS_ZERO_LENGTH = 38,
95 READSTAT_ERROR_BAD_TIMESTAMP_VALUE = 39,
97 READSTAT_ERROR_BAD_MR_STRING = 40,
99}
100
101#[non_exhaustive]
110#[derive(Debug, thiserror::Error)]
111pub enum ReadStatError {
112 #[error("ReadStat C library error: {0:?}")]
114 CLibrary(ReadStatCError),
115
116 #[error("Unknown C error code: {0}")]
118 UnknownCError(i32),
119
120 #[error("Date arithmetic overflow")]
122 DateOverflow,
123
124 #[error("Integer conversion failed: {0}")]
126 IntConversion(#[from] std::num::TryFromIntError),
127
128 #[error("{0}")]
130 Arrow(#[from] arrow::error::ArrowError),
131
132 #[cfg(feature = "parquet")]
134 #[error("{0}")]
135 Parquet(#[from] parquet::errors::ParquetError),
136
137 #[error("{0}")]
139 Io(#[from] std::io::Error),
140
141 #[cfg(not(target_arch = "wasm32"))]
143 #[error("{0}")]
144 PathAbs(#[from] path_abs::Error),
145
146 #[error("{0}")]
148 SerdeJson(#[from] serde_json::Error),
149
150 #[cfg(not(target_arch = "wasm32"))]
152 #[error("{0}")]
153 Rayon(#[from] rayon::ThreadPoolBuildError),
154
155 #[error("{0}")]
157 NulError(#[from] std::ffi::NulError),
158
159 #[error("Column(s) not found: {requested:?}\nAvailable columns: {available:?}")]
161 ColumnsNotFound {
162 requested: Vec<String>,
164 available: Vec<String>,
166 },
167
168 #[cfg(feature = "sql")]
170 #[error("{0}")]
171 DataFusion(#[from] datafusion::error::DataFusionError),
172
173 #[error("{0}")]
175 Other(String),
176}
177
178pub(crate) fn check_c_error(code: i32) -> Result<(), ReadStatError> {
181 use num_traits::FromPrimitive;
182 match FromPrimitive::from_i32(code) {
183 Some(ReadStatCError::READSTAT_OK) => Ok(()),
184 Some(e) => Err(ReadStatError::CLibrary(e)),
185 None => Err(ReadStatError::UnknownCError(code)),
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192
193 #[test]
194 fn check_c_error_ok() {
195 assert!(check_c_error(0).is_ok());
196 }
197
198 #[test]
199 fn check_c_error_known_errors() {
200 for code in 1..=40 {
201 let err = check_c_error(code).unwrap_err();
202 match err {
203 ReadStatError::CLibrary(_) => {}
204 other => panic!("Expected CLibrary error for code {code}, got {other:?}"),
205 }
206 }
207 }
208
209 #[test]
210 fn check_c_error_open() {
211 let err = check_c_error(1).unwrap_err();
212 assert!(matches!(
213 err,
214 ReadStatError::CLibrary(ReadStatCError::READSTAT_ERROR_OPEN)
215 ));
216 }
217
218 #[test]
219 fn check_c_error_parse() {
220 let err = check_c_error(5).unwrap_err();
221 assert!(matches!(
222 err,
223 ReadStatError::CLibrary(ReadStatCError::READSTAT_ERROR_PARSE)
224 ));
225 }
226
227 #[test]
228 fn check_c_error_unknown_positive() {
229 let err = check_c_error(999).unwrap_err();
230 assert!(matches!(err, ReadStatError::UnknownCError(999)));
231 }
232
233 #[test]
234 fn check_c_error_unknown_negative() {
235 let err = check_c_error(-1).unwrap_err();
236 assert!(matches!(err, ReadStatError::UnknownCError(-1)));
237 }
238
239 #[test]
240 fn error_display_messages() {
241 let err = ReadStatError::Other("test error".to_string());
242 assert_eq!(format!("{err}"), "test error");
243
244 let err = ReadStatError::DateOverflow;
245 assert_eq!(format!("{err}"), "Date arithmetic overflow");
246
247 let err = ReadStatError::UnknownCError(99);
248 assert_eq!(format!("{err}"), "Unknown C error code: 99");
249 }
250
251 #[test]
252 fn error_columns_not_found_display() {
253 let err = ReadStatError::ColumnsNotFound {
254 requested: vec!["foo".into(), "bar".into()],
255 available: vec!["a".into(), "b".into(), "c".into()],
256 };
257 let msg = format!("{err}");
258 assert!(msg.contains("foo"));
259 assert!(msg.contains("bar"));
260 assert!(msg.contains("Available columns"));
261 }
262}