zerodds_time_service/
service.rs1use core::fmt;
20
21#[cfg(feature = "std")]
22use crate::time_base::current_time;
23use crate::time_base::{InaccuracyT, IntervalT, TdfT, TimeT, UtcT};
24use crate::tio::Tio;
25use crate::uto::Uto;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct TimeUnavailable;
30
31impl fmt::Display for TimeUnavailable {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 write!(f, "underlying time service unavailable")
34 }
35}
36
37#[cfg(feature = "std")]
38impl std::error::Error for TimeUnavailable {}
39
40#[derive(Debug, Clone, Copy, Default)]
43pub struct TimeService {
44 pub default_tdf: TdfT,
47 pub default_inaccuracy: InaccuracyT,
51 pub secure_source: bool,
55}
56
57impl TimeService {
58 #[cfg(feature = "std")]
66 pub fn universal_time(&self) -> Result<Uto, TimeUnavailable> {
67 let now = current_time();
68 if now == 0 {
69 return Err(TimeUnavailable);
70 }
71 Ok(Uto::from_utc(UtcT::new(
72 now,
73 self.default_inaccuracy,
74 self.default_tdf,
75 )))
76 }
77
78 #[cfg(feature = "std")]
85 pub fn secure_universal_time(&self) -> Result<Uto, TimeUnavailable> {
86 if !self.secure_source {
87 return Err(TimeUnavailable);
88 }
89 self.universal_time()
90 }
91
92 #[must_use]
98 pub fn new_universal_time(time: TimeT, inaccuracy: InaccuracyT, tdf: TdfT) -> Uto {
99 Uto::new(time, inaccuracy, tdf)
100 }
101
102 #[must_use]
104 pub fn uto_from_utc(utc: UtcT) -> Uto {
105 Uto::from_utc(utc)
106 }
107
108 #[must_use]
111 pub fn new_interval(lower: TimeT, upper: TimeT) -> Option<Tio> {
112 IntervalT::new(lower, upper).map(Tio::from_interval)
113 }
114}
115
116#[cfg(test)]
117#[allow(clippy::expect_used)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn new_universal_time_creates_uto_from_components() {
123 let uto = TimeService::new_universal_time(1_000, 50, 60);
125 assert_eq!(uto.time(), 1_000);
126 assert_eq!(uto.inaccuracy(), 50);
127 assert_eq!(uto.tdf(), 60);
128 }
129
130 #[test]
131 fn uto_from_utc_wraps_passed_struct() {
132 let utc = UtcT::new(100, 0, 0);
134 let uto = TimeService::uto_from_utc(utc);
135 assert_eq!(uto.utc_time(), utc);
136 }
137
138 #[test]
139 fn new_interval_rejects_lower_greater_than_upper() {
140 assert!(TimeService::new_interval(200, 100).is_none());
142 }
143
144 #[test]
145 fn new_interval_creates_tio_for_valid_bounds() {
146 let tio = TimeService::new_interval(100, 200).expect("ok");
147 assert_eq!(tio.time_interval().lower_bound, 100);
148 assert_eq!(tio.time_interval().upper_bound, 200);
149 }
150
151 #[cfg(feature = "std")]
152 #[test]
153 fn universal_time_returns_recent_value() {
154 let service = TimeService::default();
155 let uto = service.universal_time().expect("ok");
156 assert!(uto.time() > 130_000_000_000_000_000);
159 }
160
161 #[cfg(feature = "std")]
162 #[test]
163 fn secure_universal_time_fails_when_source_not_marked_secure() {
164 let service = TimeService::default();
166 assert_eq!(service.secure_universal_time(), Err(TimeUnavailable));
167 }
168
169 #[cfg(feature = "std")]
170 #[test]
171 fn secure_universal_time_returns_when_source_marked_secure() {
172 let service = TimeService {
173 secure_source: true,
174 ..TimeService::default()
175 };
176 assert!(service.secure_universal_time().is_ok());
177 }
178
179 #[test]
180 fn time_unavailable_display_describes_failure_mode() {
181 let s = alloc::format!("{TimeUnavailable}");
182 assert!(s.contains("time service unavailable"));
183 }
184}