rusp_lib/usp_builder/
record.rs

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            // FIXME
104            payload_sar_state: PayloadSARState::NONE,
105            // FIXME
106            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}