use crate::error::PfcpError;
use crate::ie::{Ie, IeType};
use crate::message::{header::Header, Message, MsgType};
use crate::types::{Seid, SequenceNumber};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SessionSetDeletionRequest {
pub header: Header,
pub node_id: Ie, pub fseid_set: Option<Ie>, pub fq_csids: Vec<Ie>, pub ies: Vec<Ie>,
}
impl SessionSetDeletionRequest {
pub fn new(
seq: impl Into<SequenceNumber>,
node_id: Ie,
fseid_set: Option<Ie>,
fq_csids: Vec<Ie>,
ies: Vec<Ie>,
) -> Self {
let mut payload_len = node_id.len();
if let Some(ref ie) = fseid_set {
payload_len += ie.len();
}
for ie in &fq_csids {
payload_len += ie.len();
}
for ie in &ies {
payload_len += ie.len();
}
let mut header = Header::new(MsgType::SessionSetDeletionRequest, false, 0, seq);
header.length = 4 + payload_len;
SessionSetDeletionRequest {
header,
node_id,
fseid_set,
fq_csids,
ies,
}
}
}
#[derive(Debug, Default)]
pub struct SessionSetDeletionRequestBuilder {
sequence: SequenceNumber,
node_id: Option<Ie>,
fseid_set: Option<Ie>,
fq_csids: Vec<Ie>,
ies: Vec<Ie>,
}
impl SessionSetDeletionRequestBuilder {
pub fn new(sequence: impl Into<SequenceNumber>) -> Self {
Self {
sequence: sequence.into(),
node_id: None,
fseid_set: None,
fq_csids: Vec::new(),
ies: Vec::new(),
}
}
pub fn node_id(mut self, node_id: Ie) -> Self {
self.node_id = Some(node_id);
self
}
pub fn fseid_set(mut self, fseid_set: Ie) -> Self {
self.fseid_set = Some(fseid_set);
self
}
pub fn fq_csid(mut self, ie: Ie) -> Self {
self.fq_csids.push(ie);
self
}
pub fn additional_ies(mut self, mut ies: Vec<Ie>) -> Self {
self.ies.append(&mut ies);
self
}
pub fn add_ie(mut self, ie: Ie) -> Self {
self.ies.push(ie);
self
}
pub fn build(self) -> SessionSetDeletionRequest {
let node_id = self.node_id.expect("Node ID is required");
SessionSetDeletionRequest::new(
self.sequence,
node_id,
self.fseid_set,
self.fq_csids,
self.ies,
)
}
pub fn try_build(self) -> Result<SessionSetDeletionRequest, PfcpError> {
let node_id = self.node_id.ok_or_else(|| {
PfcpError::validation_error(
"SessionSetDeletionRequestBuilder",
"node_id",
"Node ID is required",
)
})?;
Ok(SessionSetDeletionRequest::new(
self.sequence,
node_id,
self.fseid_set,
self.fq_csids,
self.ies,
))
}
}
impl Message for SessionSetDeletionRequest {
fn msg_type(&self) -> MsgType {
MsgType::SessionSetDeletionRequest
}
fn marshal(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(self.marshaled_size());
self.marshal_into(&mut buf);
buf
}
fn marshal_into(&self, buf: &mut Vec<u8>) {
buf.reserve(self.marshaled_size());
self.header.marshal_into(buf);
self.node_id.marshal_into(buf);
if let Some(ref ie) = self.fseid_set {
ie.marshal_into(buf);
}
for ie in &self.fq_csids {
ie.marshal_into(buf);
}
for ie in &self.ies {
ie.marshal_into(buf);
}
}
fn marshaled_size(&self) -> usize {
let mut size = self.header.len() as usize;
size += self.node_id.len() as usize;
if let Some(ref ie) = self.fseid_set {
size += ie.len() as usize;
}
for ie in &self.fq_csids {
size += ie.len() as usize;
}
for ie in &self.ies {
size += ie.len() as usize;
}
size
}
fn unmarshal(buf: &[u8]) -> Result<Self, PfcpError>
where
Self: Sized,
{
let header = Header::unmarshal(buf)?;
let mut node_id = None;
let mut fseid_set = None;
let mut fq_csids = Vec::new();
let mut ies = Vec::new();
let mut cursor = header.len() as usize;
while cursor < buf.len() {
let ie = Ie::unmarshal(&buf[cursor..])?;
let ie_len = ie.len() as usize;
match ie.ie_type {
IeType::NodeId => node_id = Some(ie),
IeType::Fseid => fseid_set = Some(ie), IeType::FqCsid => fq_csids.push(ie),
_ => ies.push(ie),
}
cursor += ie_len;
}
let node_id = node_id.ok_or(PfcpError::MissingMandatoryIe {
ie_type: IeType::NodeId,
message_type: Some(MsgType::SessionSetDeletionRequest),
parent_ie: None,
})?;
Ok(SessionSetDeletionRequest {
header,
node_id,
fseid_set,
fq_csids,
ies,
})
}
fn seid(&self) -> Option<Seid> {
if self.header.has_seid {
Some(self.header.seid)
} else {
None
}
}
fn sequence(&self) -> SequenceNumber {
self.header.sequence_number
}
fn set_sequence(&mut self, seq: SequenceNumber) {
self.header.sequence_number = seq;
}
fn ies(&self, ie_type: IeType) -> crate::message::IeIter<'_> {
use crate::message::IeIter;
match ie_type {
IeType::NodeId => IeIter::single(Some(&self.node_id), ie_type),
IeType::Fseid => IeIter::single(self.fseid_set.as_ref(), ie_type),
IeType::FqCsid => IeIter::multiple(&self.fq_csids, ie_type),
_ => IeIter::generic(&self.ies, ie_type),
}
}
fn all_ies(&self) -> Vec<&Ie> {
let mut result = vec![&self.node_id];
if let Some(ref ie) = self.fseid_set {
result.push(ie);
}
result.extend(self.fq_csids.iter());
result.extend(self.ies.iter());
result
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ie::node_id::NodeId;
use std::net::Ipv4Addr;
#[test]
fn test_session_set_deletion_request_marshal_unmarshal() {
let node_id_ie = Ie::new(
IeType::NodeId,
NodeId::IPv4(Ipv4Addr::new(10, 0, 0, 1)).marshal().to_vec(),
);
let original =
SessionSetDeletionRequest::new(123, node_id_ie, None, Vec::new(), Vec::new());
let marshaled = original.marshal();
let unmarshaled = SessionSetDeletionRequest::unmarshal(&marshaled).unwrap();
assert_eq!(
original.header.message_type,
unmarshaled.header.message_type
);
assert_eq!(
original.header.sequence_number,
unmarshaled.header.sequence_number
);
assert_eq!(original.node_id, unmarshaled.node_id);
assert_eq!(original.fseid_set, unmarshaled.fseid_set);
}
#[test]
fn test_session_set_deletion_request_with_fseid() {
let node_id_ie = Ie::new(
IeType::NodeId,
NodeId::IPv4(Ipv4Addr::new(192, 168, 1, 1))
.marshal()
.to_vec(),
);
let fseid_data = {
let mut data = vec![0x01]; data.extend_from_slice(&0x1234567890ABCDEF_u64.to_be_bytes()); data.extend_from_slice(&Ipv4Addr::new(10, 0, 0, 100).octets()); data
};
let fseid_ie = Ie::new(IeType::Fseid, fseid_data);
let original =
SessionSetDeletionRequest::new(456, node_id_ie, Some(fseid_ie), Vec::new(), Vec::new());
let marshaled = original.marshal();
let unmarshaled = SessionSetDeletionRequest::unmarshal(&marshaled).unwrap();
assert_eq!(original, unmarshaled);
assert!(unmarshaled.fseid_set.is_some());
}
#[test]
fn test_session_set_deletion_request_with_additional_ies() {
let node_id_ie = Ie::new(
IeType::NodeId,
NodeId::IPv4(Ipv4Addr::new(172, 16, 0, 1))
.marshal()
.to_vec(),
);
let additional_ies = vec![
Ie::new(IeType::Timer, vec![0x00, 0x00, 0x0A, 0x00]), Ie::new(IeType::LoadControlInformation, vec![0x01, 0x02, 0x03]),
];
let original =
SessionSetDeletionRequest::new(789, node_id_ie, None, Vec::new(), additional_ies);
let marshaled = original.marshal();
let unmarshaled = SessionSetDeletionRequest::unmarshal(&marshaled).unwrap();
assert_eq!(original, unmarshaled);
assert_eq!(original.ies.len(), 2);
}
#[test]
fn test_session_set_deletion_request_missing_node_id() {
let incomplete_data = [
0x21, 0x0E, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, ];
let result = SessionSetDeletionRequest::unmarshal(&incomplete_data);
assert!(result.is_err());
}
#[test]
fn test_session_set_deletion_request_ies() {
let node_id_ie = Ie::new(
IeType::NodeId,
NodeId::IPv4(Ipv4Addr::new(10, 0, 0, 1)).marshal().to_vec(),
);
let message = SessionSetDeletionRequest::new(123, node_id_ie, None, Vec::new(), Vec::new());
assert!(message.ies(IeType::NodeId).next().is_some());
assert!(message.ies(IeType::Fseid).next().is_none());
assert!(message.ies(IeType::Unknown).next().is_none());
}
#[test]
fn test_session_set_deletion_request_header_validation() {
let node_id_ie = Ie::new(
IeType::NodeId,
NodeId::IPv4(Ipv4Addr::new(10, 0, 0, 1)).marshal().to_vec(),
);
let message = SessionSetDeletionRequest::new(999, node_id_ie, None, Vec::new(), Vec::new());
assert_eq!(message.msg_type(), MsgType::SessionSetDeletionRequest);
assert_eq!(*message.sequence(), 999);
assert_eq!(message.seid(), None); assert_eq!(message.version(), 1);
}
#[test]
fn test_session_set_deletion_request_round_trip() {
let node_id_ie = Ie::new(
IeType::NodeId,
NodeId::IPv4(Ipv4Addr::new(203, 0, 113, 1))
.marshal()
.to_vec(),
);
let original =
SessionSetDeletionRequest::new(888, node_id_ie, None, Vec::new(), Vec::new());
let marshaled = original.marshal();
let unmarshaled = SessionSetDeletionRequest::unmarshal(&marshaled).unwrap();
assert_eq!(original, unmarshaled);
}
#[test]
fn test_session_set_deletion_request_builder_basic() {
let node_id_ie = Ie::new(
IeType::NodeId,
NodeId::IPv4(Ipv4Addr::new(10, 0, 0, 1)).marshal().to_vec(),
);
let message = SessionSetDeletionRequestBuilder::new(123)
.node_id(node_id_ie.clone())
.build();
assert_eq!(*message.sequence(), 123);
assert_eq!(message.node_id, node_id_ie);
assert!(message.fseid_set.is_none());
assert!(message.ies.is_empty());
}
#[test]
fn test_session_set_deletion_request_builder_with_fseid() {
let node_id_ie = Ie::new(
IeType::NodeId,
NodeId::IPv4(Ipv4Addr::new(192, 168, 1, 1))
.marshal()
.to_vec(),
);
let fseid_data = {
let mut data = vec![0x01]; data.extend_from_slice(&0x1234567890ABCDEF_u64.to_be_bytes()); data.extend_from_slice(&Ipv4Addr::new(10, 0, 0, 100).octets()); data
};
let fseid_ie = Ie::new(IeType::Fseid, fseid_data);
let message = SessionSetDeletionRequestBuilder::new(456)
.node_id(node_id_ie.clone())
.fseid_set(fseid_ie.clone())
.build();
assert_eq!(*message.sequence(), 456);
assert_eq!(message.node_id, node_id_ie);
assert_eq!(message.fseid_set, Some(fseid_ie));
}
#[test]
fn test_session_set_deletion_request_builder_with_additional_ies() {
let node_id_ie = Ie::new(
IeType::NodeId,
NodeId::IPv4(Ipv4Addr::new(172, 16, 0, 1))
.marshal()
.to_vec(),
);
let additional_ies = vec![
Ie::new(IeType::Timer, vec![0x00, 0x00, 0x0A, 0x00]),
Ie::new(IeType::LoadControlInformation, vec![0x01, 0x02, 0x03]),
];
let message = SessionSetDeletionRequestBuilder::new(789)
.node_id(node_id_ie.clone())
.additional_ies(additional_ies.clone())
.build();
assert_eq!(*message.sequence(), 789);
assert_eq!(message.node_id, node_id_ie);
assert_eq!(message.ies, additional_ies);
}
#[test]
fn test_session_set_deletion_request_builder_add_ie() {
let node_id_ie = Ie::new(
IeType::NodeId,
NodeId::IPv4(Ipv4Addr::new(203, 0, 113, 1))
.marshal()
.to_vec(),
);
let timer_ie = Ie::new(IeType::Timer, vec![0x00, 0x00, 0x05, 0x00]);
let load_control_ie = Ie::new(IeType::LoadControlInformation, vec![0x04, 0x05]);
let message = SessionSetDeletionRequestBuilder::new(555)
.node_id(node_id_ie.clone())
.add_ie(timer_ie.clone())
.add_ie(load_control_ie.clone())
.build();
assert_eq!(*message.sequence(), 555);
assert_eq!(message.ies.len(), 2);
assert_eq!(message.ies[0], timer_ie);
assert_eq!(message.ies[1], load_control_ie);
}
#[test]
fn test_session_set_deletion_request_builder_full() {
let node_id_ie = Ie::new(
IeType::NodeId,
NodeId::IPv4(Ipv4Addr::new(198, 51, 100, 1))
.marshal()
.to_vec(),
);
let fseid_data = {
let mut data = vec![0x01]; data.extend_from_slice(&0xABCDEF1234567890_u64.to_be_bytes()); data.extend_from_slice(&Ipv4Addr::new(192, 0, 2, 1).octets()); data
};
let fseid_ie = Ie::new(IeType::Fseid, fseid_data);
let timer_ie = Ie::new(IeType::Timer, vec![0x00, 0x00, 0x1E, 0x00]);
let message = SessionSetDeletionRequestBuilder::new(777)
.node_id(node_id_ie.clone())
.fseid_set(fseid_ie.clone())
.add_ie(timer_ie.clone())
.build();
assert_eq!(*message.sequence(), 777);
assert_eq!(message.node_id, node_id_ie);
assert_eq!(message.fseid_set, Some(fseid_ie));
assert_eq!(message.ies.len(), 1);
assert_eq!(message.ies[0], timer_ie);
}
#[test]
#[should_panic(expected = "Node ID is required")]
fn test_session_set_deletion_request_builder_missing_node_id() {
SessionSetDeletionRequestBuilder::new(123).build();
}
#[test]
fn test_session_set_deletion_request_builder_try_build_success() {
let node_id_ie = Ie::new(
IeType::NodeId,
NodeId::IPv4(Ipv4Addr::new(10, 0, 0, 1)).marshal().to_vec(),
);
let result = SessionSetDeletionRequestBuilder::new(999)
.node_id(node_id_ie.clone())
.try_build();
assert!(result.is_ok());
let message = result.unwrap();
assert_eq!(*message.sequence(), 999);
assert_eq!(message.node_id, node_id_ie);
}
#[test]
fn test_session_set_deletion_request_builder_try_build_missing_node_id() {
let result = SessionSetDeletionRequestBuilder::new(123).try_build();
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
PfcpError::ValidationError { .. }
));
}
#[test]
fn test_session_set_deletion_request_builder_marshal_unmarshal() {
let node_id_ie = Ie::new(
IeType::NodeId,
NodeId::IPv4(Ipv4Addr::new(203, 0, 113, 100))
.marshal()
.to_vec(),
);
let original = SessionSetDeletionRequestBuilder::new(888)
.node_id(node_id_ie)
.build();
let marshaled = original.marshal();
let unmarshaled = SessionSetDeletionRequest::unmarshal(&marshaled).unwrap();
assert_eq!(original, unmarshaled);
}
}