use core::fmt;
#[cfg(feature = "std")]
use crate::time_base::current_time;
use crate::time_base::{InaccuracyT, IntervalT, TdfT, TimeT, UtcT};
use crate::tio::Tio;
use crate::uto::Uto;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TimeUnavailable;
impl fmt::Display for TimeUnavailable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "underlying time service unavailable")
}
}
#[cfg(feature = "std")]
impl std::error::Error for TimeUnavailable {}
#[derive(Debug, Clone, Copy, Default)]
pub struct TimeService {
pub default_tdf: TdfT,
pub default_inaccuracy: InaccuracyT,
pub secure_source: bool,
}
impl TimeService {
#[cfg(feature = "std")]
pub fn universal_time(&self) -> Result<Uto, TimeUnavailable> {
let now = current_time();
if now == 0 {
return Err(TimeUnavailable);
}
Ok(Uto::from_utc(UtcT::new(
now,
self.default_inaccuracy,
self.default_tdf,
)))
}
#[cfg(feature = "std")]
pub fn secure_universal_time(&self) -> Result<Uto, TimeUnavailable> {
if !self.secure_source {
return Err(TimeUnavailable);
}
self.universal_time()
}
#[must_use]
pub fn new_universal_time(time: TimeT, inaccuracy: InaccuracyT, tdf: TdfT) -> Uto {
Uto::new(time, inaccuracy, tdf)
}
#[must_use]
pub fn uto_from_utc(utc: UtcT) -> Uto {
Uto::from_utc(utc)
}
#[must_use]
pub fn new_interval(lower: TimeT, upper: TimeT) -> Option<Tio> {
IntervalT::new(lower, upper).map(Tio::from_interval)
}
}
#[cfg(test)]
#[allow(clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn new_universal_time_creates_uto_from_components() {
let uto = TimeService::new_universal_time(1_000, 50, 60);
assert_eq!(uto.time(), 1_000);
assert_eq!(uto.inaccuracy(), 50);
assert_eq!(uto.tdf(), 60);
}
#[test]
fn uto_from_utc_wraps_passed_struct() {
let utc = UtcT::new(100, 0, 0);
let uto = TimeService::uto_from_utc(utc);
assert_eq!(uto.utc_time(), utc);
}
#[test]
fn new_interval_rejects_lower_greater_than_upper() {
assert!(TimeService::new_interval(200, 100).is_none());
}
#[test]
fn new_interval_creates_tio_for_valid_bounds() {
let tio = TimeService::new_interval(100, 200).expect("ok");
assert_eq!(tio.time_interval().lower_bound, 100);
assert_eq!(tio.time_interval().upper_bound, 200);
}
#[cfg(feature = "std")]
#[test]
fn universal_time_returns_recent_value() {
let service = TimeService::default();
let uto = service.universal_time().expect("ok");
assert!(uto.time() > 130_000_000_000_000_000);
}
#[cfg(feature = "std")]
#[test]
fn secure_universal_time_fails_when_source_not_marked_secure() {
let service = TimeService::default();
assert_eq!(service.secure_universal_time(), Err(TimeUnavailable));
}
#[cfg(feature = "std")]
#[test]
fn secure_universal_time_returns_when_source_marked_secure() {
let service = TimeService {
secure_source: true,
..TimeService::default()
};
assert!(service.secure_universal_time().is_ok());
}
#[test]
fn time_unavailable_display_describes_failure_mode() {
let s = alloc::format!("{TimeUnavailable}");
assert!(s.contains("time service unavailable"));
}
}