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