Skip to main content

ic_backup/model/operation_plan/budget/
mod.rs

1//! Immutable declared plan ceilings over original assigned per-operation allowances.
2
3use super::OperationPlanError;
4use serde::{Deserialize, Serialize};
5
6/// Maximum combined mutation/observation attempts assigned by one declared plan.
7pub const MAX_PLAN_ATTEMPTS: u32 = 65536;
8/// Immutable declared aggregate attempt ceilings; this is not cycle accounting.
9#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
10#[serde(try_from = "BudgetFields")]
11pub struct PlanBudgetRecord {
12    mutations: u32,
13    observations: u32,
14}
15#[derive(Deserialize)]
16#[serde(deny_unknown_fields)]
17struct BudgetFields {
18    mutations: u32,
19    observations: u32,
20}
21impl TryFrom<BudgetFields> for PlanBudgetRecord {
22    type Error = OperationPlanError;
23    fn try_from(fields: BudgetFields) -> Result<Self, Self::Error> {
24        Self::new(fields.mutations, fields.observations)
25    }
26}
27impl PlanBudgetRecord {
28    /// Admit original finite aggregate ceilings, including zero, using checked arithmetic.
29    ///
30    /// # Errors
31    /// Rejects overflow or combined allowance above [`MAX_PLAN_ATTEMPTS`].
32    pub fn new(mutations: u32, observations: u32) -> Result<Self, OperationPlanError> {
33        if mutations
34            .checked_add(observations)
35            .is_none_or(|total| total > MAX_PLAN_ATTEMPTS)
36        {
37            return Err(OperationPlanError::BudgetTooLarge);
38        }
39        Ok(Self {
40            mutations,
41            observations,
42        })
43    }
44    /// Read original declared aggregate mutation ceiling.
45    #[must_use]
46    pub const fn mutations(&self) -> u32 {
47        self.mutations
48    }
49    /// Read original declared aggregate observation ceiling.
50    #[must_use]
51    pub const fn observations(&self) -> u32 {
52        self.observations
53    }
54    pub(super) const fn admits(&self, allocated: AllocatedAttemptsView) -> bool {
55        allocated.mutations <= self.mutations && allocated.observations <= self.observations
56    }
57}
58/// Read-only assigned attempt totals, not consumed allowance or observed IC debits.
59#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
60pub struct AllocatedAttemptsView {
61    /// Sum of immutable per-operation mutation allowances.
62    pub mutations: u32,
63    /// Sum of immutable per-operation observation allowances.
64    pub observations: u32,
65}