use crate::error::PfcpError;
use crate::ie::{Ie, IeType};
use std::time::Duration;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UserPlaneInactivityTimer {
pub timer_value: Duration,
}
impl UserPlaneInactivityTimer {
pub fn new(timer_value: Duration) -> Self {
UserPlaneInactivityTimer { timer_value }
}
pub fn from_seconds(seconds: u32) -> Self {
UserPlaneInactivityTimer::new(Duration::from_secs(seconds as u64))
}
pub fn from_minutes(minutes: u32) -> Self {
UserPlaneInactivityTimer::new(Duration::from_secs((minutes * 60) as u64))
}
pub fn from_hours(hours: u32) -> Self {
UserPlaneInactivityTimer::new(Duration::from_secs((hours * 3600) as u64))
}
pub fn as_seconds(&self) -> u64 {
self.timer_value.as_secs()
}
pub fn as_minutes(&self) -> u64 {
self.timer_value.as_secs() / 60
}
pub fn as_hours(&self) -> u64 {
self.timer_value.as_secs() / 3600
}
pub fn timer_value(&self) -> Duration {
self.timer_value
}
pub fn is_infinite(&self) -> bool {
self.timer_value.is_zero()
}
pub fn infinite() -> Self {
UserPlaneInactivityTimer::new(Duration::ZERO)
}
pub fn marshal(&self) -> Vec<u8> {
let seconds = self.timer_value.as_secs() as u32;
seconds.to_be_bytes().to_vec()
}
pub fn unmarshal(payload: &[u8]) -> Result<Self, PfcpError> {
if payload.len() != 4 {
return Err(PfcpError::invalid_length(
"User Plane Inactivity Timer",
IeType::UserPlaneInactivityTimer,
4,
payload.len(),
));
}
let seconds = u32::from_be_bytes([payload[0], payload[1], payload[2], payload[3]]);
let timer_value = Duration::from_secs(seconds as u64);
Ok(UserPlaneInactivityTimer { timer_value })
}
pub fn to_ie(&self) -> Ie {
Ie::new(IeType::UserPlaneInactivityTimer, self.marshal())
}
pub fn len(&self) -> usize {
4
}
pub fn is_empty(&self) -> bool {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_user_plane_inactivity_timer_marshal_unmarshal_seconds() {
let timer = UserPlaneInactivityTimer::from_seconds(300); let marshaled = timer.marshal();
let unmarshaled = UserPlaneInactivityTimer::unmarshal(&marshaled).unwrap();
assert_eq!(timer, unmarshaled);
assert_eq!(unmarshaled.as_seconds(), 300);
assert_eq!(unmarshaled.as_minutes(), 5);
assert_eq!(marshaled, vec![0x00, 0x00, 0x01, 0x2C]); assert_eq!(timer.len(), 4);
assert!(!timer.is_empty());
assert!(!timer.is_infinite());
}
#[test]
fn test_user_plane_inactivity_timer_marshal_unmarshal_minutes() {
let timer = UserPlaneInactivityTimer::from_minutes(10);
let marshaled = timer.marshal();
let unmarshaled = UserPlaneInactivityTimer::unmarshal(&marshaled).unwrap();
assert_eq!(timer, unmarshaled);
assert_eq!(unmarshaled.as_seconds(), 600);
assert_eq!(unmarshaled.as_minutes(), 10);
assert_eq!(marshaled, vec![0x00, 0x00, 0x02, 0x58]); }
#[test]
fn test_user_plane_inactivity_timer_marshal_unmarshal_hours() {
let timer = UserPlaneInactivityTimer::from_hours(2);
let marshaled = timer.marshal();
let unmarshaled = UserPlaneInactivityTimer::unmarshal(&marshaled).unwrap();
assert_eq!(timer, unmarshaled);
assert_eq!(unmarshaled.as_seconds(), 7200);
assert_eq!(unmarshaled.as_minutes(), 120);
assert_eq!(unmarshaled.as_hours(), 2);
assert_eq!(marshaled, vec![0x00, 0x00, 0x1C, 0x20]); }
#[test]
fn test_user_plane_inactivity_timer_infinite() {
let timer = UserPlaneInactivityTimer::infinite();
let marshaled = timer.marshal();
let unmarshaled = UserPlaneInactivityTimer::unmarshal(&marshaled).unwrap();
assert_eq!(timer, unmarshaled);
assert_eq!(unmarshaled.as_seconds(), 0);
assert_eq!(unmarshaled.as_minutes(), 0);
assert_eq!(unmarshaled.as_hours(), 0);
assert_eq!(marshaled, vec![0x00, 0x00, 0x00, 0x00]);
assert!(timer.is_infinite());
assert!(unmarshaled.is_infinite());
}
#[test]
fn test_user_plane_inactivity_timer_zero_duration() {
let timer = UserPlaneInactivityTimer::new(Duration::ZERO);
let marshaled = timer.marshal();
let unmarshaled = UserPlaneInactivityTimer::unmarshal(&marshaled).unwrap();
assert_eq!(timer, unmarshaled);
assert!(timer.is_infinite());
assert!(unmarshaled.is_infinite());
assert_eq!(marshaled, vec![0x00, 0x00, 0x00, 0x00]);
}
#[test]
fn test_user_plane_inactivity_timer_max_value() {
let timer = UserPlaneInactivityTimer::from_seconds(u32::MAX);
let marshaled = timer.marshal();
let unmarshaled = UserPlaneInactivityTimer::unmarshal(&marshaled).unwrap();
assert_eq!(timer, unmarshaled);
assert_eq!(unmarshaled.as_seconds(), u32::MAX as u64);
assert_eq!(marshaled, vec![0xFF, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn test_user_plane_inactivity_timer_custom_duration() {
let custom_duration = Duration::from_millis(12345678); let timer = UserPlaneInactivityTimer::new(custom_duration);
let marshaled = timer.marshal();
let unmarshaled = UserPlaneInactivityTimer::unmarshal(&marshaled).unwrap();
assert_eq!(unmarshaled.as_seconds(), 12345);
assert!(!timer.is_infinite());
}
#[test]
fn test_user_plane_inactivity_timer_to_ie() {
let timer = UserPlaneInactivityTimer::from_minutes(30);
let ie = timer.to_ie();
assert_eq!(ie.ie_type, IeType::UserPlaneInactivityTimer);
let unmarshaled = UserPlaneInactivityTimer::unmarshal(&ie.payload).unwrap();
assert_eq!(timer, unmarshaled);
}
#[test]
fn test_user_plane_inactivity_timer_unmarshal_wrong_length() {
let result = UserPlaneInactivityTimer::unmarshal(&[0x01, 0x02]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(err, PfcpError::InvalidLength { .. }));
let result = UserPlaneInactivityTimer::unmarshal(&[0x01, 0x02, 0x03, 0x04, 0x05]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(err, PfcpError::InvalidLength { .. }));
}
#[test]
fn test_user_plane_inactivity_timer_unmarshal_empty() {
let result = UserPlaneInactivityTimer::unmarshal(&[]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(err, PfcpError::InvalidLength { .. }));
}
#[test]
fn test_user_plane_inactivity_timer_common_values() {
let test_cases = vec![
(30, "30 seconds"),
(60, "1 minute"),
(300, "5 minutes"),
(1800, "30 minutes"),
(3600, "1 hour"),
(7200, "2 hours"),
(86400, "24 hours"),
];
for (seconds, description) in test_cases {
let timer = UserPlaneInactivityTimer::from_seconds(seconds);
let marshaled = timer.marshal();
let unmarshaled = UserPlaneInactivityTimer::unmarshal(&marshaled).unwrap();
assert_eq!(timer, unmarshaled, "Failed for {}", description);
assert_eq!(unmarshaled.as_seconds(), seconds as u64);
}
}
#[test]
fn test_user_plane_inactivity_timer_duration_conversions() {
let timer = UserPlaneInactivityTimer::from_seconds(3661);
assert_eq!(timer.as_seconds(), 3661);
assert_eq!(timer.as_minutes(), 61); assert_eq!(timer.as_hours(), 1); assert_eq!(timer.timer_value(), Duration::from_secs(3661));
}
#[test]
fn test_user_plane_inactivity_timer_round_trip_various_values() {
let test_values = vec![
0, 1, 59, 60, 3599, 3600, 86399, 86400, u32::MAX, ];
for value in test_values {
let original = UserPlaneInactivityTimer::from_seconds(value);
let marshaled = original.marshal();
let unmarshaled = UserPlaneInactivityTimer::unmarshal(&marshaled).unwrap();
assert_eq!(original, unmarshaled, "Failed for {} seconds", value);
}
}
}