1use crate::error::CapError;
12use alloc::vec::Vec;
13use ssz_derive::{Decode, Encode};
14
15#[derive(
21 Debug,
22 Clone,
23 Copy,
24 PartialEq,
25 Eq,
26 Hash,
27 PartialOrd,
28 Ord,
29 Encode,
30 Decode,
31 ssz_derive::HashTreeRoot,
32)]
33pub struct SlotIdx(#[ssz(transparent)] pub u32);
34
35impl SlotIdx {
36 pub const fn new(idx: u32) -> Self {
37 Self(idx)
38 }
39
40 pub const fn get(self) -> u32 {
41 self.0
42 }
43
44 pub fn fits(self, size_log: u8) -> bool {
46 if size_log >= 32 {
47 true
49 } else {
50 self.0 < (1u32 << size_log)
51 }
52 }
53
54 pub fn as_usize(self) -> usize {
56 self.0 as usize
57 }
58}
59
60impl From<u8> for SlotIdx {
61 fn from(v: u8) -> Self {
62 Self(v as u32)
63 }
64}
65
66impl From<u16> for SlotIdx {
67 fn from(v: u16) -> Self {
68 Self(v as u32)
69 }
70}
71
72#[derive(Debug, Clone, PartialEq, Eq, Hash, Encode, Decode, ssz_derive::HashTreeRoot)]
83pub struct SlotPath {
84 pub steps: Vec<SlotIdx>,
85}
86
87impl SlotPath {
88 pub fn root(idx: SlotIdx) -> Self {
90 Self { steps: vec![idx] }
91 }
92
93 pub fn new(steps: Vec<SlotIdx>) -> Result<Self, CapError> {
95 if steps.is_empty() {
96 Err(CapError::SlotOutOfRange(0, 0))
100 } else {
101 Ok(Self { steps })
102 }
103 }
104
105 pub fn is_root_slot(&self) -> bool {
108 self.steps.len() == 1
109 }
110
111 pub fn target(&self) -> SlotIdx {
114 *self.steps.last().unwrap()
116 }
117
118 pub fn prefix(&self) -> &[SlotIdx] {
121 let len = self.steps.len();
122 &self.steps[..len - 1]
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn slot_idx_fits_within_size_log() {
132 assert!(SlotIdx(0).fits(0)); assert!(!SlotIdx(1).fits(0)); assert!(SlotIdx(255).fits(8)); assert!(!SlotIdx(256).fits(8));
136 assert!(SlotIdx(255).fits(16));
137 assert!(SlotIdx(u32::MAX).fits(32));
138 }
139
140 #[test]
141 fn slot_idx_conversions() {
142 assert_eq!(SlotIdx::from(7u8).get(), 7);
143 assert_eq!(SlotIdx::from(1000u16).get(), 1000);
144 assert_eq!(SlotIdx(42).as_usize(), 42);
145 }
146
147 #[test]
148 fn slot_path_root_single_step() {
149 let p = SlotPath::root(SlotIdx(7));
150 assert!(p.is_root_slot());
151 assert_eq!(p.target(), SlotIdx(7));
152 assert_eq!(p.prefix(), &[]);
153 }
154
155 #[test]
156 fn slot_path_nested() {
157 let p = SlotPath::new(vec![SlotIdx(7), SlotIdx(3), SlotIdx(12)]).unwrap();
158 assert!(!p.is_root_slot());
159 assert_eq!(p.target(), SlotIdx(12));
160 assert_eq!(p.prefix(), &[SlotIdx(7), SlotIdx(3)]);
161 }
162
163 #[test]
164 fn slot_path_empty_rejected() {
165 assert!(SlotPath::new(vec![]).is_err());
166 }
167
168 #[test]
169 fn slot_path_equality_by_value() {
170 let a = SlotPath::root(SlotIdx(5));
171 let b = SlotPath::new(vec![SlotIdx(5)]).unwrap();
172 assert_eq!(a, b);
173 }
174}