Skip to main content

pleiades_data/coverage/
generation.rs

1use super::*;
2
3/// Structured generation policy for the packaged artifact.
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub enum PackagedArtifactGenerationPolicy {
6    /// Same-body source epochs are fit with adjacent quadratic windows.
7    AdjacentSameBodyQuadraticWindows,
8}
9
10/// Validation error for the packaged-artifact generation policy.
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
12pub enum PackagedArtifactGenerationPolicyValidationError {
13    /// A policy field is out of sync with the current packaged-artifact posture.
14    FieldOutOfSync { field: &'static str },
15}
16
17impl PackagedArtifactGenerationPolicyValidationError {
18    /// Returns the compact release-facing summary for the validation error.
19    pub fn summary_line(&self) -> String {
20        match self {
21            Self::FieldOutOfSync { field } => format!(
22                "the packaged artifact generation policy field `{field}` is out of sync with the current posture"
23            ),
24        }
25    }
26}
27
28impl fmt::Display for PackagedArtifactGenerationPolicyValidationError {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        f.write_str(&self.summary_line())
31    }
32}
33
34impl std::error::Error for PackagedArtifactGenerationPolicyValidationError {}
35
36impl PackagedArtifactGenerationPolicy {
37    /// Returns the compact label used in release-facing summaries.
38    pub const fn label(self) -> &'static str {
39        match self {
40            Self::AdjacentSameBodyQuadraticWindows => "adjacent same-body quadratic windows",
41        }
42    }
43
44    /// Returns the explanatory note used in release-facing summaries.
45    pub fn note(self) -> &'static str {
46        match self {
47            Self::AdjacentSameBodyQuadraticWindows => {
48                packaged_artifact_generation_policy_note_text()
49            }
50        }
51    }
52
53    /// Returns the segment-strategy text used in release-facing summaries.
54    pub fn segment_strategy(self) -> &'static str {
55        self.note()
56    }
57
58    /// Returns the compact release-facing summary for the generation policy.
59    pub fn summary_line(self) -> String {
60        format!("{}; {}", self.label(), self.note())
61    }
62
63    /// Returns `Ok(())` when the generation policy still matches the current packaged-artifact posture.
64    pub fn validate(self) -> Result<(), PackagedArtifactGenerationPolicyValidationError> {
65        if self != Self::AdjacentSameBodyQuadraticWindows {
66            return Err(
67                PackagedArtifactGenerationPolicyValidationError::FieldOutOfSync { field: "policy" },
68            );
69        }
70
71        Ok(())
72    }
73}
74
75impl fmt::Display for PackagedArtifactGenerationPolicy {
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        f.write_str(self.label())
78    }
79}
80
81/// Structured summary for the packaged-artifact generation policy.
82#[derive(Clone, Copy, Debug, PartialEq, Eq)]
83pub struct PackagedArtifactGenerationPolicySummary {
84    /// Policy describing how the packaged artifact is generated.
85    pub policy: PackagedArtifactGenerationPolicy,
86}
87
88/// Validation error for the packaged-artifact generation policy summary.
89#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
90pub enum PackagedArtifactGenerationPolicySummaryValidationError {
91    /// A summary field is out of sync with the current packaged-artifact posture.
92    FieldOutOfSync { field: &'static str },
93}
94
95impl PackagedArtifactGenerationPolicySummaryValidationError {
96    /// Returns the compact release-facing summary for the validation error.
97    pub fn summary_line(&self) -> String {
98        match self {
99            Self::FieldOutOfSync { field } => format!(
100                "the packaged artifact generation policy summary field `{field}` is out of sync with the current posture"
101            ),
102        }
103    }
104}
105
106impl fmt::Display for PackagedArtifactGenerationPolicySummaryValidationError {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        f.write_str(&self.summary_line())
109    }
110}
111
112impl std::error::Error for PackagedArtifactGenerationPolicySummaryValidationError {}
113
114pub(crate) fn validate_packaged_artifact_generation_policy_residual_bodies(
115    policy: PackagedArtifactGenerationPolicy,
116    residual_bodies: &[CelestialBody],
117) -> Result<(), PackagedArtifactGenerationPolicySummaryValidationError> {
118    match policy {
119        PackagedArtifactGenerationPolicy::AdjacentSameBodyQuadraticWindows => {
120            let expected_residual_bodies = packaged_artifact().residual_bodies();
121            if residual_bodies != expected_residual_bodies.as_slice() {
122                return Err(
123                    PackagedArtifactGenerationPolicySummaryValidationError::FieldOutOfSync {
124                        field: "residual_bodies",
125                    },
126                );
127            }
128        }
129    }
130
131    Ok(())
132}
133
134impl PackagedArtifactGenerationPolicySummary {
135    /// Returns the packaged-artifact generation policy as a compact human-readable line.
136    pub fn summary_line(self) -> String {
137        self.policy.summary_line()
138    }
139
140    /// Returns `Ok(())` when the summary still matches the current packaged-artifact posture.
141    pub fn validate(&self) -> Result<(), PackagedArtifactGenerationPolicySummaryValidationError> {
142        self.policy.validate().map_err(|error| match error {
143            PackagedArtifactGenerationPolicyValidationError::FieldOutOfSync { field } => {
144                PackagedArtifactGenerationPolicySummaryValidationError::FieldOutOfSync { field }
145            }
146        })?;
147
148        validate_packaged_artifact_generation_policy_residual_bodies(
149            self.policy,
150            &packaged_artifact().residual_bodies(),
151        )
152    }
153}
154
155impl fmt::Display for PackagedArtifactGenerationPolicySummary {
156    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157        f.write_str(&self.summary_line())
158    }
159}
160
161const PACKAGED_ARTIFACT_GENERATION_POLICY_SUMMARY: PackagedArtifactGenerationPolicySummary =
162    PackagedArtifactGenerationPolicySummary {
163        policy: PackagedArtifactGenerationPolicy::AdjacentSameBodyQuadraticWindows,
164    };
165
166/// Returns the current packaged-artifact generation policy summary record.
167///
168/// # Examples
169///
170/// ```
171/// use pleiades_data::{
172///     packaged_artifact_generation_policy_summary_details,
173///     packaged_artifact_generation_policy_summary_for_report,
174/// };
175///
176/// let summary = packaged_artifact_generation_policy_summary_details();
177/// assert_eq!(summary.to_string(), packaged_artifact_generation_policy_summary_for_report());
178/// assert!(summary.validate().is_ok());
179/// ```
180pub fn packaged_artifact_generation_policy_summary_details(
181) -> PackagedArtifactGenerationPolicySummary {
182    let summary = PACKAGED_ARTIFACT_GENERATION_POLICY_SUMMARY;
183    debug_assert!(summary.validate().is_ok());
184    summary
185}
186
187/// Returns the current packaged-artifact generation policy summary after validating the structured posture.
188pub fn packaged_artifact_generation_policy_summary_for_report() -> String {
189    static SUMMARY: OnceLock<String> = OnceLock::new();
190    SUMMARY
191        .get_or_init(|| {
192            let summary = packaged_artifact_generation_policy_summary_details();
193            match summary.validate() {
194                Ok(()) => summary.to_string(),
195                Err(error) => format!("Packaged-artifact generation policy: unavailable ({error})"),
196            }
197        })
198        .clone()
199}
200
201/// Returns the current packaged-artifact residual-bearing body coverage summary record.
202pub fn packaged_artifact_generation_residual_bodies_summary_details(
203) -> ArtifactResidualBodyCoverageSummary {
204    let artifact = packaged_artifact();
205    let summary = artifact.residual_body_coverage_summary();
206    debug_assert!(summary.validate(artifact).is_ok());
207    summary
208}
209
210/// Returns the current packaged-artifact residual-bearing body set after validating the structured posture.
211pub fn packaged_artifact_generation_residual_bodies_summary_for_report() -> String {
212    static SUMMARY: OnceLock<String> = OnceLock::new();
213    SUMMARY
214        .get_or_init(|| {
215            let artifact = packaged_artifact();
216            let summary = packaged_artifact_generation_residual_bodies_summary_details();
217
218            match summary.validated_summary_line_with_body_count(artifact) {
219                Ok(line) => line,
220                Err(error) => format!("residual bodies: unavailable ({error})"),
221            }
222        })
223        .clone()
224}
225
226/// Returns the current packaged-artifact generation policy summary.
227pub fn packaged_artifact_generation_policy_summary() -> &'static str {
228    static SUMMARY: OnceLock<String> = OnceLock::new();
229    SUMMARY
230        .get_or_init(|| {
231            let summary = packaged_artifact_generation_policy_summary_details();
232            match summary.validate() {
233                Ok(()) => summary.to_string(),
234                Err(error) => format!("Packaged-artifact generation policy: unavailable ({error})"),
235            }
236        })
237        .as_str()
238}