use crate::error::PfcpError;
use crate::ie::fseid::Fseid;
use crate::ie::node_id::NodeId;
use crate::ie::{Ie, IeType};
use crate::message::{header::Header, Message, MsgType};
use crate::types::{Seid, SequenceNumber};
#[derive(Debug, PartialEq)]
pub struct SessionDeletionRequest {
pub header: Header,
pub tl_container: Vec<Ie>, pub node_id: Option<Ie>, pub cp_fseid: Option<Ie>, pub ies: Vec<Ie>, }
impl Message for SessionDeletionRequest {
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);
for ie in &self.tl_container {
ie.marshal_into(buf);
}
if let Some(ref ie) = self.node_id {
ie.marshal_into(buf);
}
if let Some(ref ie) = self.cp_fseid {
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;
for ie in &self.tl_container {
size += ie.len() as usize;
}
if let Some(ref ie) = self.node_id {
size += ie.len() as usize;
}
if let Some(ref ie) = self.cp_fseid {
size += ie.len() as usize;
}
for ie in &self.ies {
size += ie.len() as usize;
}
size
}
fn unmarshal(data: &[u8]) -> Result<Self, PfcpError> {
let header = Header::unmarshal(data)?;
let mut cursor = header.len() as usize;
let mut tl_container: Vec<Ie> = Vec::new();
let mut node_id: Option<Ie> = None;
let mut cp_fseid: Option<Ie> = None;
let mut ies: Vec<Ie> = Vec::new();
while cursor < data.len() {
let ie = Ie::unmarshal(&data[cursor..])?;
let ie_len = ie.len() as usize;
match ie.ie_type {
IeType::NodeId => node_id = Some(ie),
IeType::Fseid => cp_fseid = Some(ie),
IeType::TlContainer => tl_container.push(ie),
_ => ies.push(ie),
}
cursor += ie_len;
}
Ok(SessionDeletionRequest {
header,
tl_container,
node_id,
cp_fseid,
ies,
})
}
fn msg_type(&self) -> MsgType {
MsgType::SessionDeletionRequest
}
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(self.node_id.as_ref(), ie_type),
IeType::Fseid => IeIter::single(self.cp_fseid.as_ref(), ie_type),
_ => IeIter::generic(&self.ies, ie_type),
}
}
fn all_ies(&self) -> Vec<&Ie> {
let mut result = Vec::new();
result.extend(self.tl_container.iter());
if let Some(ref ie) = self.node_id {
result.push(ie);
}
if let Some(ref ie) = self.cp_fseid {
result.push(ie);
}
result.extend(self.ies.iter());
result
}
}
impl SessionDeletionRequest {
pub fn new(
seid: impl Into<Seid>,
seq: impl Into<SequenceNumber>,
tl_container: Vec<Ie>,
node_id: Option<Ie>,
cp_fseid: Option<Ie>,
ies: Vec<Ie>,
) -> Self {
let mut header = Header::new(MsgType::SessionDeletionRequest, true, seid, seq);
let mut payload_len = 0;
for ie in &tl_container {
payload_len += ie.len();
}
if let Some(ie) = &node_id {
payload_len += ie.len();
}
if let Some(ie) = &cp_fseid {
payload_len += ie.len();
}
for ie in &ies {
payload_len += ie.len();
}
header.length = payload_len + header.len() - 4;
SessionDeletionRequest {
header,
tl_container,
node_id,
cp_fseid,
ies,
}
}
pub fn node_id(&self) -> Option<Result<NodeId, PfcpError>> {
self.node_id
.as_ref()
.map(|ie| NodeId::unmarshal(&ie.payload))
}
pub fn cp_fseid(&self) -> Option<Result<Fseid, PfcpError>> {
self.cp_fseid
.as_ref()
.map(|ie| Fseid::unmarshal(&ie.payload))
}
}
#[derive(Debug, Default)]
pub struct SessionDeletionRequestBuilder {
seid: Seid,
sequence: SequenceNumber,
tl_container: Vec<Ie>,
node_id: Option<Ie>,
cp_fseid: Option<Ie>,
ies: Vec<Ie>,
}
impl SessionDeletionRequestBuilder {
pub fn new(seid: impl Into<Seid>, sequence: impl Into<SequenceNumber>) -> Self {
Self {
seid: seid.into(),
sequence: sequence.into(),
tl_container: Vec::new(),
node_id: None,
cp_fseid: None,
ies: Vec::new(),
}
}
pub fn tl_container(mut self, tl_container: Ie) -> Self {
self.tl_container.push(tl_container);
self
}
pub fn tl_containers(mut self, mut tl_containers: Vec<Ie>) -> Self {
self.tl_container.append(&mut tl_containers);
self
}
pub fn node_id<T>(mut self, node_id: T) -> Self
where
T: Into<std::net::IpAddr>,
{
use crate::ie::node_id::NodeId;
let ip_addr = node_id.into();
let node = match ip_addr {
std::net::IpAddr::V4(v4) => NodeId::new_ipv4(v4),
std::net::IpAddr::V6(v6) => NodeId::new_ipv6(v6),
};
self.node_id = Some(node.to_ie());
self
}
pub fn node_id_fqdn(mut self, fqdn: &str) -> Self {
use crate::ie::node_id::NodeId;
let node = NodeId::new_fqdn(fqdn);
self.node_id = Some(node.to_ie());
self
}
pub fn node_id_ie(mut self, node_id: Ie) -> Self {
self.node_id = Some(node_id);
self
}
pub fn cp_fseid<T>(mut self, seid: impl Into<Seid>, ip_addr: T) -> Self
where
T: Into<std::net::IpAddr>,
{
use crate::ie::fseid::Fseid;
use crate::ie::IeType;
let ip_addr = ip_addr.into();
let fseid = match ip_addr {
std::net::IpAddr::V4(v4) => Fseid::new(seid, Some(v4), None),
std::net::IpAddr::V6(v6) => Fseid::new(seid, None, Some(v6)),
};
self.cp_fseid = Some(crate::ie::Ie::new(IeType::Fseid, fseid.marshal()));
self
}
pub fn cp_fseid_ie(mut self, cp_fseid: Ie) -> Self {
self.cp_fseid = Some(cp_fseid);
self
}
pub fn ie(mut self, ie: Ie) -> Self {
self.ies.push(ie);
self
}
pub fn ies(mut self, mut ies: Vec<Ie>) -> Self {
self.ies.append(&mut ies);
self
}
pub fn build(self) -> SessionDeletionRequest {
SessionDeletionRequest::new(
self.seid,
self.sequence,
self.tl_container,
self.node_id,
self.cp_fseid,
self.ies,
)
}
pub fn marshal(self) -> Vec<u8> {
self.build().marshal()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ie::fseid::Fseid;
use std::net::Ipv4Addr;
#[test]
fn test_session_deletion_request_builder_minimal() {
let request = SessionDeletionRequestBuilder::new(12345, 67890).build();
assert_eq!(*request.sequence(), 67890);
assert_eq!(request.seid(), Some(Seid(12345)));
assert_eq!(request.msg_type(), MsgType::SessionDeletionRequest);
assert!(request.tl_container.is_empty());
assert!(request.node_id.is_none());
assert!(request.cp_fseid.is_none());
assert!(request.ies.is_empty());
}
#[test]
fn test_session_deletion_request_builder_with_node_id() {
let node_id = NodeId::new_ipv4(Ipv4Addr::new(10, 0, 0, 1));
let node_id_ie = Ie::new(IeType::NodeId, node_id.marshal());
let request = SessionDeletionRequestBuilder::new(11111, 22222)
.node_id_ie(node_id_ie.clone())
.build();
assert_eq!(*request.sequence(), 22222);
assert_eq!(request.seid(), Some(Seid(11111)));
assert_eq!(request.node_id, Some(node_id_ie));
assert!(request.cp_fseid.is_none());
assert!(request.tl_container.is_empty());
}
#[test]
fn test_session_deletion_request_builder_with_cp_fseid() {
let cp_fseid = Fseid::new(456, Some(Ipv4Addr::new(192, 168, 1, 2)), None);
let cp_fseid_ie = Ie::new(IeType::Fseid, cp_fseid.marshal());
let request = SessionDeletionRequestBuilder::new(33333, 44444)
.cp_fseid_ie(cp_fseid_ie.clone())
.build();
assert_eq!(*request.sequence(), 44444);
assert_eq!(request.seid(), Some(Seid(33333)));
assert_eq!(request.cp_fseid, Some(cp_fseid_ie));
assert!(request.node_id.is_none());
assert!(request.tl_container.is_empty());
}
#[test]
fn test_session_deletion_request_builder_with_tl_container() {
let tl_container1 = Ie::new(IeType::Unknown, vec![0x01, 0x02, 0x03]);
let tl_container2 = Ie::new(IeType::Unknown, vec![0x04, 0x05, 0x06]);
let request = SessionDeletionRequestBuilder::new(55555, 66666)
.tl_container(tl_container1.clone())
.tl_containers(vec![tl_container2.clone()])
.build();
assert_eq!(*request.sequence(), 66666);
assert_eq!(request.seid(), Some(Seid(55555)));
assert_eq!(request.tl_container.len(), 2);
assert_eq!(request.tl_container[0], tl_container1);
assert_eq!(request.tl_container[1], tl_container2);
assert!(request.node_id.is_none());
assert!(request.cp_fseid.is_none());
}
#[test]
fn test_session_deletion_request_builder_with_additional_ies() {
let ie1 = Ie::new(IeType::Unknown, vec![0xAA, 0xBB]);
let ie2 = Ie::new(IeType::Unknown, vec![0xCC, 0xDD]);
let request = SessionDeletionRequestBuilder::new(12121, 34343)
.ie(ie1.clone())
.ies(vec![ie2.clone()])
.build();
assert_eq!(*request.sequence(), 34343);
assert_eq!(request.seid(), Some(Seid(12121)));
assert_eq!(request.ies.len(), 2);
assert_eq!(request.ies[0], ie1);
assert_eq!(request.ies[1], ie2);
}
#[test]
fn test_session_deletion_request_builder_full() {
let node_id = NodeId::new_ipv4(Ipv4Addr::new(10, 0, 0, 1));
let node_id_ie = Ie::new(IeType::NodeId, node_id.marshal());
let cp_fseid = Fseid::new(456, Some(Ipv4Addr::new(192, 168, 1, 2)), None);
let cp_fseid_ie = Ie::new(IeType::Fseid, cp_fseid.marshal());
let tl_container = Ie::new(IeType::Unknown, vec![0x01, 0x02, 0x03]);
let additional_ie = Ie::new(IeType::Unknown, vec![0x12, 0x34]);
let request = SessionDeletionRequestBuilder::new(0xABCD, 0x1234)
.tl_container(tl_container.clone())
.node_id_ie(node_id_ie.clone())
.cp_fseid_ie(cp_fseid_ie.clone())
.ie(additional_ie.clone())
.build();
assert_eq!(*request.sequence(), 0x1234);
assert_eq!(request.seid(), Some(Seid(0xABCD)));
assert_eq!(request.tl_container.len(), 1);
assert_eq!(request.tl_container[0], tl_container);
assert_eq!(request.node_id, Some(node_id_ie));
assert_eq!(request.cp_fseid, Some(cp_fseid_ie));
assert_eq!(request.ies.len(), 1);
assert_eq!(request.ies[0], additional_ie);
}
#[test]
fn test_session_deletion_request_builder_roundtrip() {
let node_id = NodeId::new_ipv4(Ipv4Addr::new(172, 16, 0, 2));
let node_id_ie = Ie::new(IeType::NodeId, node_id.marshal());
let cp_fseid = Fseid::new(789, Some(Ipv4Addr::new(172, 16, 0, 1)), None);
let cp_fseid_ie = Ie::new(IeType::Fseid, cp_fseid.marshal());
let original = SessionDeletionRequestBuilder::new(54321, 98765)
.node_id_ie(node_id_ie)
.cp_fseid_ie(cp_fseid_ie)
.build();
let marshaled = original.marshal();
let unmarshaled = SessionDeletionRequest::unmarshal(&marshaled).unwrap();
assert_eq!(original, unmarshaled);
}
#[test]
fn test_session_deletion_request_marshal_minimal() {
let request = SessionDeletionRequestBuilder::new(0x123456, 42).build();
let marshaled = request.marshal();
let parsed = SessionDeletionRequest::unmarshal(&marshaled).unwrap();
assert_eq!(parsed.seid(), Some(Seid(0x123456)));
assert_eq!(*parsed.sequence(), 42);
assert!(parsed.tl_container.is_empty());
assert!(parsed.node_id.is_none());
assert!(parsed.cp_fseid.is_none());
}
}