use crate::error::PfcpError;
use crate::ie::{Ie, IeType};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Snssai {
pub sst: u8, pub sd: Option<[u8; 3]>, }
impl Snssai {
pub fn new(sst: u8) -> Self {
Snssai { sst, sd: None }
}
pub fn with_sd(sst: u8, sd: [u8; 3]) -> Self {
Snssai { sst, sd: Some(sd) }
}
pub fn with_sd_u32(sst: u8, sd: u32) -> Self {
let sd_bytes = [
((sd >> 16) & 0xFF) as u8,
((sd >> 8) & 0xFF) as u8,
(sd & 0xFF) as u8,
];
Snssai {
sst,
sd: Some(sd_bytes),
}
}
pub fn sd_as_u32(&self) -> Option<u32> {
self.sd
.map(|sd| ((sd[0] as u32) << 16) | ((sd[1] as u32) << 8) | (sd[2] as u32))
}
pub fn marshal(&self) -> Vec<u8> {
let mut data = Vec::new();
data.push(self.sst);
if let Some(sd) = self.sd {
data.extend_from_slice(&sd);
}
data
}
pub fn unmarshal(payload: &[u8]) -> Result<Self, PfcpError> {
if payload.is_empty() {
return Err(PfcpError::invalid_length("S-NSSAI", IeType::Snssai, 1, 0));
}
let sst = payload[0];
let sd = if payload.len() >= 4 {
Some([payload[1], payload[2], payload[3]])
} else if payload.len() == 1 {
None
} else {
return Err(PfcpError::invalid_value(
"S-NSSAI",
payload.len().to_string(),
"must be 1 byte (SST only) or 4 bytes (SST + SD)",
));
};
Ok(Snssai { sst, sd })
}
pub fn to_ie(&self) -> Ie {
Ie::new(IeType::Snssai, self.marshal())
}
pub fn len(&self) -> usize {
if self.sd.is_some() {
4
} else {
1
}
}
pub fn is_empty(&self) -> bool {
false }
}
impl Snssai {
pub const SST_EMBB: u8 = 1;
pub const SST_URLLC: u8 = 2;
pub const SST_MIOT: u8 = 3;
pub fn embb() -> Self {
Snssai::new(Self::SST_EMBB)
}
pub fn urllc() -> Self {
Snssai::new(Self::SST_URLLC)
}
pub fn miot() -> Self {
Snssai::new(Self::SST_MIOT)
}
pub fn embb_with_sd(sd: u32) -> Self {
Snssai::with_sd_u32(Self::SST_EMBB, sd)
}
pub fn urllc_with_sd(sd: u32) -> Self {
Snssai::with_sd_u32(Self::SST_URLLC, sd)
}
pub fn miot_with_sd(sd: u32) -> Self {
Snssai::with_sd_u32(Self::SST_MIOT, sd)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_snssai_marshal_unmarshal_sst_only() {
let snssai = Snssai::new(1);
let marshaled = snssai.marshal();
let unmarshaled = Snssai::unmarshal(&marshaled).unwrap();
assert_eq!(snssai, unmarshaled);
assert_eq!(unmarshaled.sst, 1);
assert_eq!(unmarshaled.sd, None);
assert_eq!(marshaled, vec![1]);
assert_eq!(snssai.len(), 1);
}
#[test]
fn test_snssai_marshal_unmarshal_with_sd() {
let snssai = Snssai::with_sd(2, [0x12, 0x34, 0x56]);
let marshaled = snssai.marshal();
let unmarshaled = Snssai::unmarshal(&marshaled).unwrap();
assert_eq!(snssai, unmarshaled);
assert_eq!(unmarshaled.sst, 2);
assert_eq!(unmarshaled.sd, Some([0x12, 0x34, 0x56]));
assert_eq!(marshaled, vec![2, 0x12, 0x34, 0x56]);
assert_eq!(snssai.len(), 4);
}
#[test]
fn test_snssai_with_sd_u32() {
let snssai = Snssai::with_sd_u32(3, 0x123456);
assert_eq!(snssai.sst, 3);
assert_eq!(snssai.sd, Some([0x12, 0x34, 0x56]));
assert_eq!(snssai.sd_as_u32(), Some(0x123456));
let marshaled = snssai.marshal();
assert_eq!(marshaled, vec![3, 0x12, 0x34, 0x56]);
}
#[test]
fn test_snssai_sd_as_u32() {
let snssai1 = Snssai::new(1);
assert_eq!(snssai1.sd_as_u32(), None);
let snssai2 = Snssai::with_sd(2, [0xAB, 0xCD, 0xEF]);
assert_eq!(snssai2.sd_as_u32(), Some(0xABCDEF));
let snssai3 = Snssai::with_sd_u32(3, 0x000001);
assert_eq!(snssai3.sd_as_u32(), Some(0x000001));
}
#[test]
fn test_snssai_predefined_types() {
let embb = Snssai::embb();
assert_eq!(embb.sst, Snssai::SST_EMBB);
assert_eq!(embb.sd, None);
let urllc = Snssai::urllc();
assert_eq!(urllc.sst, Snssai::SST_URLLC);
assert_eq!(urllc.sd, None);
let miot = Snssai::miot();
assert_eq!(miot.sst, Snssai::SST_MIOT);
assert_eq!(miot.sd, None);
}
#[test]
fn test_snssai_predefined_types_with_sd() {
let embb_sd = Snssai::embb_with_sd(0x100001);
assert_eq!(embb_sd.sst, Snssai::SST_EMBB);
assert_eq!(embb_sd.sd_as_u32(), Some(0x100001));
let urllc_sd = Snssai::urllc_with_sd(0x200002);
assert_eq!(urllc_sd.sst, Snssai::SST_URLLC);
assert_eq!(urllc_sd.sd_as_u32(), Some(0x200002));
let miot_sd = Snssai::miot_with_sd(0x300003);
assert_eq!(miot_sd.sst, Snssai::SST_MIOT);
assert_eq!(miot_sd.sd_as_u32(), Some(0x300003));
}
#[test]
fn test_snssai_to_ie() {
let snssai = Snssai::with_sd_u32(2, 0x789ABC);
let ie = snssai.to_ie();
assert_eq!(ie.ie_type, IeType::Snssai);
let unmarshaled = Snssai::unmarshal(&ie.payload).unwrap();
assert_eq!(snssai, unmarshaled);
}
#[test]
fn test_snssai_unmarshal_invalid_length() {
let result = Snssai::unmarshal(&[1, 2]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(err, PfcpError::InvalidValue { .. }));
let result = Snssai::unmarshal(&[1, 2, 3]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(err, PfcpError::InvalidValue { .. }));
}
#[test]
fn test_snssai_unmarshal_empty() {
let result = Snssai::unmarshal(&[]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(err, PfcpError::InvalidLength { .. }));
}
#[test]
fn test_snssai_round_trip_various_values() {
let test_cases = vec![
Snssai::new(1),
Snssai::new(255),
Snssai::with_sd(1, [0x00, 0x00, 0x01]),
Snssai::with_sd(255, [0xFF, 0xFF, 0xFF]),
Snssai::with_sd_u32(128, 0x7F7F7F),
];
for original in test_cases {
let marshaled = original.marshal();
let unmarshaled = Snssai::unmarshal(&marshaled).unwrap();
assert_eq!(original, unmarshaled);
}
}
}