1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use core::fmt;
/// Unified error type for the DCT reader.
#[derive(Debug)]
#[non_exhaustive]
pub enum DctError {
/// An I/O error from the underlying reader.
Io(std::io::Error),
/// The dictionary file ended before its closing `}` was reached.
UnexpectedEofInDictionary,
/// The `dictionary [using FILE] {` opening could not be parsed —
/// the keyword `dictionary` was missing or the surrounding tokens
/// were malformed.
InvalidDictionaryHeader {
/// 1-based line number where the header was being parsed.
line: usize,
/// Accumulated tokens that could not be interpreted.
content: String,
},
/// A `_column(#)` directive could not be parsed.
InvalidColumnDirective {
/// 1-based line number within the dictionary file.
line: usize,
/// Verbatim line content for diagnostic display.
content: String,
},
/// A `%infmt` read-format token was not recognized.
InvalidReadFormat {
/// 1-based line number within the dictionary file.
line: usize,
/// The unrecognized read-format token.
format: String,
},
/// A directive that may appear at most once was encountered a
/// second time (e.g. two `lrecl(#)` declarations).
DuplicateDirective {
/// 1-based line number within the dictionary file.
line: usize,
/// The directive name.
directive: String,
},
/// A data record exceeded the maximum permitted length
/// (524,275 bytes).
RecordTooLong {
/// 1-based line number within the data file.
line: usize,
/// Actual length in bytes.
length: usize,
},
/// The data file ended in the middle of an observation.
UnexpectedEofInData {
/// 1-based observation number being read when EOF occurred.
observation: usize,
/// Number of variables successfully parsed before EOF.
variables_read: usize,
},
/// A data field could not be parsed as the declared numeric type.
InvalidNumericValue {
/// 1-based observation number containing the bad field.
observation: usize,
/// Name of the variable being parsed.
variable: String,
/// Verbatim field content.
content: String,
},
/// A column-position computation in the dictionary overflowed
/// `usize`. Indicates corrupt or malformed input — real DCT files
/// cannot reach the magnitudes that would trigger this.
DictionaryOffsetOverflow {
/// 1-based line number within the dictionary file.
line: usize,
/// Verbatim line content for diagnostic display.
content: String,
},
/// A column-position computation while reading a data record
/// overflowed `usize`. Indicates a schema whose offsets and
/// widths sum past `usize::MAX` — virtually impossible with
/// well-formed data.
RecordOffsetOverflow {
/// 1-based observation number containing the field.
observation: usize,
/// Name of the variable being parsed.
variable: String,
},
}
impl fmt::Display for DctError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(source) => write!(f, "I/O error: {source}"),
Self::UnexpectedEofInDictionary => {
f.write_str("dictionary ended before its closing '}'")
}
Self::InvalidDictionaryHeader { line, content } => {
write!(f, "invalid dictionary header on line {line}: {content}")
}
Self::InvalidColumnDirective { line, content } => {
write!(f, "invalid _column(#) directive on line {line}: {content}")
}
Self::InvalidReadFormat { line, format } => {
write!(f, "invalid read format '{format}' on line {line}")
}
Self::DuplicateDirective { line, directive } => write!(
f,
"directive '{directive}' on line {line} appeared more than once",
),
Self::RecordTooLong { line, length } => write!(
f,
"data record on line {line} is {length} bytes, exceeds the 524275-byte limit",
),
Self::UnexpectedEofInData {
observation,
variables_read,
} => write!(
f,
"data file ended mid-observation {observation} after \
reading {variables_read} variable(s)",
),
Self::InvalidNumericValue {
observation,
variable,
content,
} => write!(
f,
"could not parse '{content}' as a numeric value for variable \
'{variable}' in observation {observation}",
),
Self::DictionaryOffsetOverflow { line, content } => write!(
f,
"column-position computation overflowed on line {line}: {content}",
),
Self::RecordOffsetOverflow {
observation,
variable,
} => write!(
f,
"column-position computation overflowed for variable \
'{variable}' in observation {observation}",
),
}
}
}
impl std::error::Error for DctError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io(source) => Some(source),
_ => None,
}
}
}
impl From<std::io::Error> for DctError {
fn from(source: std::io::Error) -> Self {
Self::Io(source)
}
}
/// Convenience alias used throughout the `dct` module.
pub type Result<T> = std::result::Result<T, DctError>;