Skip to main content

ic_backup/model/effect_graph/node/
mod.rs

1//! Passive exact operation identity and bounded explicit prerequisite admission.
2
3use super::EffectGraphError;
4use crate::model::bounded_sequence;
5use serde::{Deserialize, Deserializer, Serialize};
6
7/// Maximum direct prerequisites on one operation, independently of total graph bounds.
8pub const MAX_EFFECT_DEPENDENCIES: usize = 1024;
9
10/// Passive operation dependency declaration, without an execution request or authority.
11#[derive(Clone, Debug)]
12pub struct EffectNodeRequest {
13    /// Exact opaque operation identity; zero and nonconsecutive u64 values are admitted.
14    pub operation_sequence: u64,
15    /// Exact operations required before this operation; duplicate declarations reject.
16    pub depends_on: Vec<u64>,
17}
18
19/// Canonical immutable operation node with sorted unique explicit dependencies.
20#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
21#[serde(try_from = "NodeFields")]
22pub struct EffectNodeRecord {
23    operation_sequence: u64,
24    depends_on: Vec<u64>,
25}
26#[derive(Deserialize)]
27#[serde(deny_unknown_fields)]
28struct NodeFields {
29    operation_sequence: u64,
30    #[serde(deserialize_with = "bounded_dependencies")]
31    depends_on: Vec<u64>,
32}
33impl TryFrom<NodeFields> for EffectNodeRecord {
34    type Error = EffectGraphError;
35    fn try_from(fields: NodeFields) -> Result<Self, Self::Error> {
36        Self::new(EffectNodeRequest {
37            operation_sequence: fields.operation_sequence,
38            depends_on: fields.depends_on,
39        })
40    }
41}
42impl EffectNodeRecord {
43    /// Admit finite sorted explicit dependencies; graph admission owns closure/cycles.
44    ///
45    /// # Errors
46    /// Rejects excessive direct dependencies and duplicate exact prerequisites.
47    pub fn new(mut request: EffectNodeRequest) -> Result<Self, EffectGraphError> {
48        if request.depends_on.len() > MAX_EFFECT_DEPENDENCIES {
49            return Err(EffectGraphError::TooManyDependencies);
50        }
51        request.depends_on.sort_unstable();
52        for pair in request.depends_on.windows(2) {
53            if pair[0] == pair[1] {
54                return Err(EffectGraphError::DuplicateDependency {
55                    operation_sequence: request.operation_sequence,
56                    dependency: pair[0],
57                });
58            }
59        }
60        Ok(Self {
61            operation_sequence: request.operation_sequence,
62            depends_on: request.depends_on,
63        })
64    }
65    /// Read the exact opaque operation sequence identity.
66    #[must_use]
67    pub const fn operation_sequence(&self) -> u64 {
68        self.operation_sequence
69    }
70    /// Read sorted exact explicit prerequisites.
71    #[must_use]
72    pub fn depends_on(&self) -> &[u64] {
73        &self.depends_on
74    }
75}
76
77fn bounded_dependencies<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u64>, D::Error> {
78    bounded_sequence::deserialize::<_, _, MAX_EFFECT_DEPENDENCIES>(
79        deserializer,
80        "bounded exact effect dependencies",
81        EffectGraphError::TooManyDependencies,
82    )
83}