use std::ffi::{CStr, CString};
use crate::error::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum TimeScale {
UTC = 0,
TDB = 1,
}
impl TimeScale {
#[allow(clippy::should_implement_trait)]
pub fn from_str(s: &str) -> Result<Self> {
match s.to_ascii_lowercase().as_str() {
"utc" => Ok(Self::UTC),
"tdb" => Ok(Self::TDB),
other => Err(Error::invalid_input(format!(
"unknown time scale: {other:?} (expected \"utc\" or \"tdb\")"
))),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Epoch {
mjd: f64,
scale: TimeScale,
}
impl Epoch {
pub fn new(mjd: f64, scale: TimeScale) -> Self {
Self { mjd, scale }
}
pub fn from_mjd_tdb(mjd: f64) -> Self {
Self {
mjd,
scale: TimeScale::TDB,
}
}
pub fn from_mjd_utc(mjd: f64) -> Self {
Self {
mjd,
scale: TimeScale::UTC,
}
}
pub fn from_iso_utc(iso: &str) -> Result<Self> {
Ok(Self::from_mjd_utc(iso_to_mjd(iso, TimeScale::UTC)?))
}
pub fn mjd(&self) -> f64 {
self.mjd
}
pub fn scale(&self) -> TimeScale {
self.scale
}
pub fn mjd_tdb(&self) -> Result<f64> {
match self.scale {
TimeScale::TDB => Ok(self.mjd),
TimeScale::UTC => {
let iso = mjd_to_iso(self.mjd, TimeScale::UTC)?;
iso_to_mjd(&iso, TimeScale::TDB)
}
}
}
pub fn mjd_utc(&self) -> Result<f64> {
match self.scale {
TimeScale::UTC => Ok(self.mjd),
TimeScale::TDB => {
let iso = mjd_to_iso(self.mjd, TimeScale::TDB)?;
iso_to_mjd(&iso, TimeScale::UTC)
}
}
}
}
pub fn iso_to_mjd(iso: &str, scale: TimeScale) -> Result<f64> {
let c_iso = CString::new(iso)
.map_err(|_| Error::invalid_input("ISO string contains an interior NUL byte"))?;
let mut out: f64 = 0.0;
let code = unsafe {
empyrean_sys::empyrean_iso_to_mjd(c_iso.as_ptr(), scale as i32, &mut out as *mut f64)
};
if code == 0 {
Ok(out)
} else {
Err(Error::capture(code))
}
}
pub fn mjd_to_iso(mjd: f64, scale: TimeScale) -> Result<String> {
let mut buf = vec![0u8; 64];
let code = unsafe {
empyrean_sys::empyrean_mjd_to_iso(
mjd,
scale as i32,
buf.as_mut_ptr() as *mut std::os::raw::c_char,
buf.len(),
)
};
if code != 0 {
return Err(Error::capture(code));
}
let cstr = unsafe { CStr::from_ptr(buf.as_ptr() as *const std::os::raw::c_char) };
Ok(cstr.to_string_lossy().into_owned())
}