use crate::types::{BrightDateError, BrightDateValue};
const UNIX_MS_MIN: f64 = -30_610_224_000_000.0;
const UNIX_MS_MAX: f64 = 253_402_300_800_000.0;
const JD_MAX: f64 = 5_373_484.0;
const GPS_WEEK_MAX: u32 = 9_999;
const GPS_SECONDS_MAX: f64 = 604_800.0;
pub fn validate_finite_number(value: f64, param_name: &str) -> Result<(), BrightDateError> {
if !value.is_finite() {
return Err(BrightDateError::InvalidNumber(format!(
"'{param_name}' must be a finite number, got {value}"
)));
}
Ok(())
}
pub fn validate_brightdate_value(value: BrightDateValue) -> Result<(), BrightDateError> {
validate_finite_number(value, "value")
}
pub fn validate_precision(precision: f64) -> Result<(), BrightDateError> {
if precision.fract() != 0.0 || !(1.0..=12.0).contains(&precision) {
return Err(BrightDateError::InvalidPrecision(format!(
"Precision must be an integer in [1, 12], got {precision}"
)));
}
Ok(())
}
pub fn validate_unix_ms(ms: f64) -> Result<(), BrightDateError> {
if !(UNIX_MS_MIN..=UNIX_MS_MAX).contains(&ms) {
return Err(BrightDateError::OutOfRange(format!(
"Unix ms timestamp {ms} is out of range [{UNIX_MS_MIN}, {UNIX_MS_MAX}]"
)));
}
Ok(())
}
pub fn validate_julian_date(jd: f64) -> Result<(), BrightDateError> {
if !(0.0..=JD_MAX).contains(&jd) {
return Err(BrightDateError::OutOfRange(format!(
"Julian Date {jd} is out of range [0, {JD_MAX}]"
)));
}
Ok(())
}
pub fn validate_gps_week(week: f64) -> Result<(), BrightDateError> {
if week.fract() != 0.0 || week < 0.0 || week > GPS_WEEK_MAX as f64 {
return Err(BrightDateError::InvalidGpsWeek(format!(
"GPS week must be a non-negative integer <= {GPS_WEEK_MAX}, got {week}"
)));
}
Ok(())
}
pub fn validate_gps_seconds(seconds: f64) -> Result<(), BrightDateError> {
if !(0.0..GPS_SECONDS_MAX).contains(&seconds) {
return Err(BrightDateError::InvalidGpsSeconds(format!(
"GPS seconds must be in [0, {GPS_SECONDS_MAX}), got {seconds}"
)));
}
Ok(())
}
pub fn is_brightdate_value(value: f64) -> bool {
value.is_finite()
}
pub fn is_valid_brightdate_string(s: &str) -> bool {
if s.is_empty() || s.trim().is_empty() {
return false;
}
let s = s.trim();
let s = s.strip_prefix('-').unwrap_or(s);
if s.is_empty() {
return false;
}
let mut dot_seen = false;
for ch in s.chars() {
if ch == '.' {
if dot_seen {
return false;
}
dot_seen = true;
} else if !ch.is_ascii_digit() {
return false;
}
}
s.chars().any(|c| c.is_ascii_digit())
}