use crate::errors::{format_display_list, EphemerisError, EphemerisErrorKind};
use crate::metadata::BackendMetadata;
use crate::request::EphemerisRequest;
use pleiades_types::{Apparentness, CoordinateFrame, TimeScale, ZodiacMode};
pub fn validate_request_policy(
req: &EphemerisRequest,
backend_label: &str,
supported_time_scales: &[TimeScale],
supported_frames: &[CoordinateFrame],
supports_mean: bool,
supports_apparent: bool,
) -> Result<(), EphemerisError> {
if !supported_time_scales.contains(&req.instant.scale) {
return Err(EphemerisError::new(
EphemerisErrorKind::UnsupportedTimeScale,
format!(
"{backend_label} expects one of [{}] for request instants",
format_display_list(supported_time_scales)
),
));
}
if !supported_frames.contains(&req.frame) {
return Err(EphemerisError::new(
EphemerisErrorKind::UnsupportedCoordinateFrame,
format!(
"{backend_label} only returns [{}] coordinates",
format_display_list(supported_frames)
),
));
}
match req.apparent {
Apparentness::Mean if !supports_mean => {
return Err(EphemerisError::new(
EphemerisErrorKind::UnsupportedApparentness,
format!(
"{backend_label} currently returns apparent coordinates only; mean geometric coordinates are not implemented"
),
));
}
Apparentness::Apparent if !supports_apparent => {
return Err(EphemerisError::new(
EphemerisErrorKind::UnsupportedApparentness,
format!(
"{backend_label} currently returns mean geometric coordinates only; apparent corrections are not implemented"
),
));
}
_ => {}
}
Ok(())
}
pub(crate) fn validate_request_observer_location(
req: &EphemerisRequest,
) -> Result<(), EphemerisError> {
if let Some(observer) = &req.observer {
observer.validate().map_err(|error| {
EphemerisError::new(
EphemerisErrorKind::InvalidObserver,
format!("request received invalid observer location: {error}"),
)
})?;
}
Ok(())
}
pub fn validate_request_against_metadata(
req: &EphemerisRequest,
metadata: &BackendMetadata,
) -> Result<(), EphemerisError> {
metadata.validate_request(req)
}
pub fn validate_requests_against_metadata(
reqs: &[EphemerisRequest],
metadata: &BackendMetadata,
) -> Result<(), EphemerisError> {
if !metadata.family.is_routing() && !metadata.capabilities.batch {
return Err(EphemerisError::new(
EphemerisErrorKind::InvalidRequest,
format!("{} does not support batch requests", metadata.id),
));
}
for (index, req) in reqs.iter().enumerate() {
if let Err(error) = metadata.validate_request(req) {
return Err(EphemerisError::new(
error.kind,
format!("batch request {}: {}", index + 1, error.message),
));
}
}
Ok(())
}
pub fn validate_zodiac_policy(
req: &EphemerisRequest,
backend_label: &str,
supported_zodiac_modes: &[ZodiacMode],
) -> Result<(), EphemerisError> {
if !supported_zodiac_modes.contains(&req.zodiac_mode) {
let message = if supported_zodiac_modes.len() == 1
&& supported_zodiac_modes[0] == ZodiacMode::Tropical
{
format!("{backend_label} currently exposes tropical coordinates only")
} else {
format!(
"{backend_label} currently exposes [{}] zodiac coordinates only",
format_display_list(supported_zodiac_modes)
)
};
return Err(EphemerisError::new(
EphemerisErrorKind::UnsupportedZodiacMode,
message,
));
}
Ok(())
}
pub fn validate_observer_policy(
req: &EphemerisRequest,
backend_label: &str,
supports_topocentric: bool,
) -> Result<(), EphemerisError> {
if let Some(observer) = req.observer.as_ref() {
observer.validate().map_err(|error| {
EphemerisError::new(
EphemerisErrorKind::InvalidObserver,
format!(
"{backend_label} received invalid observer location for {}: {error}",
observer.summary_line(),
),
)
})?;
if !supports_topocentric {
return Err(EphemerisError::new(
EphemerisErrorKind::UnsupportedObserver,
format!(
"{backend_label} is geocentric only; topocentric positions are not implemented for {}",
observer.summary_line()
),
));
}
}
Ok(())
}