#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LeapSecondTable {
pub source: &'static str,
pub first_mjd: i32,
pub last_mjd: i32,
pub entries: usize,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Ut1Provenance {
pub source: &'static str,
pub first_mjd: i32,
pub last_mjd: i32,
pub first_jd_tt: f64,
pub last_jd_tt: f64,
pub entries: usize,
}
impl Ut1Provenance {
pub fn covers_jd_tt(&self, jd_tt: f64) -> bool {
jd_tt >= self.first_jd_tt && jd_tt <= self.last_jd_tt
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ValidityMode {
Strict,
#[default]
Permissive,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DegradeReason {
BeforeCoverage,
AfterCoverage,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Validated<T> {
pub value: T,
pub degraded: Option<DegradeReason>,
}
impl<T> Validated<T> {
pub fn ok(value: T) -> Self {
Self {
value,
degraded: None,
}
}
pub fn degraded(value: T, reason: DegradeReason) -> Self {
Self {
value,
degraded: Some(reason),
}
}
pub fn is_valid(&self) -> bool {
self.degraded.is_none()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CoverageError {
OutsideCoverage(DegradeReason),
}
impl core::fmt::Display for CoverageError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
CoverageError::OutsideCoverage(DegradeReason::BeforeCoverage) => {
write!(f, "instant precedes EOP/UT1 table coverage")
}
CoverageError::OutsideCoverage(DegradeReason::AfterCoverage) => {
write!(f, "instant follows EOP/UT1 table coverage")
}
}
}
}
impl std::error::Error for CoverageError {}
pub fn check_ut1_coverage(
prov: &Ut1Provenance,
jd_tt: f64,
mode: ValidityMode,
) -> Result<Option<DegradeReason>, CoverageError> {
let reason = if jd_tt < prov.first_jd_tt {
Some(DegradeReason::BeforeCoverage)
} else if jd_tt > prov.last_jd_tt {
Some(DegradeReason::AfterCoverage)
} else {
None
};
match (mode, reason) {
(ValidityMode::Strict, Some(r)) => Err(CoverageError::OutsideCoverage(r)),
(_, r) => Ok(r),
}
}