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