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#[derive(Debug, thiserror::Error)]
106pub enum ReadStatError {
107 #[error("ReadStat C library error: {0:?}")]
109 CLibrary(ReadStatCError),
110
111 #[error("Unknown C error code: {0}")]
113 UnknownCError(i32),
114
115 #[error("Date arithmetic overflow")]
117 DateOverflow,
118
119 #[error("Integer conversion failed: {0}")]
121 IntConversion(#[from] std::num::TryFromIntError),
122
123 #[error("{0}")]
125 Arrow(#[from] arrow::error::ArrowError),
126
127 #[cfg(feature = "parquet")]
129 #[error("{0}")]
130 Parquet(#[from] parquet::errors::ParquetError),
131
132 #[error("{0}")]
134 Io(#[from] std::io::Error),
135
136 #[cfg(not(target_arch = "wasm32"))]
138 #[error("{0}")]
139 PathAbs(#[from] path_abs::Error),
140
141 #[error("{0}")]
143 SerdeJson(#[from] serde_json::Error),
144
145 #[cfg(not(target_arch = "wasm32"))]
147 #[error("{0}")]
148 Rayon(#[from] rayon::ThreadPoolBuildError),
149
150 #[error("{0}")]
152 NulError(#[from] std::ffi::NulError),
153
154 #[error("Column(s) not found: {requested:?}\nAvailable columns: {available:?}")]
156 ColumnsNotFound {
157 requested: Vec<String>,
159 available: Vec<String>,
161 },
162
163 #[cfg(feature = "sql")]
165 #[error("{0}")]
166 DataFusion(#[from] datafusion::error::DataFusionError),
167
168 #[error("{0}")]
170 Other(String),
171}
172
173pub(crate) fn check_c_error(code: i32) -> Result<(), ReadStatError> {
176 use num_traits::FromPrimitive;
177 match FromPrimitive::from_i32(code) {
178 Some(ReadStatCError::READSTAT_OK) => Ok(()),
179 Some(e) => Err(ReadStatError::CLibrary(e)),
180 None => Err(ReadStatError::UnknownCError(code)),
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187
188 #[test]
189 fn check_c_error_ok() {
190 assert!(check_c_error(0).is_ok());
191 }
192
193 #[test]
194 fn check_c_error_known_errors() {
195 for code in 1..=40 {
196 let err = check_c_error(code).unwrap_err();
197 match err {
198 ReadStatError::CLibrary(_) => {}
199 other => panic!("Expected CLibrary error for code {code}, got {other:?}"),
200 }
201 }
202 }
203
204 #[test]
205 fn check_c_error_open() {
206 let err = check_c_error(1).unwrap_err();
207 assert!(matches!(
208 err,
209 ReadStatError::CLibrary(ReadStatCError::READSTAT_ERROR_OPEN)
210 ));
211 }
212
213 #[test]
214 fn check_c_error_parse() {
215 let err = check_c_error(5).unwrap_err();
216 assert!(matches!(
217 err,
218 ReadStatError::CLibrary(ReadStatCError::READSTAT_ERROR_PARSE)
219 ));
220 }
221
222 #[test]
223 fn check_c_error_unknown_positive() {
224 let err = check_c_error(999).unwrap_err();
225 assert!(matches!(err, ReadStatError::UnknownCError(999)));
226 }
227
228 #[test]
229 fn check_c_error_unknown_negative() {
230 let err = check_c_error(-1).unwrap_err();
231 assert!(matches!(err, ReadStatError::UnknownCError(-1)));
232 }
233
234 #[test]
235 fn error_display_messages() {
236 let err = ReadStatError::Other("test error".to_string());
237 assert_eq!(format!("{err}"), "test error");
238
239 let err = ReadStatError::DateOverflow;
240 assert_eq!(format!("{err}"), "Date arithmetic overflow");
241
242 let err = ReadStatError::UnknownCError(99);
243 assert_eq!(format!("{err}"), "Unknown C error code: 99");
244 }
245
246 #[test]
247 fn error_columns_not_found_display() {
248 let err = ReadStatError::ColumnsNotFound {
249 requested: vec!["foo".into(), "bar".into()],
250 available: vec!["a".into(), "b".into(), "c".into()],
251 };
252 let msg = format!("{err}");
253 assert!(msg.contains("foo"));
254 assert!(msg.contains("bar"));
255 assert!(msg.contains("Available columns"));
256 }
257}