use crate::context::TimeContext;
use crate::error::ConversionError;
use crate::scale::conversion::{ContextScaleConvert, InfallibleScaleConvert};
use crate::scale::{Scale, TAI, TCB, TCG, TDB, TT, UT1, UTC};
use crate::sealed::Sealed;
use crate::time::Time;
#[allow(private_bounds)]
pub trait ConversionTarget<S: Scale>: Sealed {
type Output;
fn try_convert(src: Time<S>) -> Result<Self::Output, ConversionError>;
}
#[allow(private_bounds)]
pub trait InfallibleConversionTarget<S: Scale>: ConversionTarget<S> + Sealed {
fn convert(src: Time<S>) -> Self::Output;
}
#[allow(private_bounds)]
pub trait ContextConversionTarget<S: Scale>: Sealed {
type Output;
fn convert_with(src: Time<S>, ctx: &TimeContext) -> Result<Self::Output, ConversionError>;
}
impl<S1, S2> ConversionTarget<S1> for S2
where
S1: Scale + InfallibleScaleConvert<S2>,
S2: Scale,
{
type Output = Time<S2>;
#[inline]
fn try_convert(src: Time<S1>) -> Result<Self::Output, ConversionError> {
Ok(Self::convert(src))
}
}
impl<S1, S2> InfallibleConversionTarget<S1> for S2
where
S1: Scale + InfallibleScaleConvert<S2>,
S2: Scale,
{
#[inline]
fn convert(src: Time<S1>) -> Self::Output {
src.to_scale()
}
}
impl<S1, S2> ContextConversionTarget<S1> for S2
where
S1: Scale + ContextScaleConvert<S2>,
S2: Scale,
{
type Output = Time<S2>;
#[inline]
fn convert_with(src: Time<S1>, ctx: &TimeContext) -> Result<Self::Output, ConversionError> {
src.to_scale_with(ctx)
}
}
macro_rules! default_context_scale_target {
($src:ty => $dst:ty) => {
impl ConversionTarget<$src> for $dst {
type Output = Time<$dst>;
#[inline]
fn try_convert(src: Time<$src>) -> Result<Self::Output, ConversionError> {
src.to_scale_with::<$dst>(&TimeContext::new())
}
}
};
}
default_context_scale_target!(TT => UT1);
default_context_scale_target!(TAI => UT1);
default_context_scale_target!(TDB => UT1);
default_context_scale_target!(TCG => UT1);
default_context_scale_target!(TCB => UT1);
default_context_scale_target!(UTC => UT1);
default_context_scale_target!(UT1 => TT);
default_context_scale_target!(UT1 => TAI);
default_context_scale_target!(UT1 => TDB);
default_context_scale_target!(UT1 => TCG);
default_context_scale_target!(UT1 => TCB);
default_context_scale_target!(UT1 => UTC);
#[cfg(test)]
mod tests {
use crate::representation::{J2000s, Unix, GPS, JD, MJD};
use crate::scale::{TAI, TT, UT1, UTC};
use qtty::Second;
#[test]
fn scalar_targets_match_coordinate_helpers() {
let tt = crate::time::Time::<TT>::from_raw_j2000_seconds(Second::new(12_345.678)).unwrap();
assert_eq!(tt.to::<J2000s>().raw(), tt.raw_j2000_seconds());
assert_eq!(
tt.to::<JD>().raw(),
crate::encoding::j2000_seconds_to_jd(tt.raw_j2000_seconds())
);
assert_eq!(
tt.to::<MJD>().raw(),
crate::encoding::j2000_seconds_to_mjd(tt.raw_j2000_seconds())
);
}
#[test]
fn unix_and_gps_targets_use_expected_axes() {
let ctx = crate::context::TimeContext::new();
let utc =
crate::time::Time::<UTC>::from_raw_unix_seconds_with(Second::new(946_728_000.0), &ctx)
.unwrap();
let unix = utc.try_to::<Unix>().unwrap();
assert!((unix.raw() - utc.raw_unix_seconds_with(&ctx).unwrap()).abs() < Second::new(1e-12));
let tai = utc.to::<TAI>();
let gps = tai.to::<GPS>();
assert!((gps.raw() - tai.raw_gps_seconds()).abs() < Second::new(1e-12));
let gps_from_tt = crate::time::Time::<TT>::from_raw_j2000_seconds(Second::new(0.0))
.unwrap()
.to::<GPS>();
assert!(gps_from_tt.raw().is_finite());
}
#[test]
fn default_context_ut1_routes_are_reachable() {
let tt = crate::time::Time::<TT>::from_raw_j2000_seconds(Second::new(0.0)).unwrap();
let ut1 = tt.try_to::<UT1>().unwrap();
let tt_back = ut1.try_to::<TT>().unwrap();
let utc_back = ut1.try_to::<UTC>().unwrap();
assert!(tt_back.raw_j2000_seconds().is_finite());
assert!(utc_back.raw_j2000_seconds().is_finite());
}
#[test]
fn context_targets_support_ut1_sources() {
let tt = crate::time::Time::<TT>::from_raw_j2000_seconds(Second::new(0.0)).unwrap();
let ctx = crate::context::TimeContext::new();
let ut1 = tt.to_with::<UT1>(&ctx).unwrap();
let unix = ut1.to_with::<Unix>(&ctx).unwrap();
let gps = ut1.to_with::<GPS>(&ctx).unwrap();
assert!(unix.raw().is_finite());
assert!(gps.raw().is_finite());
}
}