1use crate::usp::Msg;
2use crate::usp_record::mod_MQTTConnectRecord::MQTTVersion;
3use crate::usp_record::mod_Record::OneOfrecord_type;
4use crate::usp_record::mod_SessionContextRecord::PayloadSARState;
5use crate::usp_record::NoSessionContextRecord;
6use crate::usp_record::Record;
7use crate::usp_record::SessionContextRecord;
8use crate::usp_record::{
9 DisconnectRecord, MQTTConnectRecord, STOMPConnectRecord, UDSConnectRecord,
10 WebSocketConnectRecord,
11};
12use crate::usp_types::PayloadSecurity;
13
14use anyhow::anyhow;
15use anyhow::{Context, Result};
16
17#[derive(Clone)]
18enum RecordType {
19 None,
20 NoSessionContext,
21 SessionContext {
22 session_context: SessionContextBuilder,
23 },
24 WebSocketConnect,
25 MQTTConnect {
26 version: MQTTVersion,
27 subscribed_topic: String,
28 },
29 STOMPConnect {
30 version: crate::usp_record::mod_STOMPConnectRecord::STOMPVersion,
31 subscribed_destination: String,
32 },
33 Disconnect {
34 reason: String,
35 reason_code: u32,
36 },
37 UDSConnect,
38}
39
40#[derive(Clone)]
41pub struct SessionContextBuilder {
42 session_id: Option<u64>,
43 sequence_id: Option<u64>,
44 expected_id: Option<u64>,
45 retransmit_id: u64,
46 payload: Option<Vec<u8>>,
47}
48
49impl SessionContextBuilder {
50 #[must_use]
51 pub const fn new() -> Self {
52 Self {
53 session_id: None,
54 sequence_id: None,
55 expected_id: None,
56 retransmit_id: 0,
57 payload: None,
58 }
59 }
60
61 #[must_use]
62 pub const fn with_session_id(mut self, session_id: u64) -> Self {
63 self.session_id = Some(session_id);
64 self
65 }
66
67 #[must_use]
68 pub const fn with_sequence_id(mut self, sequence_id: u64) -> Self {
69 self.sequence_id = Some(sequence_id);
70 self
71 }
72
73 #[must_use]
74 pub const fn with_expected_id(mut self, expected_id: u64) -> Self {
75 self.expected_id = Some(expected_id);
76 self
77 }
78
79 #[must_use]
80 pub const fn with_retransmit_id(mut self, retransmit_id: u64) -> Self {
81 self.retransmit_id = retransmit_id;
82 self
83 }
84
85 #[must_use]
86 pub fn with_payload(mut self, payload: Vec<u8>) -> Self {
87 self.payload = Some(payload);
88 self
89 }
90
91 pub fn build(self) -> Result<SessionContextRecord> {
92 let scr = SessionContextRecord {
93 session_id: self
94 .session_id
95 .ok_or_else(|| anyhow!("Need to supply a session ID for a session context"))?,
96 sequence_id: self
97 .sequence_id
98 .ok_or_else(|| anyhow!("Need to supply a sequence ID for a session context"))?,
99 expected_id: self
100 .expected_id
101 .ok_or_else(|| anyhow!("Need to supply a expected ID for a session context"))?,
102 retransmit_id: self.retransmit_id,
103 payload_sar_state: PayloadSARState::NONE,
105 payloadrec_sar_state: PayloadSARState::NONE,
107 payload: self.payload.map_or_else(Vec::new, |payload| vec![payload]),
108 };
109
110 Ok(scr)
111 }
112}
113
114#[derive(Clone)]
115pub struct RecordBuilder {
116 version: String,
117 to_id: Option<String>,
118 from_id: Option<String>,
119 sender_cert: Vec<u8>,
120 mac_signature: Vec<u8>,
121 payload_security: PayloadSecurity,
122 payload: Option<Vec<u8>>,
123 typ: RecordType,
124}
125
126impl RecordBuilder {
127 #[must_use]
128 pub const fn new() -> Self {
129 Self {
130 version: String::new(),
131 to_id: None,
132 from_id: None,
133 sender_cert: vec![],
134 mac_signature: vec![],
135 payload_security: PayloadSecurity::PLAINTEXT,
136 payload: None,
137 typ: RecordType::None,
138 }
139 }
140
141 #[must_use]
142 pub fn with_version(mut self, version: String) -> Self {
143 self.version = version;
144 self
145 }
146
147 #[must_use]
148 pub fn with_to_id(mut self, id: String) -> Self {
149 self.to_id = Some(id);
150 self
151 }
152
153 #[must_use]
154 pub fn with_from_id(mut self, id: String) -> Self {
155 self.from_id = Some(id);
156 self
157 }
158
159 #[must_use]
160 pub fn with_sender_cert(mut self, sender_cert: Vec<u8>) -> Self {
161 self.sender_cert = sender_cert;
162 self
163 }
164
165 #[must_use]
166 pub fn with_mac_signature(mut self, mac_signature: Vec<u8>) -> Self {
167 self.mac_signature = mac_signature;
168 self
169 }
170
171 #[must_use]
172 pub fn with_payload_security_tls12(mut self) -> Self {
173 self.payload_security = PayloadSecurity::TLS12;
174 self
175 }
176
177 #[must_use]
178 pub fn with_session_context_builder(mut self, session_context: SessionContextBuilder) -> Self {
179 self.typ = RecordType::SessionContext { session_context };
180 self
181 }
182
183 #[must_use]
184 pub fn with_no_session_context_payload(self, msg: &Msg) -> Self {
185 let mut buf = Vec::new();
186 let mut writer = quick_protobuf::Writer::new(&mut buf);
187 let _ = quick_protobuf::MessageWrite::write_message(msg, &mut writer);
188 self.with_no_session_context_payload_bytes(buf)
189 }
190
191 #[must_use]
192 pub fn with_no_session_context_payload_bytes(mut self, buf: Vec<u8>) -> Self {
193 self.payload = Some(buf);
194 self.typ = RecordType::NoSessionContext;
195 self
196 }
197
198 #[must_use]
199 pub fn as_websocket_connect_record(mut self) -> Self {
200 self.typ = RecordType::WebSocketConnect;
201 self
202 }
203
204 #[must_use]
205 pub fn as_mqtt_connect_record(
206 mut self,
207 version: MQTTVersion,
208 subscribed_topic: String,
209 ) -> Self {
210 self.typ = RecordType::MQTTConnect {
211 version,
212 subscribed_topic,
213 };
214 self
215 }
216
217 #[must_use]
218 pub fn as_stomp_connect_record(
219 mut self,
220 version: crate::usp_record::mod_STOMPConnectRecord::STOMPVersion,
221 subscribed_destination: String,
222 ) -> Self {
223 self.typ = RecordType::STOMPConnect {
224 version,
225 subscribed_destination,
226 };
227 self
228 }
229
230 #[must_use]
231 pub fn as_disconnect_record(mut self, reason: String, reason_code: u32) -> Self {
232 self.typ = RecordType::Disconnect {
233 reason,
234 reason_code,
235 };
236 self
237 }
238
239 #[must_use]
240 pub fn as_uds_connect_record(mut self) -> Self {
241 self.typ = RecordType::UDSConnect;
242 self
243 }
244
245 pub fn build(self) -> Result<Record> {
246 let to_id = self
247 .to_id
248 .with_context(|| "Cannot produce USP Record without to_id")?;
249 let from_id = self
250 .from_id
251 .with_context(|| "Cannot produce USP Record without from_id")?;
252
253 let mut record = Record {
254 version: if self.version.is_empty() {
255 "1.3".into()
256 } else {
257 self.version
258 },
259 to_id,
260 from_id,
261 sender_cert: self.sender_cert,
262 mac_signature: self.mac_signature,
263 payload_security: self.payload_security,
264 record_type: OneOfrecord_type::None,
265 };
266
267 match self.typ {
268 RecordType::None => Err(anyhow!("Cannot produce a USP Record without type"))?,
269 RecordType::NoSessionContext => {
270 let payload = self
271 .payload
272 .with_context(|| "Cannot produce USP Record without payload")?;
273
274 record.record_type =
275 OneOfrecord_type::no_session_context(NoSessionContextRecord { payload });
276 }
277 RecordType::SessionContext { session_context } => {
278 record.record_type = OneOfrecord_type::session_context(session_context.build()?);
279 }
280 RecordType::WebSocketConnect => {
281 record.record_type = OneOfrecord_type::websocket_connect(WebSocketConnectRecord {});
282 }
283 RecordType::MQTTConnect {
284 version,
285 subscribed_topic,
286 } => {
287 record.record_type = OneOfrecord_type::mqtt_connect(MQTTConnectRecord {
288 version,
289 subscribed_topic,
290 });
291 }
292 RecordType::STOMPConnect {
293 version,
294 subscribed_destination,
295 } => {
296 record.record_type = OneOfrecord_type::stomp_connect(STOMPConnectRecord {
297 version,
298 subscribed_destination,
299 });
300 }
301 RecordType::Disconnect {
302 reason,
303 reason_code,
304 } => {
305 record.record_type = OneOfrecord_type::disconnect(DisconnectRecord {
306 reason,
307 reason_code,
308 });
309 }
310 RecordType::UDSConnect => {
311 record.record_type = OneOfrecord_type::uds_connect(UDSConnectRecord {});
312 }
313 }
314
315 Ok(record)
316 }
317}