use std::fmt;
use std::marker::PhantomData;
use crate::error::{CoreError, CoreResult};
use crate::frame::FrameId;
pub struct FrameToken {
frame_id: FrameId,
domain_id: u32,
generation: u64,
epoch: u64,
_not_send_sync: PhantomData<*const ()>,
}
impl fmt::Debug for FrameToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FrameToken")
.field("frame_id", &self.frame_id)
.field("domain_id", &self.domain_id)
.field("generation", &self.generation)
.field("epoch", &self.epoch)
.finish()
}
}
impl FrameToken {
pub(crate) fn new(
frame_id: FrameId,
domain_id: u32,
generation: u64,
epoch: u64,
) -> Self {
Self {
frame_id,
domain_id,
generation,
epoch,
_not_send_sync: PhantomData,
}
}
pub fn frame_id(&self) -> FrameId {
self.frame_id
}
pub fn domain_id(&self) -> u32 {
self.domain_id
}
pub fn generation(&self) -> u64 {
self.generation
}
pub fn epoch(&self) -> u64 {
self.epoch
}
pub fn verify_ownership(
&self,
expected_domain: u32,
expected_generation: u64,
) -> CoreResult<()> {
let domain_ok = crate::constant_time_eq_u32(self.domain_id, expected_domain);
let gen_ok = crate::constant_time_eq_u64(self.generation, expected_generation);
if domain_ok && gen_ok {
Ok(())
} else {
Err(CoreError::ownership_violation(
if domain_ok { "valid domain" } else { "invalid domain" },
if gen_ok { "valid generation" } else { "invalid generation" },
))
}
}
pub fn verify_epoch(&self, expected_epoch: u64) -> CoreResult<()> {
if crate::constant_time_eq_u64(self.epoch, expected_epoch) {
Ok(())
} else {
Err(CoreError::state_conflict(
format!("epoch={}", self.epoch),
format!("epoch={}", expected_epoch),
))
}
}
pub fn into_frame_id(self) -> FrameId {
self.frame_id
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_token_creation() {
let token = FrameToken::new(FrameId::new(1), 0, 1, 0);
assert_eq!(token.frame_id(), FrameId::new(1));
assert_eq!(token.domain_id(), 0);
assert_eq!(token.generation(), 1);
assert_eq!(token.epoch(), 0);
}
#[test]
fn test_verify_ownership_success() {
let token = FrameToken::new(FrameId::new(1), 0, 1, 0);
assert!(token.verify_ownership(0, 1).is_ok());
}
#[test]
fn test_verify_ownership_failure_domain() {
let token = FrameToken::new(FrameId::new(1), 0, 1, 0);
let result = token.verify_ownership(1, 1);
assert!(result.is_err());
}
#[test]
fn test_verify_ownership_failure_generation() {
let token = FrameToken::new(FrameId::new(1), 0, 1, 0);
let result = token.verify_ownership(0, 2);
assert!(result.is_err());
}
#[test]
fn test_verify_epoch_success() {
let token = FrameToken::new(FrameId::new(1), 0, 1, 42);
assert!(token.verify_epoch(42).is_ok());
}
#[test]
fn test_verify_epoch_failure() {
let token = FrameToken::new(FrameId::new(1), 0, 1, 42);
let result = token.verify_epoch(43);
assert!(result.is_err());
}
#[test]
fn test_into_frame_id() {
let token = FrameToken::new(FrameId::new(100), 0, 1, 0);
let id = token.into_frame_id();
assert_eq!(id, FrameId::new(100));
}
#[test]
fn test_token_is_not_copy() {
let token = FrameToken::new(FrameId::new(1), 0, 1, 0);
let _token2 = token; }
#[test]
fn test_verify_ownership_both_fail() {
let token = FrameToken::new(FrameId::new(1), 0, 1, 0);
let result = token.verify_ownership(99, 99);
assert!(result.is_err());
}
#[test]
fn test_verify_epoch_success_gen0() {
let token = FrameToken::new(FrameId::new(1), 0, 0, 42);
assert!(token.verify_epoch(42).is_ok());
}
#[test]
fn test_verify_epoch_failure_gen0() {
let token = FrameToken::new(FrameId::new(1), 0, 0, 42);
let result = token.verify_epoch(100);
assert!(result.is_err());
}
#[test]
fn test_verify_epoch_zero() {
let token = FrameToken::new(FrameId::new(1), 0, 0, 0);
assert!(token.verify_epoch(0).is_ok());
assert!(token.verify_epoch(1).is_err());
}
#[test]
fn test_verify_epoch_max() {
let token = FrameToken::new(FrameId::new(1), 0, 0, u64::MAX);
assert!(token.verify_epoch(u64::MAX).is_ok());
assert!(token.verify_epoch(u64::MAX - 1).is_err());
}
#[test]
fn test_token_debug_format() {
let token = FrameToken::new(FrameId::new(42), 7, 3, 5);
let debug_str = format!("{:?}", token);
assert!(debug_str.contains("FrameToken"));
assert!(debug_str.contains("frame_id"));
assert!(debug_str.contains("domain_id"));
assert!(debug_str.contains("generation"));
assert!(debug_str.contains("epoch"));
}
#[test]
fn test_frame_id_from_u32_trait() {
let id: FrameId = 123u32.into();
assert_eq!(id.value(), 123);
assert_eq!(id, FrameId::new(123));
}
#[test]
fn test_frame_id_from_u32_zero() {
let id: FrameId = 0u32.into();
assert_eq!(id.value(), 0);
}
#[test]
fn test_frame_id_from_u32_max() {
let id: FrameId = u32::MAX.into();
assert_eq!(id.value(), u32::MAX);
}
#[test]
fn test_token_boundary_values() {
let token = FrameToken::new(
FrameId::new(u32::MAX),
u32::MAX,
u64::MAX,
u64::MAX,
);
assert_eq!(token.frame_id().value(), u32::MAX);
assert_eq!(token.domain_id(), u32::MAX);
assert_eq!(token.generation(), u64::MAX);
assert_eq!(token.epoch(), u64::MAX);
}
#[test]
fn test_into_frame_id_consumes_token() {
let token = FrameToken::new(FrameId::new(99), 0, 0, 0);
let id = token.into_frame_id();
assert_eq!(id, FrameId::new(99));
}
}