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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
//! Usage Report Within Session Modification Response IE implementation.
//!
//! Per 3GPP TS 29.244 Section 8.2.78, the Usage Report Within Session
//! Modification Response IE is used to report usage information in response
//! to a PFCP Session Modification Request.
//!
//! This IE has the same structure as Usage Report (IE 144) but uses IE type 78
//! to indicate it appears within a Session Modification Response message.
use crate::error::PfcpError;
use crate::ie::usage_report::UsageReport;
use crate::ie::{Ie, IeType};
/// Usage Report Within Session Modification Response Information Element.
///
/// This is a context-specific wrapper around the core UsageReport structure.
/// It contains the same fields and behavior as UsageReport, but with IE type 78
/// to indicate it appears in a Session Modification Response message.
///
/// # Structure
/// Identical to UsageReport (IE 144), containing:
/// - URR ID (mandatory)
/// - UR-SEQN (mandatory)
/// - Usage Report Trigger (mandatory)
/// - Plus optional measurement, quota, and extended IEs
///
/// # Example
/// ```
/// use rs_pfcp::ie::usage_report_smr::UsageReportSmr;
/// use rs_pfcp::ie::usage_report::UsageReportBuilder;
/// use rs_pfcp::ie::urr_id::UrrId;
/// use rs_pfcp::ie::sequence_number::SequenceNumber;
///
/// let usage_report = UsageReportBuilder::quota_exhausted_report(
/// UrrId::new(1),
/// SequenceNumber::new(42)
/// ).build().unwrap();
///
/// let smr_report = UsageReportSmr::new(usage_report);
/// let ie = smr_report.to_ie();
/// assert_eq!(ie.ie_type, rs_pfcp::ie::IeType::UsageReportWithinSessionModificationResponse);
/// ```
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UsageReportSmr {
/// The underlying usage report data
pub report: UsageReport,
}
impl UsageReportSmr {
/// Creates a new Usage Report Within Session Modification Response.
///
/// # Arguments
/// * `report` - The underlying UsageReport containing measurement data
///
/// # Example
/// ```
/// use rs_pfcp::ie::usage_report_smr::UsageReportSmr;
/// use rs_pfcp::ie::usage_report::UsageReport;
/// use rs_pfcp::ie::urr_id::UrrId;
/// use rs_pfcp::ie::sequence_number::SequenceNumber;
/// use rs_pfcp::ie::usage_report_trigger::UsageReportTrigger;
///
/// let report = UsageReport::new(
/// UrrId::new(1),
/// SequenceNumber::new(42),
/// UsageReportTrigger::PERIO
/// );
/// let smr_report = UsageReportSmr::new(report);
/// ```
pub fn new(report: UsageReport) -> Self {
UsageReportSmr { report }
}
/// Marshals the Usage Report Within Session Modification Response to bytes.
///
/// # Returns
/// A vector containing the marshaled usage report payload (child IEs).
pub fn marshal(&self) -> Vec<u8> {
self.report.marshal()
}
/// Unmarshals a Usage Report Within Session Modification Response from bytes.
///
/// # Arguments
/// * `data` - The byte slice containing the usage report payload
///
/// # Returns
/// A `UsageReportSmr` instance or an error if unmarshaling fails.
///
/// # Errors
/// Returns `PfcpError` if:
/// - Required fields (URR ID, UR-SEQN, Usage Report Trigger) are missing
/// - Any child IE cannot be parsed
pub fn unmarshal(data: &[u8]) -> Result<Self, PfcpError> {
Ok(UsageReportSmr {
report: UsageReport::unmarshal(data)?,
})
}
/// Wraps the Usage Report in a generic IE with type UsageReportWithinSessionModificationResponse.
///
/// # Returns
/// An `Ie` with type 78 and the marshaled payload.
pub fn to_ie(&self) -> Ie {
Ie::new(
IeType::UsageReportWithinSessionModificationResponse,
self.marshal(),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ie::sequence_number::SequenceNumber;
use crate::ie::urr_id::UrrId;
use crate::ie::usage_report::UsageReportBuilder;
use crate::ie::usage_report_trigger::UsageReportTrigger;
#[test]
fn test_usage_report_smr_new() {
let report = UsageReport::new(
UrrId::new(1),
SequenceNumber::new(42),
UsageReportTrigger::PERIO,
);
let smr_report = UsageReportSmr::new(report.clone());
assert_eq!(smr_report.report, report);
}
#[test]
fn test_usage_report_smr_marshal_unmarshal() {
let report =
UsageReportBuilder::quota_exhausted_report(UrrId::new(10), SequenceNumber::new(100))
.build()
.unwrap();
let smr_report = UsageReportSmr::new(report);
let marshaled = smr_report.marshal();
let unmarshaled = UsageReportSmr::unmarshal(&marshaled).unwrap();
assert_eq!(smr_report, unmarshaled);
}
#[test]
fn test_usage_report_smr_round_trip() {
let report =
UsageReportBuilder::periodic_usage_report(UrrId::new(5), SequenceNumber::new(50))
.build()
.unwrap();
let original = UsageReportSmr::new(report);
let marshaled = original.marshal();
let unmarshaled = UsageReportSmr::unmarshal(&marshaled).unwrap();
assert_eq!(original, unmarshaled);
}
#[test]
fn test_usage_report_smr_to_ie() {
let report = UsageReport::new(
UrrId::new(1),
SequenceNumber::new(1),
UsageReportTrigger::VOLTH,
);
let smr_report = UsageReportSmr::new(report);
let ie = smr_report.to_ie();
assert_eq!(
ie.ie_type,
IeType::UsageReportWithinSessionModificationResponse
);
assert_eq!(ie.payload, smr_report.marshal());
}
#[test]
fn test_usage_report_smr_ie_round_trip() {
let report =
UsageReportBuilder::volume_threshold_report(UrrId::new(7), SequenceNumber::new(77))
.build()
.unwrap();
let smr_report = UsageReportSmr::new(report);
let ie = smr_report.to_ie();
let unmarshaled = UsageReportSmr::unmarshal(&ie.payload).unwrap();
assert_eq!(smr_report, unmarshaled);
}
#[test]
fn test_usage_report_smr_edge_cases() {
// Test with minimal report
let minimal_report = UsageReport::new(
UrrId::new(0),
SequenceNumber::new(0),
UsageReportTrigger::new(0),
);
let smr = UsageReportSmr::new(minimal_report);
let marshaled = smr.marshal();
let unmarshaled = UsageReportSmr::unmarshal(&marshaled).unwrap();
assert_eq!(smr, unmarshaled);
// Test with maximum URR ID
let max_report = UsageReport::new(
UrrId::new(u32::MAX),
SequenceNumber::new(u32::MAX),
UsageReportTrigger::new(u8::MAX),
);
let smr = UsageReportSmr::new(max_report);
let marshaled = smr.marshal();
let unmarshaled = UsageReportSmr::unmarshal(&marshaled).unwrap();
assert_eq!(smr, unmarshaled);
}
#[test]
fn test_usage_report_smr_unmarshal_error_cases() {
// Empty payload
let result = UsageReportSmr::unmarshal(&[]);
assert!(result.is_err());
// Too short for even URR ID
let result = UsageReportSmr::unmarshal(&[0x00, 0x01]);
assert!(result.is_err());
}
#[test]
fn test_usage_report_smr_with_builder() {
let report = UsageReportBuilder::new(UrrId::new(99))
.sequence_number(SequenceNumber::new(255))
.quota_exhausted()
.with_volume_data(5000000, 3000000, 2000000)
.with_duration(3600)
.build()
.unwrap();
let smr = UsageReportSmr::new(report);
// Verify marshal/unmarshal preserves all data
let marshaled = smr.marshal();
let unmarshaled = UsageReportSmr::unmarshal(&marshaled).unwrap();
assert_eq!(smr, unmarshaled);
// Verify IE type is correct
let ie = smr.to_ie();
assert_eq!(
ie.ie_type,
IeType::UsageReportWithinSessionModificationResponse
);
}
}