use super::apparentness::ApparentnessPolicySummary;
use super::delta_t::DeltaTPolicySummary;
use super::frame::{FramePolicySummary, FrameTreatmentSummary};
use super::native_sidereal::NativeSiderealPolicySummary;
use super::observer::ObserverPolicySummary;
use super::pluto_fallback::{PlutoFallbackSummary, PlutoFallbackSummaryValidationError};
use super::request::RequestPolicySummary;
use super::time_scale::TimeScalePolicySummary;
use super::utc::UtcConveniencePolicySummary;
use super::zodiac::ZodiacPolicySummary;
use super::{
CURRENT_APPARENTNESS_POLICY_SUMMARY_TEXT, CURRENT_DELTA_T_POLICY_SUMMARY_TEXT,
CURRENT_FRAME_POLICY_SUMMARY_TEXT, CURRENT_NATIVE_SIDEREAL_POLICY_SUMMARY_TEXT,
CURRENT_OBSERVER_POLICY_SUMMARY_TEXT, CURRENT_PLUTO_FALLBACK_POLICY_SUMMARY_TEXT,
CURRENT_TIME_SCALE_POLICY_SUMMARY_TEXT, CURRENT_UNSUPPORTED_MODES_SUMMARY_TEXT,
CURRENT_UTC_CONVENIENCE_POLICY_SUMMARY_TEXT, CURRENT_ZODIAC_POLICY_SUMMARY_TEXT,
};
use crate::errors::{format_display_list, EphemerisError, EphemerisErrorKind};
use crate::metadata::BackendMetadata;
use crate::request::EphemerisRequest;
use pleiades_types::{Apparentness, CoordinateFrame, TimeScale, ZodiacMode};
pub const fn unsupported_modes_summary_for_report() -> &'static str {
CURRENT_UNSUPPORTED_MODES_SUMMARY_TEXT
}
pub const fn current_time_scale_policy_summary() -> TimeScalePolicySummary {
TimeScalePolicySummary::new(CURRENT_TIME_SCALE_POLICY_SUMMARY_TEXT)
}
pub const fn current_delta_t_policy_summary() -> DeltaTPolicySummary {
DeltaTPolicySummary::new(CURRENT_DELTA_T_POLICY_SUMMARY_TEXT)
}
pub const fn current_utc_convenience_policy_summary() -> UtcConveniencePolicySummary {
UtcConveniencePolicySummary::new(CURRENT_UTC_CONVENIENCE_POLICY_SUMMARY_TEXT)
}
pub const fn utc_convenience_policy_summary_for_report() -> UtcConveniencePolicySummary {
current_utc_convenience_policy_summary()
}
pub fn validated_utc_convenience_policy_summary_for_report() -> String {
match current_utc_convenience_policy_summary().validated_summary_line() {
Ok(summary) => summary.to_string(),
Err(error) => format!("UTC convenience policy unavailable ({error})"),
}
}
pub const fn current_observer_policy_summary() -> ObserverPolicySummary {
ObserverPolicySummary::new(CURRENT_OBSERVER_POLICY_SUMMARY_TEXT)
}
pub const fn current_apparentness_policy_summary() -> ApparentnessPolicySummary {
ApparentnessPolicySummary::new(CURRENT_APPARENTNESS_POLICY_SUMMARY_TEXT)
}
pub const fn current_request_policy_summary() -> RequestPolicySummary {
RequestPolicySummary {
time_scale: CURRENT_TIME_SCALE_POLICY_SUMMARY_TEXT,
observer: CURRENT_OBSERVER_POLICY_SUMMARY_TEXT,
apparentness: CURRENT_APPARENTNESS_POLICY_SUMMARY_TEXT,
frame: CURRENT_FRAME_POLICY_SUMMARY_TEXT,
}
}
pub const fn current_frame_policy_summary() -> FramePolicySummary {
FramePolicySummary::new(CURRENT_FRAME_POLICY_SUMMARY_TEXT)
}
pub const fn current_native_sidereal_policy_summary() -> NativeSiderealPolicySummary {
NativeSiderealPolicySummary::new(CURRENT_NATIVE_SIDEREAL_POLICY_SUMMARY_TEXT)
}
pub const fn native_sidereal_policy_summary_for_report() -> NativeSiderealPolicySummary {
current_native_sidereal_policy_summary()
}
pub fn validated_native_sidereal_policy_summary_for_report() -> String {
match current_native_sidereal_policy_summary().validated_summary_line() {
Ok(summary) => summary.to_string(),
Err(error) => format!("native sidereal policy unavailable ({error})"),
}
}
pub const fn current_zodiac_policy_summary() -> ZodiacPolicySummary {
ZodiacPolicySummary::new(CURRENT_ZODIAC_POLICY_SUMMARY_TEXT)
}
pub fn validated_zodiac_policy_summary_for_report() -> String {
match current_zodiac_policy_summary().validated_summary_line() {
Ok(summary) => summary.to_string(),
Err(error) => format!("zodiac policy unavailable ({error})"),
}
}
pub const fn current_pluto_fallback_summary() -> PlutoFallbackSummary {
PlutoFallbackSummary::new(CURRENT_PLUTO_FALLBACK_POLICY_SUMMARY_TEXT)
}
pub const fn pluto_fallback_summary_for_report() -> PlutoFallbackSummary {
current_pluto_fallback_summary()
}
pub fn validated_pluto_fallback_summary_line_for_report(
) -> Result<&'static str, PlutoFallbackSummaryValidationError> {
current_pluto_fallback_summary().validated_summary_line()
}
pub const fn request_policy_summary_for_report() -> RequestPolicySummary {
current_request_policy_summary()
}
pub fn validated_request_policy_summary_for_report() -> String {
match current_request_policy_summary().validated_summary_line() {
Ok(summary) => summary,
Err(error) => format!("request policy unavailable ({error})"),
}
}
pub fn validated_request_semantics_summary_for_report() -> String {
validated_request_policy_summary_for_report()
}
pub const fn request_semantics_summary_for_report() -> RequestPolicySummary {
request_policy_summary_for_report()
}
pub const fn observer_policy_summary_for_report() -> ObserverPolicySummary {
current_observer_policy_summary()
}
pub fn validated_observer_policy_summary_for_report() -> String {
match current_observer_policy_summary().validated_summary_line() {
Ok(summary) => summary.to_string(),
Err(error) => format!("observer policy unavailable ({error})"),
}
}
pub const fn apparentness_policy_summary_for_report() -> ApparentnessPolicySummary {
current_apparentness_policy_summary()
}
pub fn validated_apparentness_policy_summary_for_report() -> String {
match current_apparentness_policy_summary().validated_summary_line() {
Ok(summary) => summary.to_string(),
Err(error) => format!("apparentness policy unavailable ({error})"),
}
}
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 const fn time_scale_policy_summary_for_report() -> TimeScalePolicySummary {
current_time_scale_policy_summary()
}
pub fn validated_time_scale_policy_summary_for_report() -> String {
match current_time_scale_policy_summary().validated_summary_line() {
Ok(summary) => summary.to_string(),
Err(error) => format!("time-scale policy unavailable ({error})"),
}
}
pub const fn delta_t_policy_summary_for_report() -> DeltaTPolicySummary {
current_delta_t_policy_summary()
}
pub fn validated_delta_t_policy_summary_for_report() -> String {
match current_delta_t_policy_summary().validated_summary_line() {
Ok(summary) => summary.to_string(),
Err(error) => format!("Delta T policy unavailable ({error})"),
}
}
pub const fn frame_policy_summary_for_report() -> &'static str {
current_frame_policy_summary().summary_line()
}
pub fn validated_frame_policy_summary_for_report() -> String {
match current_frame_policy_summary().validated_summary_line() {
Ok(summary) => summary.to_string(),
Err(error) => format!("frame policy unavailable ({error})"),
}
}
pub const fn frame_policy_summary_details() -> FramePolicySummary {
current_frame_policy_summary()
}
pub const fn frame_treatment_summary_for_report() -> FrameTreatmentSummary {
FrameTreatmentSummary::new(current_request_policy_summary().frame)
}
pub fn validated_frame_treatment_summary_for_report() -> String {
match frame_treatment_summary_for_report().validated_summary_line() {
Ok(summary) => summary.to_string(),
Err(error) => format!("frame treatment unavailable ({error})"),
}
}
pub fn zodiac_policy_summary_for_report(supported_zodiac_modes: &[ZodiacMode]) -> String {
if supported_zodiac_modes.len() == 1 && supported_zodiac_modes[0] == ZodiacMode::Tropical {
"tropical only".to_string()
} else {
format!(
"zodiac modes=[{}]",
format_display_list(supported_zodiac_modes)
)
}
}
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(())
}