1use 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}