tempoch_core/foundation/
error.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum ConversionError {
10 UtcHistoryUnsupported,
12 InvalidLeapSecond,
15 OutOfRange,
17 Ut1HorizonExceeded,
20 NonFinite,
22 UtcBeforeDefinition,
30}
31
32impl core::fmt::Display for ConversionError {
33 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
34 match self {
35 Self::UtcHistoryUnsupported => {
36 f.write_str("UTC history is unavailable for the requested date")
37 }
38 Self::InvalidLeapSecond => {
39 f.write_str("leap-second label is not present in the compiled UTC history")
40 }
41 Self::OutOfRange => f.write_str("converted value is out of representable range"),
42 Self::Ut1HorizonExceeded => {
43 f.write_str("UT1 conversion exceeds the ΔT model or data horizon")
44 }
45 Self::NonFinite => f.write_str(
46 "time coordinate is not usable for this operation (NaN or unsupported non-finite value)",
47 ),
48 Self::UtcBeforeDefinition => f.write_str(
49 "date precedes 1961-01-01, before which UTC was not defined; \
50 use TimeContext::allow_pre_definition_utc() to permit extrapolation",
51 ),
52 }
53 }
54}
55
56impl std::error::Error for ConversionError {}
57
58#[derive(Debug)]
64pub enum TimeDataError {
65 Io(std::io::Error),
67 Download(String),
69 Parse(String),
71 Integrity(String),
73}
74
75impl core::fmt::Display for TimeDataError {
76 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
77 match self {
78 Self::Io(err) => write!(f, "I/O error: {err}"),
79 Self::Download(msg) => write!(f, "download error: {msg}"),
80 Self::Parse(msg) => write!(f, "parse error: {msg}"),
81 Self::Integrity(msg) => write!(f, "integrity error: {msg}"),
82 }
83 }
84}
85
86impl std::error::Error for TimeDataError {
87 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
88 match self {
89 Self::Io(err) => Some(err),
90 _ => None,
91 }
92 }
93}
94
95impl From<std::io::Error> for TimeDataError {
96 fn from(err: std::io::Error) -> Self {
97 Self::Io(err)
98 }
99}
100
101impl From<crate::archive::time::TimeDataError> for TimeDataError {
102 fn from(err: crate::archive::time::TimeDataError) -> Self {
103 match err {
104 crate::archive::time::TimeDataError::Io(e) => Self::Io(e),
105 crate::archive::time::TimeDataError::Download(msg) => Self::Download(msg),
106 crate::archive::time::TimeDataError::Parse(msg) => Self::Parse(msg),
107 crate::archive::time::TimeDataError::Integrity(msg) => Self::Integrity(msg),
108 }
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115 use std::error::Error;
116
117 fn io_error(msg: &str) -> std::io::Error {
118 std::io::Error::other(msg.to_string())
119 }
120
121 #[test]
122 fn display_all_variants() {
123 let cases: &[(ConversionError, &str)] = &[
124 (ConversionError::UtcHistoryUnsupported, "history"),
125 (ConversionError::InvalidLeapSecond, "leap-second"),
126 (ConversionError::OutOfRange, "range"),
127 (ConversionError::Ut1HorizonExceeded, "horizon"),
128 (ConversionError::NonFinite, "usable"),
129 (ConversionError::UtcBeforeDefinition, "1961"),
130 ];
131 for (variant, fragment) in cases {
132 let s = variant.to_string();
133 assert!(s.contains(fragment), "{variant:?}: got {s:?}");
134 }
135 }
136
137 #[test]
138 fn time_data_error_display_and_source() {
139 let io = TimeDataError::Io(io_error("disk full"));
140 assert!(io.to_string().contains("I/O error: disk full"));
141 assert!(io.source().is_some());
142
143 let download = TimeDataError::Download("network timeout".to_string());
144 assert!(download
145 .to_string()
146 .contains("download error: network timeout"));
147 assert!(download.source().is_none());
148
149 let parse = TimeDataError::Parse("bad row".to_string());
150 assert!(parse.to_string().contains("parse error: bad row"));
151 assert!(parse.source().is_none());
152
153 let integrity = TimeDataError::Integrity("checksum mismatch".to_string());
154 assert!(integrity
155 .to_string()
156 .contains("integrity error: checksum mismatch"));
157 assert!(integrity.source().is_none());
158 }
159
160 #[test]
161 fn time_data_error_from_mappings_cover_all_variants() {
162 let io_mapped: TimeDataError = io_error("io map").into();
163 assert!(matches!(io_mapped, TimeDataError::Io(_)));
164
165 let mapped_download: TimeDataError =
166 crate::archive::time::TimeDataError::Download("d".to_string()).into();
167 assert!(matches!(mapped_download, TimeDataError::Download(msg) if msg == "d"));
168
169 let mapped_parse: TimeDataError =
170 crate::archive::time::TimeDataError::Parse("p".to_string()).into();
171 assert!(matches!(mapped_parse, TimeDataError::Parse(msg) if msg == "p"));
172
173 let mapped_integrity: TimeDataError =
174 crate::archive::time::TimeDataError::Integrity("i".to_string()).into();
175 assert!(matches!(mapped_integrity, TimeDataError::Integrity(msg) if msg == "i"));
176
177 let mapped_io: TimeDataError =
178 crate::archive::time::TimeDataError::Io(io_error("x")).into();
179 assert!(matches!(mapped_io, TimeDataError::Io(_)));
180 }
181}