use super::*;
#[derive(Clone, Debug, PartialEq)]
pub struct PackagedArtifactProductionProfileSummary {
pub profile_id: &'static str,
pub label: &'static str,
pub artifact_version: u16,
pub time_range: TimeRange,
pub source_provenance: String,
pub body_coverage: PackagedBodyCoverageSummary,
pub artifact_profile: ArtifactProfile,
pub speed_policy: pleiades_compression::SpeedPolicy,
pub generation_policy: PackagedArtifactGenerationPolicy,
pub request_policy: PackagedRequestPolicySummary,
pub lookup_epoch_policy: PackagedLookupEpochPolicy,
pub frame_treatment: PackagedFrameTreatmentSummary,
pub storage_summary: PackagedArtifactStorageSummary,
pub target_thresholds: PackagedArtifactTargetThresholdSummary,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PackagedArtifactProductionProfileSummaryValidationError {
FieldOutOfSync { field: &'static str },
}
impl fmt::Display for PackagedArtifactProductionProfileSummaryValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::FieldOutOfSync { field } => write!(
f,
"the packaged artifact production profile summary field `{field}` is out of sync with the current posture"
),
}
}
}
impl std::error::Error for PackagedArtifactProductionProfileSummaryValidationError {}
impl PackagedArtifactProductionProfileSummary {
pub fn summary_line(&self) -> String {
format!(
"Packaged artifact production profile draft: profile id={}; label={}; version={}; time range={}; source provenance={}; body coverage={}; artifact profile={}; output support={}; speed policy={}; generation policy={}; segment strategy={}; request policy={}; lookup epoch policy={}; frame treatment={}; storage/reconstruction={}; {}",
self.profile_id,
self.label,
self.artifact_version,
self.time_range,
self.source_provenance,
self.body_coverage,
self.artifact_profile,
self.artifact_profile.output_support_entries_summary_line(),
self.speed_policy,
self.generation_policy,
self.generation_policy.segment_strategy(),
self.request_policy,
self.lookup_epoch_policy.summary_line(),
self.frame_treatment,
self.storage_summary,
self.target_thresholds,
)
}
pub fn validate(&self) -> Result<(), PackagedArtifactProductionProfileSummaryValidationError> {
if self.profile_id != ARTIFACT_PROFILE_ID {
return Err(
PackagedArtifactProductionProfileSummaryValidationError::FieldOutOfSync {
field: "profile_id",
},
);
}
if self.label != ARTIFACT_LABEL {
return Err(
PackagedArtifactProductionProfileSummaryValidationError::FieldOutOfSync {
field: "label",
},
);
}
if self.artifact_version != packaged_artifact().header.version {
return Err(
PackagedArtifactProductionProfileSummaryValidationError::FieldOutOfSync {
field: "artifact_version",
},
);
}
if self.time_range != artifact_time_range(packaged_artifact()) {
return Err(
PackagedArtifactProductionProfileSummaryValidationError::FieldOutOfSync {
field: "time_range",
},
);
}
if self.source_provenance != production_generation_source_summary_for_report() {
return Err(
PackagedArtifactProductionProfileSummaryValidationError::FieldOutOfSync {
field: "source_provenance",
},
);
}
self.body_coverage.validate().map_err(|_| {
PackagedArtifactProductionProfileSummaryValidationError::FieldOutOfSync {
field: "body_coverage",
}
})?;
if self.artifact_profile != packaged_artifact_profile_summary_details().profile {
return Err(
PackagedArtifactProductionProfileSummaryValidationError::FieldOutOfSync {
field: "artifact_profile",
},
);
}
if self.speed_policy != self.artifact_profile.speed_policy {
return Err(
PackagedArtifactProductionProfileSummaryValidationError::FieldOutOfSync {
field: "speed_policy",
},
);
}
self.generation_policy.validate().map_err(|_| {
PackagedArtifactProductionProfileSummaryValidationError::FieldOutOfSync {
field: "generation_policy",
}
})?;
self.request_policy.validate().map_err(|_| {
PackagedArtifactProductionProfileSummaryValidationError::FieldOutOfSync {
field: "request_policy",
}
})?;
if self.lookup_epoch_policy != packaged_lookup_epoch_policy_summary_details().policy {
return Err(
PackagedArtifactProductionProfileSummaryValidationError::FieldOutOfSync {
field: "lookup_epoch_policy",
},
);
}
self.frame_treatment.validate().map_err(|_| {
PackagedArtifactProductionProfileSummaryValidationError::FieldOutOfSync {
field: "frame_treatment",
}
})?;
self.storage_summary.validate().map_err(|_| {
PackagedArtifactProductionProfileSummaryValidationError::FieldOutOfSync {
field: "storage_summary",
}
})?;
self.target_thresholds.validate().map_err(|_| {
PackagedArtifactProductionProfileSummaryValidationError::FieldOutOfSync {
field: "target_thresholds",
}
})?;
Ok(())
}
pub fn validated_summary_line(
&self,
) -> Result<String, PackagedArtifactProductionProfileSummaryValidationError> {
self.validate()?;
Ok(self.summary_line())
}
}
impl fmt::Display for PackagedArtifactProductionProfileSummary {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.summary_line())
}
}
pub fn packaged_artifact_production_profile_summary_details(
) -> PackagedArtifactProductionProfileSummary {
let artifact = packaged_artifact();
let profile_summary = packaged_artifact_profile_summary_details();
let speed_policy = profile_summary.profile.speed_policy;
let summary = PackagedArtifactProductionProfileSummary {
profile_id: ARTIFACT_PROFILE_ID,
label: ARTIFACT_LABEL,
artifact_version: artifact.header.version,
time_range: artifact_time_range(artifact),
source_provenance: production_generation_source_summary_for_report(),
body_coverage: packaged_body_coverage_summary_details(),
artifact_profile: profile_summary.profile,
speed_policy,
generation_policy: PackagedArtifactGenerationPolicy::AdjacentSameBodyQuadraticWindows,
request_policy: packaged_request_policy_summary_details(),
lookup_epoch_policy: packaged_lookup_epoch_policy_summary_details().policy,
frame_treatment: packaged_frame_treatment_summary_details(),
storage_summary: packaged_artifact_storage_summary_details(),
target_thresholds: packaged_artifact_target_threshold_summary_details(),
};
debug_assert!(summary.validate().is_ok());
summary
}
pub fn packaged_artifact_production_profile_summary() -> &'static str {
static SUMMARY: OnceLock<String> = OnceLock::new();
SUMMARY
.get_or_init(|| {
let summary = packaged_artifact_production_profile_summary_details();
match summary.validated_summary_line() {
Ok(rendered) => rendered,
Err(error) => {
format!("Packaged artifact production profile draft: unavailable ({error})")
}
}
})
.as_str()
}
#[derive(Clone, Debug, PartialEq)]
pub struct PackagedArtifactGeneratorParameters {
pub profile_id: &'static str,
pub label: &'static str,
pub artifact_version: u16,
pub time_range: TimeRange,
pub source_provenance: String,
pub checksum: u64,
pub artifact_size_bytes: usize,
pub body_coverage: PackagedBodyCoverageSummary,
pub residual_body_coverage: ArtifactResidualBodyCoverageSummary,
pub artifact_profile: ArtifactProfile,
pub speed_policy: pleiades_compression::SpeedPolicy,
pub generation_policy: PackagedArtifactGenerationPolicy,
pub request_policy: PackagedRequestPolicySummary,
pub lookup_epoch_policy: PackagedLookupEpochPolicy,
pub frame_treatment: PackagedFrameTreatmentSummary,
pub storage_summary: PackagedArtifactStorageSummary,
pub target_thresholds: PackagedArtifactTargetThresholdSummary,
}
impl PackagedArtifactGeneratorParameters {
pub fn summary_line(&self) -> String {
format!(
"Packaged artifact generator parameters: profile id={}; label={}; version={}; time range={}; source provenance={}; checksum=0x{:016x}; artifact size={} bytes; body coverage={}; residual bodies={}; artifact profile={}; output support={}; speed policy={}; generation policy={}; segment strategy={}; request policy={}; lookup epoch policy={}; frame treatment={}; storage/reconstruction={}; {}",
self.profile_id,
self.label,
self.artifact_version,
self.time_range,
self.source_provenance,
self.checksum,
self.artifact_size_bytes,
self.body_coverage,
self.residual_body_coverage.summary_line_with_body_count(),
self.artifact_profile,
self.artifact_profile.output_support_entries_summary_line(),
self.speed_policy,
self.generation_policy,
self.generation_policy.segment_strategy(),
self.request_policy,
self.lookup_epoch_policy.summary_line(),
self.frame_treatment,
self.storage_summary,
self.target_thresholds,
)
}
pub fn validate(&self) -> Result<(), pleiades_compression::CompressionError> {
let current = packaged_artifact_production_profile_summary_details();
let artifact = packaged_artifact();
if self.profile_id != current.profile_id {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact generator parameters profile id does not match the current production profile",
));
}
if self.label != current.label {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact generator parameters label does not match the current production profile",
));
}
if self.artifact_version != current.artifact_version {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact generator parameters version does not match the current production profile",
));
}
if self.time_range != current.time_range {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact generator parameters time range does not match the current production profile",
));
}
if self.source_provenance != current.source_provenance {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact generator parameters source provenance does not match the current production profile",
));
}
if self.checksum != artifact.checksum {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact generator parameters checksum does not match the current packaged artifact",
));
}
let expected_artifact_size_bytes = packaged_artifact_encoded_bytes(artifact);
if self.artifact_size_bytes != expected_artifact_size_bytes {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
format!(
"packaged artifact generator parameters artifact size {} bytes does not match the current packaged artifact size {} bytes",
self.artifact_size_bytes,
expected_artifact_size_bytes
),
));
}
if self.body_coverage != current.body_coverage {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact generator parameters body coverage does not match the current production profile",
));
}
if self.residual_body_coverage != artifact.residual_body_coverage_summary() {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact generator parameters residual body coverage does not match the current packaged artifact",
));
}
if self.artifact_profile != current.artifact_profile {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact generator parameters artifact profile does not match the current production profile",
));
}
if self.speed_policy != current.speed_policy {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact generator parameters speed policy does not match the current production profile",
));
}
if self.generation_policy != current.generation_policy {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact generator parameters generation policy does not match the current production profile",
));
}
if self.request_policy != current.request_policy {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact generator parameters request policy does not match the current production profile",
));
}
if self.lookup_epoch_policy != current.lookup_epoch_policy {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact generator parameters lookup epoch policy does not match the current production profile",
));
}
if self.frame_treatment != current.frame_treatment {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact generator parameters frame-treatment policy does not match the current production profile",
));
}
if self.storage_summary != current.storage_summary {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact generator parameters storage summary does not match the current production profile",
));
}
self.target_thresholds
.state
.validate_production_ready()
.map_err(|_| {
pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact generator parameters target thresholds do not match the current production profile",
)
})?;
if self.target_thresholds != current.target_thresholds {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact generator parameters target thresholds do not match the current production profile",
));
}
Ok(())
}
pub fn validated_summary_line(&self) -> Result<String, pleiades_compression::CompressionError> {
self.validate()?;
Ok(self.summary_line())
}
}
impl fmt::Display for PackagedArtifactGeneratorParameters {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.summary_line())
}
}
pub(crate) fn fnv1a64(bytes: &[u8]) -> u64 {
const OFFSET_BASIS: u64 = 0xcbf29ce484222325;
const PRIME: u64 = 0x100000001b3;
let mut hash = OFFSET_BASIS;
for byte in bytes {
hash ^= u64::from(*byte);
hash = hash.wrapping_mul(PRIME);
}
hash
}
fn packaged_artifact_generation_manifest_checksum(
parameters: &PackagedArtifactGeneratorParameters,
regeneration: &PackagedArtifactRegenerationSummary,
) -> u64 {
let mut bytes = Vec::new();
bytes.extend_from_slice(parameters.summary_line().as_bytes());
bytes.push(b'\n');
bytes.extend_from_slice(regeneration.summary_line().as_bytes());
fnv1a64(&bytes)
}
#[derive(Clone, Debug, PartialEq)]
pub struct PackagedArtifactGenerationManifest {
pub parameters: PackagedArtifactGeneratorParameters,
pub regeneration: PackagedArtifactRegenerationSummary,
pub manifest_checksum: u64,
}
impl PackagedArtifactGenerationManifest {
pub fn summary_line(&self) -> String {
format!(
"Packaged artifact generation manifest: manifest checksum=0x{:016x}; {}; regeneration={}",
self.manifest_checksum, self.parameters, self.regeneration,
)
}
pub fn validate(&self) -> Result<(), pleiades_compression::CompressionError> {
self.parameters.validate()?;
self.regeneration.validate()?;
let expected_checksum =
packaged_artifact_generation_manifest_checksum(&self.parameters, &self.regeneration);
if self.manifest_checksum != expected_checksum {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
format!(
"packaged artifact generation manifest checksum 0x{:016x} does not match the current packaged-artifact manifest checksum 0x{:016x}",
self.manifest_checksum,
expected_checksum
),
));
}
Ok(())
}
pub fn validated_summary_line(&self) -> Result<String, pleiades_compression::CompressionError> {
self.validate()?;
Ok(self.summary_line())
}
}
impl fmt::Display for PackagedArtifactGenerationManifest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.summary_line())
}
}
pub fn packaged_artifact_generator_parameters_details() -> PackagedArtifactGeneratorParameters {
let summary = packaged_artifact_production_profile_summary_details();
let regeneration = packaged_artifact_regeneration_summary_details();
let parameters = PackagedArtifactGeneratorParameters {
profile_id: summary.profile_id,
label: summary.label,
artifact_version: summary.artifact_version,
time_range: summary.time_range,
source_provenance: summary.source_provenance,
checksum: regeneration.checksum,
artifact_size_bytes: regeneration.artifact_size_bytes,
body_coverage: summary.body_coverage,
residual_body_coverage: regeneration.residual_body_coverage_summary(),
artifact_profile: summary.artifact_profile,
speed_policy: summary.speed_policy,
generation_policy: summary.generation_policy,
request_policy: summary.request_policy,
lookup_epoch_policy: summary.lookup_epoch_policy,
frame_treatment: summary.frame_treatment,
storage_summary: summary.storage_summary,
target_thresholds: summary.target_thresholds,
};
debug_assert!(parameters.validate().is_ok());
parameters
}
pub fn packaged_artifact_generation_manifest_details() -> PackagedArtifactGenerationManifest {
let parameters = packaged_artifact_generator_parameters_details();
let regeneration = packaged_artifact_regeneration_summary_details();
let manifest = PackagedArtifactGenerationManifest {
manifest_checksum: packaged_artifact_generation_manifest_checksum(
¶meters,
®eneration,
),
parameters,
regeneration,
};
debug_assert!(manifest.validate().is_ok());
manifest
}
pub fn packaged_artifact_generation_manifest() -> &'static str {
static SUMMARY: OnceLock<String> = OnceLock::new();
SUMMARY
.get_or_init(|| {
let manifest = packaged_artifact_generation_manifest_details();
match manifest.validated_summary_line() {
Ok(rendered) => rendered,
Err(error) => {
format!("Packaged artifact generation manifest: unavailable ({error})")
}
}
})
.as_str()
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PackagedArtifactProfileSummary {
pub body_count: usize,
pub bodies: Vec<CelestialBody>,
pub endian_policy: EndianPolicy,
pub profile: ArtifactProfile,
}
impl PackagedArtifactProfileSummary {
pub fn profile_coverage_summary(&self) -> ArtifactProfileCoverageSummary {
ArtifactProfileCoverageSummary::new(self.profile.clone(), self.bodies.clone())
}
pub fn validate(&self) -> Result<(), pleiades_compression::CompressionError> {
let artifact = packaged_artifact();
if self.endian_policy != artifact.header.endian_policy {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact profile byte-order policy does not match the checked-in packaged artifact header",
));
}
if self.profile != artifact.profile_coverage_summary().profile {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact profile metadata does not match the checked-in packaged artifact profile",
));
}
if self.body_count != self.bodies.len() {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
"packaged artifact profile body count does not match bundled body list",
));
}
if self.bodies.is_empty() {
let coverage = self.profile_coverage_summary();
coverage.validate()?;
return Ok(());
}
if self
.bodies
.iter()
.enumerate()
.any(|(index, body)| self.bodies[..index].contains(body))
{
let coverage = self.profile_coverage_summary();
coverage.validate()?;
return Ok(());
}
if self.bodies.as_slice() != packaged_bodies() {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
format!(
"packaged artifact profile bundled body list does not match the checked-in packaged body set: expected [{}]; got [{}]",
join_display(packaged_bodies()),
join_display(&self.bodies)
),
));
}
let coverage = self.profile_coverage_summary();
coverage.validate()?;
Ok(())
}
pub fn validated_summary_line(&self) -> Result<String, pleiades_compression::CompressionError> {
self.validate()?;
Ok(self.summary_line())
}
pub fn validated_summary_line_with_bodies(
&self,
) -> Result<String, pleiades_compression::CompressionError> {
self.validate()?;
Ok(self.summary_line_with_bodies())
}
pub fn validated_summary_line_with_output_support(
&self,
) -> Result<String, pleiades_compression::CompressionError> {
self.validate()?;
Ok(format!(
"{}; output support: {}",
self.summary_line_with_bodies(),
self.profile.validated_output_support_summary_line()?
))
}
pub fn summary_line(&self) -> String {
let coverage = self.profile_coverage_summary();
format!(
"byte order: {}; {}",
self.endian_policy,
coverage.summary_line()
)
}
pub fn output_support_summary_line(&self) -> String {
self.profile.output_support_summary_line()
}
pub fn summary_line_with_bodies(&self) -> String {
let coverage = self.profile_coverage_summary();
format!(
"byte order: {}; {}",
self.endian_policy,
coverage.summary_line_with_bodies(),
)
}
pub fn summary_line_with_output_support(&self) -> String {
format!(
"{}; output support: {}",
self.summary_line_with_bodies(),
self.output_support_summary_line()
)
}
}
impl fmt::Display for PackagedArtifactProfileSummary {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.summary_line())
}
}
pub fn packaged_artifact_profile_summary_details() -> PackagedArtifactProfileSummary {
let artifact = packaged_artifact();
let coverage = artifact.profile_coverage_summary();
let summary = PackagedArtifactProfileSummary {
body_count: coverage.body_count,
bodies: coverage.bodies,
endian_policy: artifact.header.endian_policy,
profile: coverage.profile,
};
debug_assert!(summary.validate().is_ok());
summary
}
pub fn packaged_artifact_profile_coverage_summary_details() -> ArtifactProfileCoverageSummary {
packaged_artifact_profile_summary_details().profile_coverage_summary()
}
pub fn packaged_artifact_profile_summary() -> String {
render_packaged_artifact_profile_summary(&packaged_artifact_profile_summary_details(), false)
}
pub fn packaged_artifact_profile_summary_with_body_coverage() -> String {
render_packaged_artifact_profile_summary(&packaged_artifact_profile_summary_details(), true)
}
pub fn packaged_artifact_profile_summary_with_output_support() -> String {
let summary = packaged_artifact_profile_summary_details();
match summary.validated_summary_line_with_output_support() {
Ok(line) => line,
Err(error) => {
format!("Packaged artifact profile with output support: unavailable ({error})")
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PackagedArtifactOutputSupportSummary {
pub profile: ArtifactProfile,
}
fn validate_packaged_artifact_output_support_profile(
profile: &ArtifactProfile,
) -> Result<(), pleiades_compression::CompressionError> {
let expected_states = [
(
ArtifactOutput::EclipticCoordinates,
ArtifactOutputSupport::Derived,
),
(
ArtifactOutput::EquatorialCoordinates,
ArtifactOutputSupport::Derived,
),
(
ArtifactOutput::ApparentCorrections,
ArtifactOutputSupport::Unsupported,
),
(
ArtifactOutput::TopocentricCoordinates,
ArtifactOutputSupport::Unsupported,
),
(
ArtifactOutput::SiderealCoordinates,
ArtifactOutputSupport::Unsupported,
),
(ArtifactOutput::Motion, ArtifactOutputSupport::Derived),
];
for (output, expected_support) in expected_states {
let actual_support = profile.output_support(output);
if actual_support != expected_support {
return Err(pleiades_compression::CompressionError::new(
pleiades_compression::CompressionErrorKind::InvalidFormat,
format!(
"packaged artifact output support summary is out of sync with the bundled artifact profile field `output_support[{output}]`: expected {expected_support}, found {actual_support}"
),
));
}
}
Ok(())
}
impl PackagedArtifactOutputSupportSummary {
pub fn validate(&self) -> Result<(), pleiades_compression::CompressionError> {
self.profile.validate()?;
validate_packaged_artifact_output_support_profile(&self.profile)
}
pub fn validated_summary_line(&self) -> Result<String, pleiades_compression::CompressionError> {
self.validate()?;
Ok(self.summary_line())
}
pub fn summary_line(&self) -> String {
self.profile.output_support_summary_line()
}
}
impl fmt::Display for PackagedArtifactOutputSupportSummary {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.summary_line())
}
}
pub fn packaged_artifact_output_support_summary_details() -> PackagedArtifactOutputSupportSummary {
let summary = PackagedArtifactOutputSupportSummary {
profile: packaged_artifact_profile_summary_details().profile,
};
debug_assert!(summary.validate().is_ok());
summary
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PackagedArtifactSpeedPolicySummary {
pub policy: SpeedPolicy,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PackagedArtifactSpeedPolicySummaryValidationError {
FieldOutOfSync { field: &'static str },
}
impl fmt::Display for PackagedArtifactSpeedPolicySummaryValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::FieldOutOfSync { field } => write!(
f,
"the packaged artifact speed-policy summary field `{field}` is out of sync with the current posture"
),
}
}
}
impl std::error::Error for PackagedArtifactSpeedPolicySummaryValidationError {}
impl PackagedArtifactSpeedPolicySummary {
pub fn summary_line(self) -> String {
format!(
"{}; motion output support={}",
self.policy,
self.policy.motion_output_support()
)
}
pub fn validate(&self) -> Result<(), PackagedArtifactSpeedPolicySummaryValidationError> {
let current_policy = packaged_artifact_profile_summary_details()
.profile
.speed_policy;
if self.policy != current_policy {
return Err(
PackagedArtifactSpeedPolicySummaryValidationError::FieldOutOfSync {
field: "policy",
},
);
}
Ok(())
}
pub fn validated_summary_line(
&self,
) -> Result<String, PackagedArtifactSpeedPolicySummaryValidationError> {
self.validate()?;
Ok(self.summary_line())
}
}
impl fmt::Display for PackagedArtifactSpeedPolicySummary {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.summary_line())
}
}
const PACKAGED_ARTIFACT_SPEED_POLICY_SUMMARY: PackagedArtifactSpeedPolicySummary =
PackagedArtifactSpeedPolicySummary {
policy: SpeedPolicy::FittedDerivative,
};
pub fn packaged_artifact_speed_policy_summary_details() -> PackagedArtifactSpeedPolicySummary {
let summary = PACKAGED_ARTIFACT_SPEED_POLICY_SUMMARY;
debug_assert!(summary.validate().is_ok());
summary
}
pub(crate) fn render_packaged_artifact_profile_summary(
summary: &PackagedArtifactProfileSummary,
with_bodies: bool,
) -> String {
if with_bodies {
match summary.validated_summary_line_with_bodies() {
Ok(line) => line,
Err(error) => {
format!("Packaged artifact profile with bundled bodies: unavailable ({error})")
}
}
} else {
match summary.validated_summary_line() {
Ok(line) => line,
Err(error) => format!("Packaged artifact profile: unavailable ({error})"),
}
}
}