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