Skip to main content

ic_backup/model/application_ingress/request/
mod.rs

1//! Passive original application reservations; reconstructing one is never a resend permit.
2
3use super::{ApplicationIngressError, ApplicationIngressPayload};
4use crate::model::attempt_journal::MutationReservation;
5use crate::model::attempt_journal::MutationReservationError as Reservation;
6use crate::model::attempt_journal::ObservationReservation;
7use crate::model::{
8    attempt_journal::{AttemptAuthorityRecord, AttemptJournalRecord},
9    operation_plan::OperationPlanRecord,
10};
11
12/// Exact original application mutation after durable reservation.
13///
14/// The caller holds its original guard and independently admits fresh authority,
15/// dependencies, application safety and exclusive never-dispatched custody.
16#[derive(Debug)]
17pub struct ApplicationMutationRequest<'a> {
18    plan: &'a OperationPlanRecord,
19    payload: &'a ApplicationIngressPayload,
20    reservation: MutationReservation,
21}
22impl<'a> ApplicationMutationRequest<'a> {
23    /// Bind original exact opaque bytes to the current pending mutation.
24    /// # Errors
25    /// Rejects changed authority/payload, absent mutation or pending recovery.
26    pub fn new(
27        plan: &'a OperationPlanRecord,
28        sequence: u64,
29        journal: &AttemptJournalRecord,
30        payload: &'a ApplicationIngressPayload,
31    ) -> Result<Self, ApplicationIngressError> {
32        let authority = authority(plan, sequence, journal)?;
33        payload.validate_mutation_binding(authority.binding())?;
34        let reservation = MutationReservation::new(authority, journal)?;
35        Ok(Self {
36            plan,
37            payload,
38            reservation,
39        })
40    }
41    /// Read the complete original plan/context and inventory.
42    #[must_use]
43    pub const fn plan(&self) -> &OperationPlanRecord {
44        self.plan
45    }
46    /// Read exact opaque update fields.
47    #[must_use]
48    pub const fn payload(&self) -> &ApplicationIngressPayload {
49        self.payload
50    }
51    /// Read full original authority and finite allowances.
52    #[must_use]
53    pub const fn authority(&self) -> &AttemptAuthorityRecord {
54        &self.reservation.authority
55    }
56    /// Read the already consumed mutation attempt.
57    #[must_use]
58    pub const fn mutation_attempt(&self) -> u32 {
59        self.reservation.mutation_attempt
60    }
61    /// Recheck current exact original reservation without authorizing dispatch.
62    /// # Errors
63    /// Rejects changed authority, mutation or pending observation.
64    pub fn validate_journal(
65        &self,
66        journal: &AttemptJournalRecord,
67    ) -> Result<(), ApplicationIngressError> {
68        self.reservation.validate(journal).map_err(Into::into)
69    }
70}
71
72/// Exact separately reserved application recovery update for an unresolved mutation.
73///
74/// This consumes the observation lane, even when its opaque method reads status.
75/// It never reuses mutation allowance or supplies application settlement semantics.
76#[derive(Debug)]
77pub struct ApplicationObservationRequest<'a> {
78    plan: &'a OperationPlanRecord,
79    payload: &'a ApplicationIngressPayload,
80    reservation: ObservationReservation,
81}
82impl<'a> ApplicationObservationRequest<'a> {
83    /// Bind exact recovery bytes to the independently reserved observation digest.
84    /// # Errors
85    /// Rejects changed authority/target, absent attempts or different reserved bytes.
86    pub fn new(
87        plan: &'a OperationPlanRecord,
88        sequence: u64,
89        journal: &AttemptJournalRecord,
90        payload: &'a ApplicationIngressPayload,
91    ) -> Result<Self, ApplicationIngressError> {
92        let authority = authority(plan, sequence, journal)?;
93        payload.validate_observation_binding(authority.binding())?;
94        let reservation = ObservationReservation::new(authority, journal, payload.digest())
95            .map_err(crate::model::ic_observation::IcObservationRequestError::from)?;
96        Ok(Self {
97            plan,
98            payload,
99            reservation,
100        })
101    }
102    /// Read the complete original mutation plan/context and inventory.
103    #[must_use]
104    pub const fn plan(&self) -> &OperationPlanRecord {
105        self.plan
106    }
107    /// Read exact original recovery update fields.
108    #[must_use]
109    pub const fn payload(&self) -> &ApplicationIngressPayload {
110        self.payload
111    }
112    /// Read original authority; its request still names the unresolved mutation.
113    #[must_use]
114    pub const fn authority(&self) -> &AttemptAuthorityRecord {
115        &self.reservation.authority
116    }
117    /// Read the original unresolved mutation attempt.
118    #[must_use]
119    pub const fn mutation_attempt(&self) -> u32 {
120        self.reservation.mutation_attempt
121    }
122    /// Read the independently consumed observation attempt.
123    #[must_use]
124    pub const fn observation_attempt(&self) -> u32 {
125        self.reservation.observation_attempt
126    }
127    /// Recheck exact current observation identity without a dispatch permit.
128    /// # Errors
129    /// Rejects settled/replaced attempts, changed authority or different reserved bytes.
130    pub fn validate_journal(
131        &self,
132        journal: &AttemptJournalRecord,
133    ) -> Result<(), ApplicationIngressError> {
134        self.reservation
135            .validate(journal)
136            .map_err(crate::model::ic_observation::IcObservationRequestError::from)?;
137        Ok(())
138    }
139}
140
141fn authority(
142    plan: &OperationPlanRecord,
143    sequence: u64,
144    journal: &AttemptJournalRecord,
145) -> Result<AttemptAuthorityRecord, ApplicationIngressError> {
146    let authority = plan.attempt_authority(sequence)?;
147    if journal.authority() != &authority {
148        return Err(ApplicationIngressError::AuthorityMismatch);
149    }
150    Ok(authority)
151}
152
153impl From<crate::model::attempt_journal::MutationReservationError> for ApplicationIngressError {
154    fn from(error: crate::model::attempt_journal::MutationReservationError) -> Self {
155        match error {
156            Reservation::NoPendingMutation => Self::NoPendingMutation,
157            Reservation::AuthorityMismatch => Self::AuthorityMismatch,
158            Reservation::MutationMismatch => Self::MutationMismatch,
159            Reservation::ObservationPending => Self::ObservationPending,
160        }
161    }
162}