1use super::*;
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub enum PackagedArtifactTargetThresholdState {
6 Draft,
8 ProductionReady,
10}
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14pub enum PackagedArtifactTargetThresholdStateValidationError {
15 Draft,
17}
18
19impl fmt::Display for PackagedArtifactTargetThresholdStateValidationError {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 match self {
22 Self::Draft => write!(
23 f,
24 "the packaged-artifact target-threshold state is draft; production thresholds are not yet release-ready"
25 ),
26 }
27 }
28}
29
30impl std::error::Error for PackagedArtifactTargetThresholdStateValidationError {}
31
32impl PackagedArtifactTargetThresholdState {
33 pub const fn label(self) -> &'static str {
35 match self {
36 Self::Draft => {
37 "calibrated fit envelope recorded; production thresholds not yet release-ready"
38 }
39 Self::ProductionReady => "production thresholds recorded",
40 }
41 }
42
43 pub fn summary_line(self) -> String {
45 format!("target-threshold state: {}", self)
46 }
47
48 pub const fn is_production_ready(self) -> bool {
50 matches!(self, Self::ProductionReady)
51 }
52
53 pub fn validate_production_ready(
55 self,
56 ) -> Result<(), PackagedArtifactTargetThresholdStateValidationError> {
57 if self.is_production_ready() {
58 Ok(())
59 } else {
60 Err(PackagedArtifactTargetThresholdStateValidationError::Draft)
61 }
62 }
63
64 pub fn validated_summary_line(
66 self,
67 ) -> Result<String, PackagedArtifactTargetThresholdStateValidationError> {
68 self.validate_production_ready()?;
69 Ok(self.summary_line())
70 }
71}
72
73impl fmt::Display for PackagedArtifactTargetThresholdState {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 f.write_str(self.label())
76 }
77}
78
79const PACKAGED_ARTIFACT_TARGET_THRESHOLD_STATE: PackagedArtifactTargetThresholdState =
80 PackagedArtifactTargetThresholdState::ProductionReady;
81pub(crate) const PACKAGED_ARTIFACT_TARGET_THRESHOLD_SCOPES: &[&str] = &[
82 "luminaries",
83 "major planets",
84 "pluto",
85 "lunar points",
86 "selected asteroids",
87 "custom bodies",
88];
89
90#[derive(Clone, Debug, PartialEq)]
93pub struct PackagedArtifactPhase2CorpusAlignmentSummary {
94 pub reference_snapshot_source: pleiades_jpl::ReferenceSnapshotSourceSummary,
96 pub reference_snapshot: pleiades_jpl::ReferenceSnapshotBodyClassCoverageSummary,
98 pub reference_snapshot_exact_j2000: pleiades_jpl::ReferenceSnapshotExactJ2000EvidenceSummary,
100 pub comparison_snapshot_source: pleiades_jpl::ComparisonSnapshotSourceSummary,
102 pub comparison_snapshot: pleiades_jpl::ComparisonSnapshotBodyClassCoverageSummary,
104 pub independent_holdout_source: pleiades_jpl::IndependentHoldoutSourceSummary,
106 pub independent_holdout: pleiades_jpl::IndependentHoldoutSnapshotBodyClassCoverageSummary,
108 pub selected_asteroid_source: pleiades_jpl::SelectedAsteroidSourceSummary,
110 pub selected_asteroid_source_windows: pleiades_jpl::SelectedAsteroidSourceWindowSummary,
112 pub selected_asteroid_source_request_corpus:
114 pleiades_jpl::SelectedAsteroidSourceRequestCorpusSummary,
115 pub selected_asteroid_source_request_corpus_equatorial:
117 pleiades_jpl::SelectedAsteroidSourceRequestCorpusSummary,
118 pub production_generation_boundary_source: pleiades_jpl::IndependentHoldoutSourceSummary,
120 pub production_generation_body_class_coverage:
122 pleiades_jpl::ProductionGenerationSnapshotBodyClassCoverageSummary,
123 pub production_generation_source: ProductionGenerationSourceSummary,
125}
126
127#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
129pub enum PackagedArtifactPhase2CorpusAlignmentSummaryValidationError {
130 FieldOutOfSync { field: &'static str },
132}
133
134impl fmt::Display for PackagedArtifactPhase2CorpusAlignmentSummaryValidationError {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 match self {
137 Self::FieldOutOfSync { field } => write!(
138 f,
139 "the packaged artifact phase-2 corpus alignment summary field `{field}` is out of sync with the current posture"
140 ),
141 }
142 }
143}
144
145impl std::error::Error for PackagedArtifactPhase2CorpusAlignmentSummaryValidationError {}
146
147fn phase2_corpus_alignment_validation_field_path(field: &'static str) -> &'static str {
148 match field {
149 "reference_snapshot_source" => "phase2_corpus_alignment.reference_snapshot_source",
150 "reference_snapshot" => "phase2_corpus_alignment.reference_snapshot",
151 "reference_snapshot_exact_j2000" => {
152 "phase2_corpus_alignment.reference_snapshot_exact_j2000"
153 }
154 "comparison_snapshot_source" => "phase2_corpus_alignment.comparison_snapshot_source",
155 "comparison_snapshot" => "phase2_corpus_alignment.comparison_snapshot",
156 "independent_holdout_source" => "phase2_corpus_alignment.independent_holdout_source",
157 "independent_holdout" => "phase2_corpus_alignment.independent_holdout",
158 "selected_asteroid_source" => "phase2_corpus_alignment.selected_asteroid_source",
159 "selected_asteroid_source_windows" => {
160 "phase2_corpus_alignment.selected_asteroid_source_windows"
161 }
162 "selected_asteroid_source_request_corpus" => {
163 "phase2_corpus_alignment.selected_asteroid_source_request_corpus"
164 }
165 "selected_asteroid_source_request_corpus_equatorial" => {
166 "phase2_corpus_alignment.selected_asteroid_source_request_corpus_equatorial"
167 }
168 "production_generation_boundary_source" => {
169 "phase2_corpus_alignment.production_generation_boundary_source"
170 }
171 "production_generation_body_class_coverage" => {
172 "phase2_corpus_alignment.production_generation_body_class_coverage"
173 }
174 "production_generation_source" => "phase2_corpus_alignment.production_generation_source",
175 _ => "phase2_corpus_alignment",
176 }
177}
178
179impl PackagedArtifactPhase2CorpusAlignmentSummary {
180 pub fn summary_line(&self) -> String {
182 format!(
183 "reference source={}; reference snapshot={}; reference exact J2000 evidence={}; comparison source={}; comparison snapshot={}; independent hold-out source={}; independent hold-out={}; selected asteroid source evidence={}; selected asteroid source windows={}; selected asteroid source request corpus={}; selected asteroid source request corpus equatorial={}; production generation boundary source={}; production generation body-class coverage={}; production generation source={}",
184 self.reference_snapshot_source.summary_line(),
185 self.reference_snapshot.summary_line(),
186 self.reference_snapshot_exact_j2000.summary_line(),
187 self.comparison_snapshot_source.summary_line(),
188 self.comparison_snapshot.summary_line(),
189 self.independent_holdout_source.summary_line(),
190 self.independent_holdout.summary_line(),
191 self.selected_asteroid_source.summary_line(),
192 self.selected_asteroid_source_windows.summary_line(),
193 self.selected_asteroid_source_request_corpus.summary_line(),
194 self.selected_asteroid_source_request_corpus_equatorial.summary_line(),
195 pleiades_jpl::format_production_generation_boundary_source_summary(
196 &self.production_generation_boundary_source,
197 ),
198 self.production_generation_body_class_coverage.summary_line(),
199 self.production_generation_source.summary_line(),
200 )
201 }
202
203 pub fn validate(
205 &self,
206 ) -> Result<(), PackagedArtifactPhase2CorpusAlignmentSummaryValidationError> {
207 let Some(expected) = packaged_artifact_phase2_corpus_alignment_summary_details() else {
208 return Err(
209 PackagedArtifactPhase2CorpusAlignmentSummaryValidationError::FieldOutOfSync {
210 field: "phase2_corpus_alignment",
211 },
212 );
213 };
214
215 let field_out_of_sync = |field| {
216 PackagedArtifactPhase2CorpusAlignmentSummaryValidationError::FieldOutOfSync { field }
217 };
218
219 if self.reference_snapshot_source != expected.reference_snapshot_source {
220 return Err(field_out_of_sync("reference_snapshot_source"));
221 }
222 self.reference_snapshot_source
223 .validate()
224 .map_err(|_| field_out_of_sync("reference_snapshot_source"))?;
225
226 if self.reference_snapshot != expected.reference_snapshot {
227 return Err(field_out_of_sync("reference_snapshot"));
228 }
229 self.reference_snapshot
230 .validate()
231 .map_err(|_| field_out_of_sync("reference_snapshot"))?;
232
233 if self.reference_snapshot_exact_j2000 != expected.reference_snapshot_exact_j2000 {
234 return Err(field_out_of_sync("reference_snapshot_exact_j2000"));
235 }
236 self.reference_snapshot_exact_j2000
237 .validate()
238 .map_err(|_| field_out_of_sync("reference_snapshot_exact_j2000"))?;
239
240 if self.comparison_snapshot_source != expected.comparison_snapshot_source {
241 return Err(field_out_of_sync("comparison_snapshot_source"));
242 }
243 self.comparison_snapshot_source
244 .validate()
245 .map_err(|_| field_out_of_sync("comparison_snapshot_source"))?;
246
247 if self.comparison_snapshot != expected.comparison_snapshot {
248 return Err(field_out_of_sync("comparison_snapshot"));
249 }
250 self.comparison_snapshot
251 .validate()
252 .map_err(|_| field_out_of_sync("comparison_snapshot"))?;
253
254 if self.independent_holdout_source != expected.independent_holdout_source {
255 return Err(field_out_of_sync("independent_holdout_source"));
256 }
257 self.independent_holdout_source
258 .validate()
259 .map_err(|_| field_out_of_sync("independent_holdout_source"))?;
260
261 if self.independent_holdout != expected.independent_holdout {
262 return Err(field_out_of_sync("independent_holdout"));
263 }
264 self.independent_holdout
265 .validate()
266 .map_err(|_| field_out_of_sync("independent_holdout"))?;
267
268 if self.selected_asteroid_source != expected.selected_asteroid_source {
269 return Err(field_out_of_sync("selected_asteroid_source"));
270 }
271 self.selected_asteroid_source
272 .validate()
273 .map_err(|_| field_out_of_sync("selected_asteroid_source"))?;
274
275 if self.selected_asteroid_source_windows != expected.selected_asteroid_source_windows {
276 return Err(field_out_of_sync("selected_asteroid_source_windows"));
277 }
278 self.selected_asteroid_source_windows
279 .validate()
280 .map_err(|_| field_out_of_sync("selected_asteroid_source_windows"))?;
281
282 if self.selected_asteroid_source_request_corpus
283 != expected.selected_asteroid_source_request_corpus
284 {
285 return Err(field_out_of_sync("selected_asteroid_source_request_corpus"));
286 }
287 self.selected_asteroid_source_request_corpus
288 .validate()
289 .map_err(|_| field_out_of_sync("selected_asteroid_source_request_corpus"))?;
290
291 if self.selected_asteroid_source_request_corpus_equatorial
292 != expected.selected_asteroid_source_request_corpus_equatorial
293 {
294 return Err(field_out_of_sync(
295 "selected_asteroid_source_request_corpus_equatorial",
296 ));
297 }
298 self.selected_asteroid_source_request_corpus_equatorial
299 .validate()
300 .map_err(|_| field_out_of_sync("selected_asteroid_source_request_corpus_equatorial"))?;
301
302 if self.production_generation_boundary_source
303 != expected.production_generation_boundary_source
304 {
305 return Err(field_out_of_sync("production_generation_boundary_source"));
306 }
307 self.production_generation_boundary_source
308 .validate()
309 .map_err(|_| field_out_of_sync("production_generation_boundary_source"))?;
310
311 if self.production_generation_body_class_coverage
312 != expected.production_generation_body_class_coverage
313 {
314 return Err(field_out_of_sync(
315 "production_generation_body_class_coverage",
316 ));
317 }
318 self.production_generation_body_class_coverage
319 .validate()
320 .map_err(|_| field_out_of_sync("production_generation_body_class_coverage"))?;
321
322 if self.production_generation_source != expected.production_generation_source {
323 return Err(field_out_of_sync("production_generation_source"));
324 }
325 self.production_generation_source
326 .validate()
327 .map_err(|_| field_out_of_sync("production_generation_source"))?;
328
329 Ok(())
330 }
331
332 pub fn validated_summary_line(
334 &self,
335 ) -> Result<String, PackagedArtifactPhase2CorpusAlignmentSummaryValidationError> {
336 self.validate()?;
337 Ok(self.summary_line())
338 }
339}
340
341impl fmt::Display for PackagedArtifactPhase2CorpusAlignmentSummary {
342 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343 f.write_str(&self.summary_line())
344 }
345}
346
347pub fn packaged_artifact_phase2_corpus_alignment_summary_details(
349) -> Option<PackagedArtifactPhase2CorpusAlignmentSummary> {
350 Some(PackagedArtifactPhase2CorpusAlignmentSummary {
351 reference_snapshot_source: pleiades_jpl::reference_snapshot_source_summary(),
352 reference_snapshot: pleiades_jpl::reference_snapshot_body_class_coverage_summary()?,
353 reference_snapshot_exact_j2000:
354 pleiades_jpl::reference_snapshot_exact_j2000_evidence_summary()?,
355 comparison_snapshot_source: pleiades_jpl::comparison_snapshot_source_summary(),
356 comparison_snapshot: comparison_snapshot_body_class_coverage_summary()?,
357 independent_holdout_source: pleiades_jpl::independent_holdout_source_summary(),
358 independent_holdout: independent_holdout_snapshot_body_class_coverage_summary()?,
359 selected_asteroid_source: pleiades_jpl::selected_asteroid_source_evidence_summary()?,
360 selected_asteroid_source_windows: pleiades_jpl::selected_asteroid_source_window_summary()?,
361 selected_asteroid_source_request_corpus: selected_asteroid_source_request_corpus_summary(
362 CoordinateFrame::Ecliptic,
363 )?,
364 selected_asteroid_source_request_corpus_equatorial:
365 selected_asteroid_source_request_corpus_summary(CoordinateFrame::Equatorial)?,
366 production_generation_boundary_source:
367 pleiades_jpl::production_generation_boundary_source_summary(),
368 production_generation_body_class_coverage:
369 pleiades_jpl::production_generation_snapshot_body_class_coverage_summary()?,
370 production_generation_source: production_generation_source_summary(),
371 })
372}
373
374#[derive(Clone, Debug, PartialEq)]
376pub struct PackagedArtifactTargetThresholdSummary {
377 pub profile_id: &'static str,
379 pub state: PackagedArtifactTargetThresholdState,
381 pub scopes: &'static [&'static str],
383 pub fit_envelope: PackagedArtifactFitEnvelopeSummary,
385 pub scope_envelopes: PackagedArtifactTargetThresholdScopeEnvelopesSummary,
387 pub phase2_corpus_alignment: PackagedArtifactPhase2CorpusAlignmentSummary,
389}
390
391#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
393pub enum PackagedArtifactTargetThresholdSummaryValidationError {
394 FieldOutOfSync { field: &'static str },
396}
397
398impl fmt::Display for PackagedArtifactTargetThresholdSummaryValidationError {
399 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400 match self {
401 Self::FieldOutOfSync { field } => write!(
402 f,
403 "the packaged artifact target-threshold summary field `{field}` is out of sync with the current posture"
404 ),
405 }
406 }
407}
408
409impl std::error::Error for PackagedArtifactTargetThresholdSummaryValidationError {}
410
411impl PackagedArtifactTargetThresholdSummary {
412 pub fn summary_line(&self) -> String {
414 format!(
415 "profile id={}; target thresholds: {}; scopes={}; {}; scope envelopes={}; phase 2 corpus alignment={}",
416 self.profile_id,
417 self.state,
418 self.scopes.join(", "),
419 self.fit_envelope.summary_line(),
420 join_display(&self.scope_envelopes.scope_envelopes),
421 self.phase2_corpus_alignment.summary_line(),
422 )
423 }
424
425 pub fn validate(&self) -> Result<(), PackagedArtifactTargetThresholdSummaryValidationError> {
427 if self.profile_id != ARTIFACT_PROFILE_ID {
428 return Err(
429 PackagedArtifactTargetThresholdSummaryValidationError::FieldOutOfSync {
430 field: "profile_id",
431 },
432 );
433 }
434 self.state.validate_production_ready().map_err(|_| {
435 PackagedArtifactTargetThresholdSummaryValidationError::FieldOutOfSync { field: "state" }
436 })?;
437 if self.state != PACKAGED_ARTIFACT_TARGET_THRESHOLD_STATE {
438 return Err(
439 PackagedArtifactTargetThresholdSummaryValidationError::FieldOutOfSync {
440 field: "state",
441 },
442 );
443 }
444 if self.scopes != PACKAGED_ARTIFACT_TARGET_THRESHOLD_SCOPES {
445 return Err(
446 PackagedArtifactTargetThresholdSummaryValidationError::FieldOutOfSync {
447 field: "scopes",
448 },
449 );
450 }
451
452 let expected_fit_envelope = packaged_artifact_fit_envelope_summary_details();
453 if self.fit_envelope != expected_fit_envelope {
454 return Err(
455 PackagedArtifactTargetThresholdSummaryValidationError::FieldOutOfSync {
456 field: "fit_envelope",
457 },
458 );
459 }
460 if self.state.is_production_ready() {
461 let thresholds = packaged_artifact_fit_threshold_summary_details();
462 self.fit_envelope
463 .validate_against_thresholds(&thresholds)
464 .map_err(|_| {
465 PackagedArtifactTargetThresholdSummaryValidationError::FieldOutOfSync {
466 field: "fit_envelope",
467 }
468 })?;
469 }
470 let expected_scope_envelopes =
471 packaged_artifact_target_threshold_scope_envelopes_summary_details();
472 if self.scope_envelopes != expected_scope_envelopes {
473 return Err(
474 PackagedArtifactTargetThresholdSummaryValidationError::FieldOutOfSync {
475 field: "scope_envelopes",
476 },
477 );
478 }
479 self.scope_envelopes.validate().map_err(|_| {
480 PackagedArtifactTargetThresholdSummaryValidationError::FieldOutOfSync {
481 field: "scope_envelopes",
482 }
483 })?;
484
485 let expected_phase2_corpus_alignment =
486 packaged_artifact_phase2_corpus_alignment_summary_details().ok_or(
487 PackagedArtifactTargetThresholdSummaryValidationError::FieldOutOfSync {
488 field: "phase2_corpus_alignment",
489 },
490 )?;
491 self.phase2_corpus_alignment
492 .validate()
493 .map_err(|error| match error {
494 PackagedArtifactPhase2CorpusAlignmentSummaryValidationError::FieldOutOfSync {
495 field,
496 } => PackagedArtifactTargetThresholdSummaryValidationError::FieldOutOfSync {
497 field: phase2_corpus_alignment_validation_field_path(field),
498 },
499 })?;
500 if self.phase2_corpus_alignment != expected_phase2_corpus_alignment {
501 return Err(
502 PackagedArtifactTargetThresholdSummaryValidationError::FieldOutOfSync {
503 field: "phase2_corpus_alignment",
504 },
505 );
506 }
507
508 let thresholds = packaged_artifact_fit_threshold_summary_details();
509 for scope_envelope in &self.scope_envelopes.scope_envelopes {
510 scope_envelope
511 .fit_envelope
512 .validate_against_thresholds(&thresholds)
513 .map_err(|_| {
514 PackagedArtifactTargetThresholdSummaryValidationError::FieldOutOfSync {
515 field: "scope_envelopes",
516 }
517 })?;
518 }
519
520 Ok(())
521 }
522
523 pub fn validated_summary_line(
525 &self,
526 ) -> Result<String, PackagedArtifactTargetThresholdSummaryValidationError> {
527 self.validate()?;
528 Ok(self.summary_line())
529 }
530}
531
532impl fmt::Display for PackagedArtifactTargetThresholdSummary {
533 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
534 f.write_str(&self.summary_line())
535 }
536}
537
538pub fn packaged_artifact_target_threshold_summary_details() -> PackagedArtifactTargetThresholdSummary
540{
541 let summary = PackagedArtifactTargetThresholdSummary {
542 profile_id: ARTIFACT_PROFILE_ID,
543 state: PACKAGED_ARTIFACT_TARGET_THRESHOLD_STATE,
544 scopes: PACKAGED_ARTIFACT_TARGET_THRESHOLD_SCOPES,
545 fit_envelope: packaged_artifact_fit_envelope_summary_details(),
546 scope_envelopes: packaged_artifact_target_threshold_scope_envelopes_summary_details(),
547 phase2_corpus_alignment: packaged_artifact_phase2_corpus_alignment_summary_details()
548 .expect("phase-2 corpus evidence should be available"),
549 };
550 debug_assert!(summary.validate().is_ok());
551 summary
552}
553
554#[derive(Clone, Debug, PartialEq)]
556pub struct PackagedArtifactSourceFitHoldoutSyncSummary {
557 pub fit_thresholds: PackagedArtifactFitThresholdSummary,
559 pub target_thresholds: PackagedArtifactTargetThresholdSummary,
561 pub phase2_corpus_alignment: PackagedArtifactPhase2CorpusAlignmentSummary,
563}
564
565#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
567pub enum PackagedArtifactSourceFitHoldoutSyncSummaryValidationError {
568 FieldOutOfSync { field: &'static str },
570}
571
572impl fmt::Display for PackagedArtifactSourceFitHoldoutSyncSummaryValidationError {
573 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574 match self {
575 Self::FieldOutOfSync { field } => write!(
576 f,
577 "the packaged artifact source-fit and hold-out sync summary field `{field}` is out of sync with the current posture"
578 ),
579 }
580 }
581}
582
583impl std::error::Error for PackagedArtifactSourceFitHoldoutSyncSummaryValidationError {}
584
585impl PackagedArtifactSourceFitHoldoutSyncSummary {
586 pub fn summary_line(&self) -> String {
588 format!(
589 "source-fit and hold-out sync: fit thresholds={}; target thresholds={}; phase 2 corpus alignment={}",
590 self.fit_thresholds.summary_line(),
591 self.target_thresholds.summary_line(),
592 self.phase2_corpus_alignment.summary_line(),
593 )
594 }
595
596 pub fn validate(
598 &self,
599 ) -> Result<(), PackagedArtifactSourceFitHoldoutSyncSummaryValidationError> {
600 let expected_fit_thresholds = packaged_artifact_fit_threshold_summary_details();
601 if self.fit_thresholds != expected_fit_thresholds {
602 return Err(
603 PackagedArtifactSourceFitHoldoutSyncSummaryValidationError::FieldOutOfSync {
604 field: "fit_thresholds",
605 },
606 );
607 }
608 self.fit_thresholds.validate().map_err(|_| {
609 PackagedArtifactSourceFitHoldoutSyncSummaryValidationError::FieldOutOfSync {
610 field: "fit_thresholds",
611 }
612 })?;
613
614 let expected_target_thresholds = packaged_artifact_target_threshold_summary_details();
615 if self.target_thresholds != expected_target_thresholds {
616 return Err(
617 PackagedArtifactSourceFitHoldoutSyncSummaryValidationError::FieldOutOfSync {
618 field: "target_thresholds",
619 },
620 );
621 }
622 self.target_thresholds.validate().map_err(|_| {
623 PackagedArtifactSourceFitHoldoutSyncSummaryValidationError::FieldOutOfSync {
624 field: "target_thresholds",
625 }
626 })?;
627
628 let expected_phase2_corpus_alignment =
629 packaged_artifact_phase2_corpus_alignment_summary_details().ok_or(
630 PackagedArtifactSourceFitHoldoutSyncSummaryValidationError::FieldOutOfSync {
631 field: "phase2_corpus_alignment",
632 },
633 )?;
634 self.phase2_corpus_alignment
635 .validate()
636 .map_err(|error| match error {
637 PackagedArtifactPhase2CorpusAlignmentSummaryValidationError::FieldOutOfSync {
638 field,
639 } => PackagedArtifactSourceFitHoldoutSyncSummaryValidationError::FieldOutOfSync {
640 field: phase2_corpus_alignment_validation_field_path(field),
641 },
642 })?;
643 if self.phase2_corpus_alignment != expected_phase2_corpus_alignment {
644 return Err(
645 PackagedArtifactSourceFitHoldoutSyncSummaryValidationError::FieldOutOfSync {
646 field: "phase2_corpus_alignment",
647 },
648 );
649 }
650
651 Ok(())
652 }
653
654 pub fn validated_summary_line(
656 &self,
657 ) -> Result<String, PackagedArtifactSourceFitHoldoutSyncSummaryValidationError> {
658 self.validate()?;
659 Ok(self.summary_line())
660 }
661}
662
663impl fmt::Display for PackagedArtifactSourceFitHoldoutSyncSummary {
664 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
665 f.write_str(&self.summary_line())
666 }
667}
668
669pub fn packaged_artifact_source_fit_holdout_sync_summary_details(
671) -> PackagedArtifactSourceFitHoldoutSyncSummary {
672 let summary = PackagedArtifactSourceFitHoldoutSyncSummary {
673 fit_thresholds: packaged_artifact_fit_threshold_summary_details(),
674 target_thresholds: packaged_artifact_target_threshold_summary_details(),
675 phase2_corpus_alignment: packaged_artifact_phase2_corpus_alignment_summary_details()
676 .expect("phase-2 corpus evidence should be available"),
677 };
678 debug_assert!(summary.validate().is_ok());
679 summary
680}