use crate::model::{
artifacts::ArtifactChecksumRecord,
inventory::InventoryRecord,
membership::{MembershipObservation, MembershipObservationRequest},
};
use thiserror::Error;
#[derive(Clone, Debug)]
pub struct MembershipView<'a> {
request: ArtifactChecksumRecord,
inventory: &'a InventoryRecord,
selected_targets: &'a [String],
revision: Option<&'a ArtifactChecksumRecord>,
evidence: &'a ArtifactChecksumRecord,
remote_observations: u32,
}
impl MembershipView<'_> {
#[must_use]
pub const fn request(&self) -> &ArtifactChecksumRecord {
&self.request
}
#[must_use]
pub const fn inventory(&self) -> &InventoryRecord {
self.inventory
}
#[must_use]
pub const fn selected_targets(&self) -> &[String] {
self.selected_targets
}
#[must_use]
pub const fn revision(&self) -> Option<&ArtifactChecksumRecord> {
self.revision
}
#[must_use]
pub const fn evidence(&self) -> &ArtifactChecksumRecord {
self.evidence
}
#[must_use]
pub const fn remote_observations(&self) -> u32 {
self.remote_observations
}
}
pub fn validate<'a>(
request: &'a MembershipObservationRequest<'_>,
observation: &'a MembershipObservation,
) -> Result<MembershipView<'a>, MembershipError> {
let digest = request.digest();
let binding = request.binding();
super::observed_context::admit_request_context(
&digest,
&observation.request,
binding,
&observation.context,
MembershipError::RequestMismatch,
MembershipError::ContextMismatch,
)?;
if observation.inventory != *request.inventory() {
return Err(MembershipError::InventoryMismatch);
}
super::observed_context::admit_limit(
request.max_remote_observations(),
observation.remote_observations,
|limit, reported| MembershipError::ObservationLimitExceeded { limit, reported },
)?;
Ok(MembershipView {
request: digest,
inventory: &observation.inventory,
selected_targets: request.selected_targets(),
revision: observation.revision.as_ref(),
evidence: &observation.evidence,
remote_observations: observation.remote_observations,
})
}
#[derive(Debug, Eq, Error, PartialEq)]
pub enum MembershipError {
#[error("membership request identity mismatch")]
RequestMismatch,
#[error("membership observed {0} mismatch")]
ContextMismatch(&'static str),
#[error("membership inventory mismatch")]
InventoryMismatch,
#[error("membership reports {reported} observations above ceiling {limit}")]
ObservationLimitExceeded {
limit: u32,
reported: u32,
},
}
#[cfg(test)]
mod tests;