1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
//! Graceful Release Period Information Element
//!
//! The Graceful Release Period IE specifies the period for graceful release of PFCP association.
//! Per 3GPP TS 29.244 Section 8.2.78.
use crate::error::PfcpError;
use crate::ie::{Ie, IeType};
/// Graceful Release Period
///
/// Specifies the grace period (in seconds) for the release of PFCP association.
/// Used during graceful shutdown of PFCP sessions.
///
/// # 3GPP Reference
/// 3GPP TS 29.244 Section 8.2.78
///
/// # Structure
/// - 2 bytes: Release period (u16, big-endian, seconds)
///
/// # Examples
///
/// ```
/// use rs_pfcp::ie::graceful_release_period::GracefulReleasePeriod;
///
/// // Create Graceful Release Period with 60 seconds
/// let period = GracefulReleasePeriod::new(60);
/// assert_eq!(period.period(), 60);
///
/// // Marshal and unmarshal
/// let bytes = period.marshal();
/// let parsed = GracefulReleasePeriod::unmarshal(&bytes)?;
/// assert_eq!(period, parsed);
/// # Ok::<(), rs_pfcp::error::PfcpError>(())
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GracefulReleasePeriod {
/// Release period in seconds (u16)
period: u16,
}
impl GracefulReleasePeriod {
/// Create a new Graceful Release Period
///
/// # Arguments
/// * `period` - Release period in seconds (0-65535)
///
/// # Example
/// ```
/// use rs_pfcp::ie::graceful_release_period::GracefulReleasePeriod;
///
/// let period = GracefulReleasePeriod::new(120);
/// assert_eq!(period.period(), 120);
/// ```
pub fn new(period: u16) -> Self {
GracefulReleasePeriod { period }
}
/// Get the release period value in seconds
///
/// # Example
/// ```
/// use rs_pfcp::ie::graceful_release_period::GracefulReleasePeriod;
///
/// let period = GracefulReleasePeriod::new(300);
/// assert_eq!(period.period(), 300);
/// ```
pub fn period(&self) -> u16 {
self.period
}
/// Marshal Graceful Release Period to bytes
///
/// # Returns
/// 2-byte vector containing release period (big-endian)
pub fn marshal(&self) -> Vec<u8> {
self.period.to_be_bytes().to_vec()
}
/// Unmarshal Graceful Release Period from bytes
///
/// # Arguments
/// * `data` - Byte slice containing release period (must be at least 2 bytes)
///
/// # Errors
/// Returns error if data is too short
///
/// # Example
/// ```
/// use rs_pfcp::ie::graceful_release_period::GracefulReleasePeriod;
///
/// let period = GracefulReleasePeriod::new(180);
/// let bytes = period.marshal();
/// let parsed = GracefulReleasePeriod::unmarshal(&bytes)?;
/// assert_eq!(period, parsed);
/// # Ok::<(), rs_pfcp::error::PfcpError>(())
/// ```
pub fn unmarshal(data: &[u8]) -> Result<Self, PfcpError> {
if data.len() < 2 {
return Err(PfcpError::invalid_length(
"Graceful Release Period",
IeType::GracefulReleasePeriod,
2,
data.len(),
));
}
let bytes: [u8; 2] = data[0..2].try_into().unwrap();
let period = u16::from_be_bytes(bytes);
Ok(GracefulReleasePeriod { period })
}
/// Convert to generic IE
///
/// # Example
/// ```
/// use rs_pfcp::ie::graceful_release_period::GracefulReleasePeriod;
/// use rs_pfcp::ie::IeType;
///
/// let period = GracefulReleasePeriod::new(100);
/// let ie = period.to_ie();
/// assert_eq!(ie.ie_type, IeType::GracefulReleasePeriod);
/// ```
pub fn to_ie(&self) -> Ie {
Ie::new(IeType::GracefulReleasePeriod, self.marshal())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_graceful_release_period_new() {
let period = GracefulReleasePeriod::new(60);
assert_eq!(period.period(), 60);
}
#[test]
fn test_graceful_release_period_marshal_unmarshal() {
let original = GracefulReleasePeriod::new(300);
let bytes = original.marshal();
assert_eq!(bytes.len(), 2);
let parsed = GracefulReleasePeriod::unmarshal(&bytes).unwrap();
assert_eq!(original, parsed);
assert_eq!(parsed.period(), 300);
}
#[test]
fn test_graceful_release_period_marshal_zero() {
let period = GracefulReleasePeriod::new(0);
let bytes = period.marshal();
let parsed = GracefulReleasePeriod::unmarshal(&bytes).unwrap();
assert_eq!(period, parsed);
assert_eq!(parsed.period(), 0);
}
#[test]
fn test_graceful_release_period_marshal_max_value() {
let period = GracefulReleasePeriod::new(u16::MAX);
let bytes = period.marshal();
let parsed = GracefulReleasePeriod::unmarshal(&bytes).unwrap();
assert_eq!(period, parsed);
assert_eq!(parsed.period(), u16::MAX);
}
#[test]
fn test_graceful_release_period_unmarshal_short() {
let data = vec![0x00]; // Only 1 byte
let result = GracefulReleasePeriod::unmarshal(&data);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(err, PfcpError::InvalidLength { .. }));
}
#[test]
fn test_graceful_release_period_unmarshal_empty() {
let data = vec![];
let result = GracefulReleasePeriod::unmarshal(&data);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(err, PfcpError::InvalidLength { .. }));
}
#[test]
fn test_graceful_release_period_to_ie() {
let period = GracefulReleasePeriod::new(500);
let ie = period.to_ie();
assert_eq!(ie.ie_type, IeType::GracefulReleasePeriod);
assert_eq!(ie.payload.len(), 2);
// Verify IE can be unmarshaled
let parsed = GracefulReleasePeriod::unmarshal(&ie.payload).unwrap();
assert_eq!(period, parsed);
}
#[test]
fn test_graceful_release_period_byte_order() {
// Verify big-endian encoding
let period = GracefulReleasePeriod::new(0x1234);
let bytes = period.marshal();
assert_eq!(bytes, vec![0x12, 0x34]);
}
#[test]
fn test_graceful_release_period_round_trip_various() {
let values = vec![1, 10, 60, 300, 3600, 65535];
for period_val in values {
let original = GracefulReleasePeriod::new(period_val);
let bytes = original.marshal();
let parsed = GracefulReleasePeriod::unmarshal(&bytes).unwrap();
assert_eq!(original, parsed, "Failed for period {}", period_val);
}
}
#[test]
fn test_graceful_release_period_5g_graceful_shutdown() {
// Scenario: PFCP association graceful shutdown
let period = GracefulReleasePeriod::new(30);
let bytes = period.marshal();
let parsed = GracefulReleasePeriod::unmarshal(&bytes).unwrap();
assert_eq!(parsed.period(), 30);
assert_eq!(period, parsed);
}
}