use super::model::AirglowModel;
use crate::error::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum AirglowSelection {
Automatic,
Explicit(AirglowModel),
}
impl AirglowSelection {
pub const fn as_str(self) -> &'static str {
match self {
Self::Automatic => "automatic",
Self::Explicit(_) => "explicit",
}
}
pub const fn requested_model(self) -> Option<AirglowModel> {
match self {
Self::Automatic => None,
Self::Explicit(model) => Some(model),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum AirglowSelectionKind {
Automatic,
Explicit,
}
impl AirglowSelectionKind {
pub const fn as_str(self) -> &'static str {
match self {
Self::Automatic => "automatic",
Self::Explicit => "explicit",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum AirglowFallbackReason {
GlobalPlanningModelUnavailable,
}
impl AirglowFallbackReason {
pub const fn as_str(self) -> &'static str {
match self {
Self::GlobalPlanningModelUnavailable => "global-planning-model-unavailable",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum AirglowPhysicalZeroReason {
OutsideAstronomicalNight,
}
impl AirglowPhysicalZeroReason {
pub const fn as_str(self) -> &'static str {
match self {
Self::OutsideAstronomicalNight => "outside-astronomical-night",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum AirglowPhysicalOutcome {
Evaluated,
PhysicalZero,
}
impl AirglowPhysicalOutcome {
pub const fn as_str(self) -> &'static str {
match self {
Self::Evaluated => "evaluated",
Self::PhysicalZero => "physical-zero",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct AirglowSelectionMetadata {
pub selection_kind: AirglowSelectionKind,
pub requested_model: Option<AirglowModel>,
pub resolved_model: Option<AirglowModel>,
pub used_automatic_fallback: bool,
pub fallback_reason: Option<AirglowFallbackReason>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct AirglowEvaluationOutcome {
pub physical_outcome: AirglowPhysicalOutcome,
pub physical_zero_reason: Option<AirglowPhysicalZeroReason>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct ResolvedAirglowSelection {
pub model: AirglowModel,
pub selection_kind: AirglowSelectionKind,
pub requested_model: Option<AirglowModel>,
pub used_automatic_fallback: bool,
pub fallback_reason: Option<AirglowFallbackReason>,
}
impl ResolvedAirglowSelection {
pub(crate) fn selection_metadata(self) -> AirglowSelectionMetadata {
AirglowSelectionMetadata {
selection_kind: self.selection_kind,
requested_model: self.requested_model,
resolved_model: Some(self.model),
used_automatic_fallback: self.used_automatic_fallback,
fallback_reason: self.fallback_reason,
}
}
pub(crate) fn evaluation_outcome(
physical_outcome: AirglowPhysicalOutcome,
physical_zero_reason: Option<AirglowPhysicalZeroReason>,
) -> AirglowEvaluationOutcome {
AirglowEvaluationOutcome {
physical_outcome,
physical_zero_reason,
}
}
}
pub(crate) fn resolve_airglow_selection(
selection: AirglowSelection,
) -> Result<ResolvedAirglowSelection> {
match selection {
AirglowSelection::Automatic => Ok(ResolvedAirglowSelection {
model: AirglowModel::ParanalPalaceV1,
selection_kind: AirglowSelectionKind::Automatic,
requested_model: None,
used_automatic_fallback: true,
fallback_reason: Some(AirglowFallbackReason::GlobalPlanningModelUnavailable),
}),
AirglowSelection::Explicit(model) => match model {
AirglowModel::ParanalPalaceV1 => Ok(ResolvedAirglowSelection {
model,
selection_kind: AirglowSelectionKind::Explicit,
requested_model: Some(model),
used_automatic_fallback: false,
fallback_reason: None,
}),
},
}
}
pub(crate) fn load_continuum_for_model(
model: AirglowModel,
) -> Result<std::sync::Arc<super::calibration::AirglowContinuum>> {
match model {
AirglowModel::ParanalPalaceV1 => Ok(std::sync::Arc::new(
super::calibration::load_builtin_standard()?,
)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn automatic_selection_uses_visible_temporary_fallback() {
let resolved = resolve_airglow_selection(AirglowSelection::Automatic).unwrap();
assert_eq!(resolved.model, AirglowModel::ParanalPalaceV1);
assert!(resolved.used_automatic_fallback);
assert_eq!(
resolved.fallback_reason,
Some(AirglowFallbackReason::GlobalPlanningModelUnavailable)
);
assert_eq!(
resolved.fallback_reason.unwrap().as_str(),
"global-planning-model-unavailable"
);
assert_eq!(resolved.selection_kind, AirglowSelectionKind::Automatic);
}
#[test]
fn explicit_paranal_selection_is_not_reported_as_fallback() {
let resolved =
resolve_airglow_selection(AirglowSelection::Explicit(AirglowModel::ParanalPalaceV1))
.unwrap();
assert!(!resolved.used_automatic_fallback);
assert_eq!(resolved.fallback_reason, None);
assert_eq!(resolved.selection_kind, AirglowSelectionKind::Explicit);
assert_eq!(
resolved.requested_model,
Some(AirglowModel::ParanalPalaceV1)
);
}
#[test]
fn physical_zero_reason_identifier_is_stable() {
assert_eq!(
AirglowPhysicalZeroReason::OutsideAstronomicalNight.as_str(),
"outside-astronomical-night"
);
}
}