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
56#[cfg(feature = "std")]
57impl std::error::Error for ConversionError {}
58
59#[cfg(feature = "std")]
65#[derive(Debug)]
66pub enum TimeDataError {
67 Io(std::io::Error),
69 Download(alloc::string::String),
71 Parse(alloc::string::String),
73 Integrity(alloc::string::String),
75}
76
77#[cfg(feature = "std")]
78impl core::fmt::Display for TimeDataError {
79 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
80 match self {
81 Self::Io(err) => write!(f, "I/O error: {err}"),
82 Self::Download(msg) => write!(f, "download error: {msg}"),
83 Self::Parse(msg) => write!(f, "parse error: {msg}"),
84 Self::Integrity(msg) => write!(f, "integrity error: {msg}"),
85 }
86 }
87}
88
89#[cfg(feature = "std")]
90impl std::error::Error for TimeDataError {
91 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
92 match self {
93 Self::Io(err) => Some(err),
94 _ => None,
95 }
96 }
97}
98
99#[cfg(feature = "std")]
100impl From<std::io::Error> for TimeDataError {
101 fn from(err: std::io::Error) -> Self {
102 Self::Io(err)
103 }
104}
105
106#[cfg(feature = "std")]
107impl From<crate::archive::time::TimeDataError> for TimeDataError {
108 fn from(err: crate::archive::time::TimeDataError) -> Self {
109 match err {
110 crate::archive::time::TimeDataError::Io(e) => Self::Io(e),
111 crate::archive::time::TimeDataError::Download(msg) => Self::Download(msg),
112 crate::archive::time::TimeDataError::Parse(msg) => Self::Parse(msg),
113 crate::archive::time::TimeDataError::Integrity(msg) => Self::Integrity(msg),
114 }
115 }
116}
117
118#[cfg(all(test, feature = "std"))]
119mod tests {
120 use super::*;
121 use alloc::string::ToString;
122 use std::error::Error;
123
124 fn io_error(msg: &str) -> std::io::Error {
125 std::io::Error::other(msg.to_string())
126 }
127
128 #[test]
129 fn display_all_variants() {
130 let cases: &[(ConversionError, &str)] = &[
131 (ConversionError::UtcHistoryUnsupported, "history"),
132 (ConversionError::InvalidLeapSecond, "leap-second"),
133 (ConversionError::OutOfRange, "range"),
134 (ConversionError::Ut1HorizonExceeded, "horizon"),
135 (ConversionError::NonFinite, "usable"),
136 (ConversionError::UtcBeforeDefinition, "1961"),
137 ];
138 for (variant, fragment) in cases {
139 let s = variant.to_string();
140 assert!(s.contains(fragment), "{variant:?}: got {s:?}");
141 }
142 }
143
144 #[test]
145 fn time_data_error_display_and_source() {
146 let io = TimeDataError::Io(io_error("disk full"));
147 assert!(io.to_string().contains("I/O error: disk full"));
148 assert!(io.source().is_some());
149
150 let download = TimeDataError::Download("network timeout".to_string());
151 assert!(download
152 .to_string()
153 .contains("download error: network timeout"));
154 assert!(download.source().is_none());
155
156 let parse = TimeDataError::Parse("bad row".to_string());
157 assert!(parse.to_string().contains("parse error: bad row"));
158 assert!(parse.source().is_none());
159
160 let integrity = TimeDataError::Integrity("checksum mismatch".to_string());
161 assert!(integrity
162 .to_string()
163 .contains("integrity error: checksum mismatch"));
164 assert!(integrity.source().is_none());
165 }
166
167 #[test]
168 fn time_data_error_from_mappings_cover_all_variants() {
169 let io_mapped: TimeDataError = io_error("io map").into();
170 assert!(matches!(io_mapped, TimeDataError::Io(_)));
171
172 let mapped_download: TimeDataError =
173 crate::archive::time::TimeDataError::Download("d".to_string()).into();
174 assert!(matches!(mapped_download, TimeDataError::Download(msg) if msg == "d"));
175
176 let mapped_parse: TimeDataError =
177 crate::archive::time::TimeDataError::Parse("p".to_string()).into();
178 assert!(matches!(mapped_parse, TimeDataError::Parse(msg) if msg == "p"));
179
180 let mapped_integrity: TimeDataError =
181 crate::archive::time::TimeDataError::Integrity("i".to_string()).into();
182 assert!(matches!(mapped_integrity, TimeDataError::Integrity(msg) if msg == "i"));
183
184 let mapped_io: TimeDataError =
185 crate::archive::time::TimeDataError::Io(io_error("x")).into();
186 assert!(matches!(mapped_io, TimeDataError::Io(_)));
187 }
188}