use crate::error::PfcpError;
use crate::ie::{Ie, IeType};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AccessType {
Tgpp = 0,
NonTgpp = 1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AvailabilityStatus {
Unavailable = 0,
Available = 1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AccessAvailabilityInformation {
pub access_type: AccessType,
pub availability_status: AvailabilityStatus,
}
impl AccessAvailabilityInformation {
pub fn new(access_type: AccessType, availability_status: AvailabilityStatus) -> Self {
Self {
access_type,
availability_status,
}
}
pub fn marshal(&self) -> [u8; 1] {
let byte = ((self.availability_status as u8) << 2) | (self.access_type as u8);
[byte]
}
pub fn unmarshal(data: &[u8]) -> Result<Self, PfcpError> {
if data.is_empty() {
return Err(PfcpError::invalid_length(
"Access Availability Information",
IeType::AccessAvailabilityInformation,
1,
0,
));
}
let byte = data[0];
let access_type = match byte & 0x03 {
0 => AccessType::Tgpp,
1 => AccessType::NonTgpp,
v => {
return Err(PfcpError::invalid_value(
"Access Availability Information access_type",
v.to_string(),
"must be 0 (3GPP) or 1 (Non-3GPP)",
))
}
};
let availability_status = match (byte >> 2) & 0x03 {
0 => AvailabilityStatus::Unavailable,
1 => AvailabilityStatus::Available,
v => {
return Err(PfcpError::invalid_value(
"Access Availability Information availability_status",
v.to_string(),
"must be 0 (unavailable) or 1 (available)",
))
}
};
Ok(Self {
access_type,
availability_status,
})
}
pub fn to_ie(&self) -> Ie {
Ie::new(
IeType::AccessAvailabilityInformation,
self.marshal().to_vec(),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_marshal_unmarshal() {
let ie =
AccessAvailabilityInformation::new(AccessType::Tgpp, AvailabilityStatus::Available);
let parsed = AccessAvailabilityInformation::unmarshal(&ie.marshal()).unwrap();
assert_eq!(parsed, ie);
}
#[test]
fn test_all_combinations() {
let cases = [
(AccessType::Tgpp, AvailabilityStatus::Unavailable),
(AccessType::Tgpp, AvailabilityStatus::Available),
(AccessType::NonTgpp, AvailabilityStatus::Unavailable),
(AccessType::NonTgpp, AvailabilityStatus::Available),
];
for (at, avail) in cases {
let ie = AccessAvailabilityInformation::new(at, avail);
let parsed = AccessAvailabilityInformation::unmarshal(&ie.marshal()).unwrap();
assert_eq!(parsed, ie);
}
}
#[test]
fn test_marshal_byte_layout() {
let ie =
AccessAvailabilityInformation::new(AccessType::NonTgpp, AvailabilityStatus::Available);
assert_eq!(ie.marshal(), [0x05]);
}
#[test]
fn test_unmarshal_empty() {
assert!(matches!(
AccessAvailabilityInformation::unmarshal(&[]),
Err(PfcpError::InvalidLength { .. })
));
}
#[test]
fn test_unmarshal_invalid_access_type() {
assert!(matches!(
AccessAvailabilityInformation::unmarshal(&[0x03]),
Err(PfcpError::InvalidValue { .. })
));
}
#[test]
fn test_to_ie() {
let ie =
AccessAvailabilityInformation::new(AccessType::Tgpp, AvailabilityStatus::Available)
.to_ie();
assert_eq!(ie.ie_type, IeType::AccessAvailabilityInformation);
assert_eq!(ie.payload.len(), 1);
}
}