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 AssociationReleaseResponse {
header: Header,
node_id: Ie, cause: Ie, }
impl AssociationReleaseResponse {
pub fn new(seq: impl Into<SequenceNumber>, cause: Ie, node_id: Ie) -> Self {
let mut header = Header::new(MsgType::AssociationReleaseResponse, false, 0, seq);
header.length = cause.len() + node_id.len() + (header.len() - 4);
AssociationReleaseResponse {
header,
cause,
node_id,
}
}
pub fn node_id(&self) -> Result<crate::ie::node_id::NodeId, PfcpError> {
crate::ie::node_id::NodeId::unmarshal(&self.node_id.payload)
}
pub fn cause(&self) -> Result<crate::ie::cause::Cause, PfcpError> {
crate::ie::cause::Cause::unmarshal(&self.cause.payload)
}
pub fn node_id_ie(&self) -> &Ie {
&self.node_id
}
pub fn cause_ie(&self) -> &Ie {
&self.cause
}
}
impl Message for AssociationReleaseResponse {
fn msg_type(&self) -> MsgType {
MsgType::AssociationReleaseResponse
}
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.cause.marshal_into(buf);
self.node_id.marshal_into(buf);
}
fn marshaled_size(&self) -> usize {
let mut size = self.header.len() as usize;
size += self.cause.len() as usize;
size += self.node_id.len() as usize;
size
}
fn unmarshal(buf: &[u8]) -> Result<Self, PfcpError>
where
Self: Sized,
{
let header = Header::unmarshal(buf)?;
let mut cause = None;
let mut node_id = None;
let mut offset = header.len() as usize;
while offset < buf.len() {
let ie = Ie::unmarshal(&buf[offset..])?;
let ie_len = ie.len() as usize;
match ie.ie_type {
IeType::Cause => cause = Some(ie),
IeType::NodeId => node_id = Some(ie),
_ => {} }
offset += ie_len;
}
Ok(AssociationReleaseResponse {
header,
cause: cause.ok_or(PfcpError::MissingMandatoryIe {
ie_type: IeType::Cause,
message_type: Some(MsgType::AssociationReleaseResponse),
parent_ie: None,
})?,
node_id: node_id.ok_or(PfcpError::MissingMandatoryIe {
ie_type: IeType::NodeId,
message_type: Some(MsgType::AssociationReleaseResponse),
parent_ie: None,
})?,
})
}
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::Cause => IeIter::single(Some(&self.cause), ie_type),
_ => IeIter::single(None, ie_type),
}
}
fn all_ies(&self) -> Vec<&Ie> {
vec![&self.cause, &self.node_id]
}
}
#[derive(Debug, Default)]
pub struct AssociationReleaseResponseBuilder {
sequence: SequenceNumber,
cause: Option<Ie>,
node_id: Option<Ie>,
}
impl AssociationReleaseResponseBuilder {
pub fn new(sequence: impl Into<SequenceNumber>) -> Self {
Self {
sequence: sequence.into(),
cause: None,
node_id: None,
}
}
pub fn cause(mut self, cause_value: crate::ie::cause::CauseValue) -> Self {
use crate::ie::cause::Cause;
use crate::ie::{Ie, IeType};
let cause = Cause::new(cause_value);
self.cause = Some(Ie::new(IeType::Cause, cause.marshal().to_vec()));
self
}
pub fn cause_accepted(self) -> Self {
self.cause(crate::ie::cause::CauseValue::RequestAccepted)
}
pub fn cause_rejected(self) -> Self {
self.cause(crate::ie::cause::CauseValue::RequestRejected)
}
pub fn cause_ie(mut self, cause: Ie) -> Self {
self.cause = Some(cause);
self
}
pub fn node_id(mut self, node_id: Ie) -> Self {
self.node_id = Some(node_id);
self
}
pub fn build(self) -> AssociationReleaseResponse {
let cause = self
.cause
.expect("Cause IE is required for AssociationReleaseResponse");
let node_id = self
.node_id
.expect("Node ID IE is required for AssociationReleaseResponse");
AssociationReleaseResponse::new(self.sequence, cause, node_id)
}
pub fn try_build(self) -> Result<AssociationReleaseResponse, &'static str> {
let cause = self
.cause
.ok_or("Cause IE is required for AssociationReleaseResponse")?;
let node_id = self
.node_id
.ok_or("Node ID IE is required for AssociationReleaseResponse")?;
Ok(AssociationReleaseResponse::new(
self.sequence,
cause,
node_id,
))
}
pub fn marshal(self) -> Vec<u8> {
self.build().marshal()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ie::{cause::*, node_id::NodeId};
use std::net::Ipv4Addr;
#[test]
fn test_association_release_response_builder() {
let cause = Cause::new(CauseValue::RequestAccepted);
let cause_ie = Ie::new(IeType::Cause, cause.marshal().to_vec());
let node_id = NodeId::new_ipv4(Ipv4Addr::new(192, 168, 1, 1));
let node_ie = Ie::new(IeType::NodeId, node_id.marshal());
let response = AssociationReleaseResponseBuilder::new(12345)
.cause_ie(cause_ie.clone())
.node_id(node_ie.clone())
.build();
assert_eq!(*response.sequence(), 12345);
assert_eq!(response.msg_type(), MsgType::AssociationReleaseResponse);
assert_eq!(response.cause_ie(), &cause_ie);
assert_eq!(response.node_id_ie(), &node_ie);
}
#[test]
fn test_association_release_response_builder_try_build_success() {
let cause = Cause::new(CauseValue::RequestAccepted);
let cause_ie = Ie::new(IeType::Cause, cause.marshal().to_vec());
let node_id = NodeId::new_ipv4(Ipv4Addr::new(192, 168, 1, 1));
let node_ie = Ie::new(IeType::NodeId, node_id.marshal());
let result = AssociationReleaseResponseBuilder::new(12345)
.cause_ie(cause_ie.clone())
.node_id(node_ie.clone())
.try_build();
assert!(result.is_ok());
let response = result.unwrap();
assert_eq!(*response.sequence(), 12345);
assert_eq!(response.cause_ie(), &cause_ie);
assert_eq!(response.node_id_ie(), &node_ie);
}
#[test]
fn test_association_release_response_builder_try_build_missing_cause() {
let node_id = NodeId::new_ipv4(Ipv4Addr::new(192, 168, 1, 1));
let node_ie = Ie::new(IeType::NodeId, node_id.marshal());
let result = AssociationReleaseResponseBuilder::new(12345)
.node_id(node_ie)
.try_build();
assert!(result.is_err());
assert_eq!(
result.unwrap_err(),
"Cause IE is required for AssociationReleaseResponse"
);
}
#[test]
fn test_association_release_response_builder_try_build_missing_node_id() {
let cause = Cause::new(CauseValue::RequestAccepted);
let cause_ie = Ie::new(IeType::Cause, cause.marshal().to_vec());
let result = AssociationReleaseResponseBuilder::new(12345)
.cause_ie(cause_ie)
.try_build();
assert!(result.is_err());
assert_eq!(
result.unwrap_err(),
"Node ID IE is required for AssociationReleaseResponse"
);
}
#[test]
#[should_panic(expected = "Cause IE is required for AssociationReleaseResponse")]
fn test_association_release_response_builder_build_panic_missing_cause() {
let node_id = NodeId::new_ipv4(Ipv4Addr::new(192, 168, 1, 1));
let node_ie = Ie::new(IeType::NodeId, node_id.marshal());
AssociationReleaseResponseBuilder::new(12345)
.node_id(node_ie)
.build();
}
#[test]
#[should_panic(expected = "Node ID IE is required for AssociationReleaseResponse")]
fn test_association_release_response_builder_build_panic_missing_node_id() {
let cause = Cause::new(CauseValue::RequestAccepted);
let cause_ie = Ie::new(IeType::Cause, cause.marshal().to_vec());
AssociationReleaseResponseBuilder::new(12345)
.cause_ie(cause_ie)
.build();
}
#[test]
fn test_association_release_response_roundtrip_via_builder() {
let cause = Cause::new(CauseValue::RequestRejected);
let cause_ie = Ie::new(IeType::Cause, cause.marshal().to_vec());
let node_id = NodeId::new_ipv4(Ipv4Addr::new(10, 0, 0, 1));
let node_ie = Ie::new(IeType::NodeId, node_id.marshal());
let original = AssociationReleaseResponseBuilder::new(98765)
.cause_ie(cause_ie)
.node_id(node_ie)
.build();
let marshaled = original.marshal();
let unmarshaled = AssociationReleaseResponse::unmarshal(&marshaled).unwrap();
assert_eq!(original, unmarshaled);
}
}