use crate::descriptor::{ClaimRef, MutationPointRef, namespaced_reference};
use crate::muterprater::OperatorFamilyRef;
use crate::muterprater::discovery::encode;
use crate::muterprater::discovery::types::{
ActivationSite, ActiveSelection, AdmittedAlternative, AlternativeDeclaration, AlternativeId,
DiscoveredMutationSite, DiscoveryDisposition, DiscoveryEntry, DiscoveryRefusal,
EVALUATION_SURFACE_TAG, EvaluationDirective, EvaluationFamilyRef, EvaluationSurface,
EvaluationSurfaceId, MUTATION_ALTERNATIVE_TAG, MUTATION_DISCOVERY_TAG, MutationDiscoveryId,
MutationDiscoveryReading, MutationPoint, MutationPolicy, MutationPolicyId,
MutationSurfaceLowering, OwnerClaimMapping, PointCatalogPosture, PolicyMembership,
ResolvedMutation, SelectionRefusal,
};
namespaced_reference!(EvaluationFamilyRef, ActivationSite);
impl PolicyMembership {
#[must_use]
pub const fn policy(self) -> MutationPolicyId {
self.policy
}
#[must_use]
pub const fn owner_claim(self) -> ClaimRef {
self.owner_claim
}
}
impl AlternativeDeclaration {
#[must_use]
pub fn stated(family: OperatorFamilyRef, operation: Vec<u8>) -> Self {
Self { family, operation }
}
#[must_use]
pub const fn family(&self) -> OperatorFamilyRef {
self.family
}
#[must_use]
pub fn operation(&self) -> &[u8] {
&self.operation
}
}
impl DiscoveredMutationSite {
pub fn discovered(
identity: MutationPointRef,
mapping: OwnerClaimMapping,
original_operation: Vec<u8>,
alternatives: Vec<AlternativeDeclaration>,
activation_site: ActivationSite,
) -> Result<Self, DiscoveryRefusal> {
if original_operation.is_empty() {
return Err(DiscoveryRefusal::EmptyOriginalOperation);
}
if alternatives.is_empty() {
return Err(DiscoveryRefusal::NoAlternative);
}
for (at, alternative) in alternatives.iter().enumerate() {
if alternative.operation().is_empty() {
return Err(DiscoveryRefusal::EmptyAlternative { at });
}
if alternative.operation() == original_operation {
return Err(DiscoveryRefusal::AlternativeIsOriginal { at });
}
if alternatives.iter().take(at).any(|earlier| {
earlier.family() == alternative.family()
&& earlier.operation() == alternative.operation()
}) {
return Err(DiscoveryRefusal::DuplicateAlternativeMeaning { at });
}
}
Ok(Self {
identity,
mapping,
original_operation,
alternatives,
activation_site,
})
}
#[must_use]
pub const fn identity(&self) -> MutationPointRef {
self.identity
}
#[must_use]
pub const fn mapping(&self) -> OwnerClaimMapping {
self.mapping
}
#[must_use]
pub fn original_operation(&self) -> &[u8] {
&self.original_operation
}
#[must_use]
pub fn alternatives(&self) -> &[AlternativeDeclaration] {
&self.alternatives
}
#[must_use]
pub const fn activation_site(&self) -> ActivationSite {
self.activation_site
}
}
impl DiscoveryEntry {
pub(in crate::muterprater) fn recorded(
site: DiscoveredMutationSite,
disposition: DiscoveryDisposition,
) -> Self {
Self { site, disposition }
}
#[must_use]
pub const fn site(&self) -> &DiscoveredMutationSite {
&self.site
}
#[must_use]
pub const fn disposition(&self) -> DiscoveryDisposition {
self.disposition
}
}
crate::identity::content_address_reference! {
value MutationDiscoveryId;
}
impl MutationDiscoveryReading {
pub(in crate::muterprater) fn recorded(
policy: &MutationPolicy,
entries: Vec<DiscoveryEntry>,
) -> Self {
let preimage = encode::discovery_preimage(policy.family(), policy.identity(), &entries);
Self {
family: policy.family(),
policy: policy.identity(),
identity: MutationDiscoveryId(encode::address(MUTATION_DISCOVERY_TAG, &preimage)),
entries,
}
}
#[must_use]
pub const fn family(&self) -> EvaluationFamilyRef {
self.family
}
#[must_use]
pub const fn policy(&self) -> MutationPolicyId {
self.policy
}
#[must_use]
pub const fn identity(&self) -> MutationDiscoveryId {
self.identity
}
#[must_use]
pub fn entries(&self) -> &[DiscoveryEntry] {
&self.entries
}
}
impl MutationSurfaceLowering {
pub(in crate::muterprater) fn lowered(
discovery: MutationDiscoveryReading,
surface: EvaluationSurface,
) -> Self {
Self { discovery, surface }
}
#[must_use]
pub const fn discovery(&self) -> &MutationDiscoveryReading {
&self.discovery
}
#[must_use]
pub const fn surface(&self) -> &EvaluationSurface {
&self.surface
}
#[must_use]
pub fn into_parts(self) -> (MutationDiscoveryReading, EvaluationSurface) {
(self.discovery, self.surface)
}
}
crate::identity::content_address_reference! {
value AlternativeId;
}
impl AdmittedAlternative {
#[must_use]
pub const fn identity(&self) -> AlternativeId {
self.identity
}
#[must_use]
pub const fn family(&self) -> OperatorFamilyRef {
self.family
}
#[must_use]
pub fn operation(&self) -> &[u8] {
&self.operation
}
}
impl MutationPoint {
pub(in crate::muterprater) fn admitted(
policy: &MutationPolicy,
owner_claim: ClaimRef,
discovered: DiscoveredMutationSite,
) -> Self {
let identity = discovered.identity;
let mut admitted = Vec::new();
for alternative in discovered.alternatives {
let preimage = encode::alternative_preimage(
identity,
alternative.family(),
alternative.operation(),
);
admitted.push(AdmittedAlternative {
identity: AlternativeId(encode::address(MUTATION_ALTERNATIVE_TAG, &preimage)),
family: alternative.family(),
operation: alternative.operation,
});
}
admitted.sort_by_key(AdmittedAlternative::identity);
Self {
identity,
membership: PolicyMembership {
policy: policy.identity(),
owner_claim,
},
original_operation: discovered.original_operation,
admitted_alternatives: admitted,
activation_site: discovered.activation_site,
}
}
#[must_use]
pub const fn identity(&self) -> MutationPointRef {
self.identity
}
#[must_use]
pub const fn membership(&self) -> PolicyMembership {
self.membership
}
#[must_use]
pub const fn owner_claim(&self) -> ClaimRef {
self.membership.owner_claim()
}
#[must_use]
pub fn original_operation(&self) -> &[u8] {
&self.original_operation
}
#[must_use]
pub fn admitted_alternatives(&self) -> &[AdmittedAlternative] {
&self.admitted_alternatives
}
#[must_use]
pub const fn activation_site(&self) -> ActivationSite {
self.activation_site
}
}
crate::identity::content_address_reference! {
value EvaluationSurfaceId;
}
impl EvaluationSurface {
pub(in crate::muterprater) fn admitted(
policy: &MutationPolicy,
mut points: Vec<MutationPoint>,
) -> Self {
points.sort_by_key(MutationPoint::identity);
let preimage = encode::surface_preimage(policy.family(), policy.identity(), &points);
let identity = EvaluationSurfaceId(encode::address(EVALUATION_SURFACE_TAG, &preimage));
Self {
family: policy.family(),
policy: policy.identity(),
identity,
points,
}
}
#[must_use]
pub const fn family(&self) -> EvaluationFamilyRef {
self.family
}
#[must_use]
pub const fn policy(&self) -> MutationPolicyId {
self.policy
}
#[must_use]
pub const fn identity(&self) -> EvaluationSurfaceId {
self.identity
}
#[must_use]
pub const fn catalog_posture(&self) -> PointCatalogPosture {
if self.points.is_empty() {
PointCatalogPosture::NoAdmittedPoints
} else {
PointCatalogPosture::Mutable
}
}
#[must_use]
pub fn points(&self) -> &[MutationPoint] {
&self.points
}
#[must_use]
pub fn point(&self, identity: MutationPointRef) -> Option<&MutationPoint> {
self.points
.iter()
.find(|point| point.identity() == identity)
}
pub(in crate::muterprater) fn selected_alternative(
&self,
selection: ActiveSelection,
) -> Result<(&MutationPoint, &AdmittedAlternative), SelectionRefusal> {
if selection.surface() != self.identity() {
return Err(SelectionRefusal::SelectionFromAnotherSurface {
expected: self.identity(),
found: selection.surface(),
});
}
let Some(point) = self.point(selection.point()) else {
return Err(SelectionRefusal::NoSuchPoint(selection.point()));
};
let Some(alternative) = point
.admitted_alternatives()
.iter()
.find(|alternative| alternative.identity() == selection.alternative())
else {
return Err(SelectionRefusal::NoSuchAlternative {
point: selection.point(),
alternative: selection.alternative(),
});
};
Ok((point, alternative))
}
pub fn select(
&self,
point: MutationPointRef,
alternative: AlternativeId,
) -> Result<ActiveSelection, SelectionRefusal> {
let Some(found) = self.point(point) else {
return Err(SelectionRefusal::NoSuchPoint(point));
};
if !found
.admitted_alternatives()
.iter()
.any(|admitted| admitted.identity() == alternative)
{
return Err(SelectionRefusal::NoSuchAlternative { point, alternative });
}
Ok(ActiveSelection {
surface: self.identity,
point,
alternative,
})
}
#[must_use]
pub fn selections(&self) -> Vec<ActiveSelection> {
self.points
.iter()
.flat_map(|point| {
point
.admitted_alternatives()
.iter()
.map(|alternative| ActiveSelection {
surface: self.identity,
point: point.identity(),
alternative: alternative.identity(),
})
})
.collect()
}
}
impl ActiveSelection {
#[must_use]
pub const fn surface(self) -> EvaluationSurfaceId {
self.surface
}
#[must_use]
pub const fn point(self) -> MutationPointRef {
self.point
}
#[must_use]
pub const fn alternative(self) -> AlternativeId {
self.alternative
}
}
impl<'surface> ResolvedMutation<'surface> {
pub(in crate::muterprater) const fn resolved(
selection: ActiveSelection,
point: &'surface MutationPoint,
alternative: &'surface AdmittedAlternative,
) -> Self {
Self {
selection,
point,
alternative,
}
}
#[must_use]
pub const fn selection(self) -> ActiveSelection {
self.selection
}
#[must_use]
pub const fn point(self) -> &'surface MutationPoint {
self.point
}
#[must_use]
pub const fn alternative(self) -> &'surface AdmittedAlternative {
self.alternative
}
}
impl<'surface> EvaluationDirective<'surface> {
#[must_use]
pub const fn no_mutation() -> Self {
Self { resolved: None }
}
pub(in crate::muterprater) const fn active(
selection: ActiveSelection,
point: &'surface MutationPoint,
alternative: &'surface AdmittedAlternative,
) -> Self {
Self {
resolved: Some(ResolvedMutation::resolved(selection, point, alternative)),
}
}
#[must_use]
pub const fn resolved(self) -> Option<ResolvedMutation<'surface>> {
self.resolved
}
}