Skip to main content

tf_types/
delegation.rs

1//! Delegation chain walker — mirrors
2//! `tools/tf-types-ts/src/core/delegation.ts`.
3
4use crate::capability::intersect_constraints;
5use crate::generated::common::{Constraint, DelegationLink};
6
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct WalkResult {
9    pub valid: bool,
10    pub effective: Vec<Constraint>,
11    pub expired_at: Option<String>,
12    pub broken_step: Option<usize>,
13    pub reason: Option<String>,
14}
15
16pub fn walk_chain(chain: &[DelegationLink], now: &str) -> WalkResult {
17    let mut effective: Vec<Constraint> = Vec::new();
18    let mut allow_redelegation = true;
19    let mut max_depth_remaining: i64 = i64::MAX;
20
21    for (i, step) in chain.iter().enumerate() {
22        if i > 0 {
23            if !allow_redelegation {
24                return WalkResult {
25                    valid: false,
26                    effective,
27                    expired_at: None,
28                    broken_step: Some(i),
29                    reason: Some(format!("step {} disallows redelegation", i - 1)),
30                };
31            }
32            if max_depth_remaining <= 0 {
33                return WalkResult {
34                    valid: false,
35                    effective,
36                    expired_at: None,
37                    broken_step: Some(i),
38                    reason: Some(format!("max_depth exceeded at step {}", i)),
39                };
40            }
41            max_depth_remaining -= 1;
42        }
43
44        if let Some(expires_at) = &step.expires_at {
45            if expires_at.as_str() < now {
46                return WalkResult {
47                    valid: false,
48                    effective,
49                    expired_at: Some(expires_at.clone()),
50                    broken_step: Some(i),
51                    reason: Some(format!("step {} expired at {}", i, expires_at)),
52                };
53            }
54        }
55
56        if let Some(constraints) = &step.constraints {
57            if !constraints.is_empty() {
58                effective = intersect_constraints(&effective, constraints);
59            }
60        }
61
62        if let Some(redelegation) = &step.redelegation {
63            allow_redelegation = redelegation.allowed;
64            if let Some(d) = redelegation.max_depth {
65                max_depth_remaining = max_depth_remaining.min(d);
66            }
67        } else {
68            allow_redelegation = true;
69        }
70    }
71
72    WalkResult {
73        valid: true,
74        effective,
75        expired_at: None,
76        broken_step: None,
77        reason: None,
78    }
79}