1use std::fmt;
7use std::marker::PhantomData;
8
9use crate::error::{CoreError, CoreResult};
10use crate::frame::FrameId;
11
12pub struct FrameToken {
27 frame_id: FrameId,
29 domain_id: u32,
31 generation: u64,
33 epoch: u64,
35 _not_send_sync: PhantomData<*const ()>,
39}
40
41impl fmt::Debug for FrameToken {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 f.debug_struct("FrameToken")
44 .field("frame_id", &self.frame_id)
45 .field("domain_id", &self.domain_id)
46 .field("generation", &self.generation)
47 .field("epoch", &self.epoch)
48 .finish()
49 }
50}
51
52impl FrameToken {
53 pub(crate) fn new(
67 frame_id: FrameId,
68 domain_id: u32,
69 generation: u64,
70 epoch: u64,
71 ) -> Self {
72 Self {
73 frame_id,
74 domain_id,
75 generation,
76 epoch,
77 _not_send_sync: PhantomData,
78 }
79 }
80
81 pub fn frame_id(&self) -> FrameId {
83 self.frame_id
84 }
85
86 pub fn domain_id(&self) -> u32 {
88 self.domain_id
89 }
90
91 pub fn generation(&self) -> u64 {
93 self.generation
94 }
95
96 pub fn epoch(&self) -> u64 {
98 self.epoch
99 }
100
101 pub fn verify_ownership(
113 &self,
114 expected_domain: u32,
115 expected_generation: u64,
116 ) -> CoreResult<()> {
117 let domain_ok = crate::constant_time_eq_u32(self.domain_id, expected_domain);
118 let gen_ok = crate::constant_time_eq_u64(self.generation, expected_generation);
119
120 if domain_ok && gen_ok {
121 Ok(())
122 } else {
123 Err(CoreError::ownership_violation(
124 if domain_ok { "valid domain" } else { "invalid domain" },
125 if gen_ok { "valid generation" } else { "invalid generation" },
126 ))
127 }
128 }
129
130 pub fn verify_epoch(&self, expected_epoch: u64) -> CoreResult<()> {
141 if crate::constant_time_eq_u64(self.epoch, expected_epoch) {
142 Ok(())
143 } else {
144 Err(CoreError::state_conflict(
145 format!("epoch={}", self.epoch),
146 format!("epoch={}", expected_epoch),
147 ))
148 }
149 }
150
151 pub fn into_frame_id(self) -> FrameId {
159 self.frame_id
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn test_token_creation() {
169 let token = FrameToken::new(FrameId::new(1), 0, 1, 0);
170 assert_eq!(token.frame_id(), FrameId::new(1));
171 assert_eq!(token.domain_id(), 0);
172 assert_eq!(token.generation(), 1);
173 assert_eq!(token.epoch(), 0);
174 }
175
176 #[test]
177 fn test_verify_ownership_success() {
178 let token = FrameToken::new(FrameId::new(1), 0, 1, 0);
179 assert!(token.verify_ownership(0, 1).is_ok());
180 }
181
182 #[test]
183 fn test_verify_ownership_failure_domain() {
184 let token = FrameToken::new(FrameId::new(1), 0, 1, 0);
185 let result = token.verify_ownership(1, 1);
186 assert!(result.is_err());
187 }
188
189 #[test]
190 fn test_verify_ownership_failure_generation() {
191 let token = FrameToken::new(FrameId::new(1), 0, 1, 0);
192 let result = token.verify_ownership(0, 2);
193 assert!(result.is_err());
194 }
195
196 #[test]
197 fn test_verify_epoch_success() {
198 let token = FrameToken::new(FrameId::new(1), 0, 1, 42);
199 assert!(token.verify_epoch(42).is_ok());
200 }
201
202 #[test]
203 fn test_verify_epoch_failure() {
204 let token = FrameToken::new(FrameId::new(1), 0, 1, 42);
205 let result = token.verify_epoch(43);
206 assert!(result.is_err());
207 }
208
209 #[test]
210 fn test_into_frame_id() {
211 let token = FrameToken::new(FrameId::new(100), 0, 1, 0);
212 let id = token.into_frame_id();
213 assert_eq!(id, FrameId::new(100));
214 }
215
216 #[test]
217 fn test_token_is_not_copy() {
218 let token = FrameToken::new(FrameId::new(1), 0, 1, 0);
220 let _token2 = token; }
223
224 #[test]
227 fn test_verify_ownership_both_fail() {
228 let token = FrameToken::new(FrameId::new(1), 0, 1, 0);
229 let result = token.verify_ownership(99, 99);
230 assert!(result.is_err());
231 }
232
233 #[test]
236 fn test_verify_epoch_success_gen0() {
237 let token = FrameToken::new(FrameId::new(1), 0, 0, 42);
238 assert!(token.verify_epoch(42).is_ok());
239 }
240
241 #[test]
242 fn test_verify_epoch_failure_gen0() {
243 let token = FrameToken::new(FrameId::new(1), 0, 0, 42);
244 let result = token.verify_epoch(100);
245 assert!(result.is_err());
246 }
247
248 #[test]
249 fn test_verify_epoch_zero() {
250 let token = FrameToken::new(FrameId::new(1), 0, 0, 0);
251 assert!(token.verify_epoch(0).is_ok());
252 assert!(token.verify_epoch(1).is_err());
253 }
254
255 #[test]
256 fn test_verify_epoch_max() {
257 let token = FrameToken::new(FrameId::new(1), 0, 0, u64::MAX);
258 assert!(token.verify_epoch(u64::MAX).is_ok());
259 assert!(token.verify_epoch(u64::MAX - 1).is_err());
260 }
261
262 #[test]
265 fn test_token_debug_format() {
266 let token = FrameToken::new(FrameId::new(42), 7, 3, 5);
267 let debug_str = format!("{:?}", token);
268 assert!(debug_str.contains("FrameToken"));
269 assert!(debug_str.contains("frame_id"));
270 assert!(debug_str.contains("domain_id"));
271 assert!(debug_str.contains("generation"));
272 assert!(debug_str.contains("epoch"));
273 }
274
275 #[test]
278 fn test_frame_id_from_u32_trait() {
279 let id: FrameId = 123u32.into();
280 assert_eq!(id.value(), 123);
281 assert_eq!(id, FrameId::new(123));
282 }
283
284 #[test]
285 fn test_frame_id_from_u32_zero() {
286 let id: FrameId = 0u32.into();
287 assert_eq!(id.value(), 0);
288 }
289
290 #[test]
291 fn test_frame_id_from_u32_max() {
292 let id: FrameId = u32::MAX.into();
293 assert_eq!(id.value(), u32::MAX);
294 }
295
296 #[test]
299 fn test_token_boundary_values() {
300 let token = FrameToken::new(
301 FrameId::new(u32::MAX),
302 u32::MAX,
303 u64::MAX,
304 u64::MAX,
305 );
306 assert_eq!(token.frame_id().value(), u32::MAX);
307 assert_eq!(token.domain_id(), u32::MAX);
308 assert_eq!(token.generation(), u64::MAX);
309 assert_eq!(token.epoch(), u64::MAX);
310 }
311
312 #[test]
315 fn test_into_frame_id_consumes_token() {
316 let token = FrameToken::new(FrameId::new(99), 0, 0, 0);
317 let id = token.into_frame_id();
318 assert_eq!(id, FrameId::new(99));
319 }
320}