rusp_lib/usp_encoder.rs
1use quick_protobuf::{message::MessageWrite, Writer};
2
3use crate::usp::Msg;
4use crate::usp_record::{Record, SessionContextRecord};
5
6use anyhow::{Context, Result};
7
8impl SessionContextRecord {
9 /// Creates a new [`SessionContextRecord`] with an unfragmented payload
10 #[must_use]
11 pub fn new_unfragmented(
12 session_id: u64,
13 sequence_id: u64,
14 expected_id: u64,
15 retransmit_id: u64,
16 payload: Vec<u8>,
17 ) -> Self {
18 use crate::usp_record::mod_SessionContextRecord::PayloadSARState;
19
20 Self {
21 session_id,
22 sequence_id,
23 expected_id,
24 retransmit_id,
25 payload_sar_state: PayloadSARState::NONE,
26 payloadrec_sar_state: PayloadSARState::NONE,
27 payload: vec![payload],
28 }
29 }
30}
31
32/// Implementation of some extension methods for `Record`s
33impl Record {
34 /// Encode the `Record` into a Protobuf byte stream returned as `Vec<[u8]>`
35 ///
36 /// # Arguments
37 ///
38 /// * `self` - A USP `Record` structure
39 ///
40 /// # Example
41 ///
42 /// ```
43 /// use rusp_lib::usp_decoder::try_decode_record;
44 /// let bytes = &[
45 /// 0x0a, 0x03, 0x31, 0x2e, 0x33, 0x12, 0x07, 0x64,
46 /// 0x6f, 0x63, 0x3a, 0x3a, 0x74, 0x6f, 0x1a, 0x09,
47 /// 0x64, 0x6f, 0x63, 0x3a, 0x3a, 0x66, 0x72, 0x6f,
48 /// 0x6d, 0x52, 0x09, 0x08, 0x01, 0x12, 0x05, 0x74,
49 /// 0x6f, 0x70, 0x69, 0x63,
50 /// ];
51 /// let record = try_decode_record(bytes).unwrap();
52 /// assert_eq!(record.to_vec().unwrap(), bytes);
53 /// ```
54 ///
55 /// # Errors
56 ///
57 /// This function will return `Err` containing a textual description of the encountered error if
58 /// the `Record` cannot be serialized into a Protobuf representation
59 pub fn to_vec(&self) -> Result<Vec<u8>> {
60 let mut buf = Vec::new();
61 let mut writer = Writer::new(&mut buf);
62 self.write_message(&mut writer)
63 .context("Failed serializing USP Record to Protobuf")?;
64
65 Ok(buf)
66 }
67
68 /// Render the `Record` into a raw C string representation
69 ///
70 /// # Arguments
71 ///
72 /// * `self` - A USP `Record` structure
73 ///
74 /// # Example
75 ///
76 /// ```
77 /// use rusp_lib::usp_decoder::try_decode_record;
78 /// let record =
79 /// try_decode_record(&[
80 /// 0x0a, 0x03, 0x31, 0x2e, 0x33, 0x12, 0x07, 0x64,
81 /// 0x6f, 0x63, 0x3a, 0x3a, 0x74, 0x6f, 0x1a, 0x09,
82 /// 0x64, 0x6f, 0x63, 0x3a, 0x3a, 0x66, 0x72, 0x6f,
83 /// 0x6d, 0x52, 0x09, 0x08, 0x01, 0x12, 0x05, 0x74,
84 /// 0x6f, 0x70, 0x69, 0x63,
85 /// ]).unwrap();
86 /// assert_eq!(record.to_c_str().unwrap(), "\"\\x0a\\x031.3\\x12\\x07doc\\x3a\\x3ato\\x1a\\x09doc\\x3a\\x3afromR\\x09\\x08\\x01\\x12\\x05topic\"\n");
87 /// ```
88 ///
89 /// # Errors
90 ///
91 /// This function will return `Err` containing a textual description of the encountered error if
92 /// the `Record` cannot be serialized into a C string representation
93 pub fn to_c_str(&self) -> Result<String> {
94 use std::fmt::Write as _;
95
96 const fn check_printable(c: u8) -> bool {
97 match c as char {
98 ' ' | '.' | '!' | '(' | ')' | '\'' | ',' | '*' | '[' | ']' | '=' | '<' | '>'
99 | '-' | '_' => true,
100 _ if c.is_ascii_alphanumeric() => true,
101 _ => false,
102 }
103 }
104
105 let data = self.to_vec()?;
106 let mut out = String::new();
107
108 write!(out, "\"")?;
109 for i in &data {
110 if check_printable(*i) {
111 write!(out, "{}", char::from(*i))?;
112 } else {
113 write!(out, "\\x{i:02x}")?;
114 }
115 }
116 writeln!(out, "\"")?;
117
118 Ok(out)
119 }
120
121 /// Render the `Record` into a raw C array representation
122 ///
123 /// # Arguments
124 ///
125 /// * `self` - A USP `Record` structure
126 ///
127 /// # Example
128 ///
129 /// ```
130 /// use rusp_lib::usp_decoder::try_decode_record;
131 /// let record =
132 /// try_decode_record(&[
133 /// 0x0a, 0x03, 0x31, 0x2e, 0x33, 0x12, 0x07, 0x64,
134 /// 0x6f, 0x63, 0x3a, 0x3a, 0x74, 0x6f, 0x1a, 0x09,
135 /// 0x64, 0x6f, 0x63, 0x3a, 0x3a, 0x66, 0x72, 0x6f,
136 /// 0x6d, 0x52, 0x09, 0x08, 0x01, 0x12, 0x05, 0x74,
137 /// 0x6f, 0x70, 0x69, 0x63,
138 /// ]).unwrap();
139 /// assert_eq!(record.to_c_array().unwrap(), "unsigned int pb_len = 36;\nconst char pb[] = {\n 0x0a, 0x03, 0x31, 0x2e, 0x33, 0x12, 0x07, 0x64, /* __1.3__d */\n 0x6f, 0x63, 0x3a, 0x3a, 0x74, 0x6f, 0x1a, 0x09, /* oc__to__ */\n 0x64, 0x6f, 0x63, 0x3a, 0x3a, 0x66, 0x72, 0x6f, /* doc__fro */\n 0x6d, 0x52, 0x09, 0x08, 0x01, 0x12, 0x05, 0x74, /* mR_____t */\n 0x6f, 0x70, 0x69, 0x63, /* opic */\n};\n");
140 /// ```
141 ///
142 /// # Errors
143 ///
144 /// This function will return `Err` containing a textual description of the encountered error if
145 /// the `Record` cannot be serialized into a C array representation
146 pub fn to_c_array(&self) -> Result<String> {
147 self.to_c_array_custom("pb")
148 }
149
150 /// Render the `Record` into a raw C array representation with a custom name
151 ///
152 /// # Arguments
153 ///
154 /// * `self` - A USP `Record` structure
155 /// * `name` - The variable name prefix used in the rendered output
156 ///
157 /// # Example
158 ///
159 /// ```
160 /// use rusp_lib::usp_decoder::try_decode_record;
161 /// let record =
162 /// try_decode_record(&[
163 /// 0x0a, 0x03, 0x31, 0x2e, 0x33, 0x12, 0x07, 0x64,
164 /// 0x6f, 0x63, 0x3a, 0x3a, 0x74, 0x6f, 0x1a, 0x09,
165 /// 0x64, 0x6f, 0x63, 0x3a, 0x3a, 0x66, 0x72, 0x6f,
166 /// 0x6d, 0x52, 0x09, 0x08, 0x01, 0x12, 0x05, 0x74,
167 /// 0x6f, 0x70, 0x69, 0x63,
168 /// ]).unwrap();
169 /// assert_eq!(record.to_c_array_custom("rec").unwrap(), "unsigned int rec_len = 36;\nconst char rec[] = {\n 0x0a, 0x03, 0x31, 0x2e, 0x33, 0x12, 0x07, 0x64, /* __1.3__d */\n 0x6f, 0x63, 0x3a, 0x3a, 0x74, 0x6f, 0x1a, 0x09, /* oc__to__ */\n 0x64, 0x6f, 0x63, 0x3a, 0x3a, 0x66, 0x72, 0x6f, /* doc__fro */\n 0x6d, 0x52, 0x09, 0x08, 0x01, 0x12, 0x05, 0x74, /* mR_____t */\n 0x6f, 0x70, 0x69, 0x63, /* opic */\n};\n");
170 /// ```
171 ///
172 /// # Errors
173 ///
174 /// This function will return `Err` containing a textual description of the encountered error if
175 /// the `Record` cannot be serialized into a C array representation
176 pub fn to_c_array_custom(&self, name: &str) -> Result<String> {
177 const CHUNK_LEN: usize = 8;
178 use std::fmt::Write as _;
179
180 const fn check_printable(c: u8) -> bool {
181 match c as char {
182 ' ' | '.' | '!' | '(' | ')' | '\'' | '"' | ',' | '*' | '[' | ']' | '=' | '<'
183 | '>' | '-' | '_' => true,
184 _ if c.is_ascii_alphanumeric() => true,
185 _ => false,
186 }
187 }
188
189 let data = self.to_vec()?;
190 let mut out = String::new();
191
192 writeln!(out, "unsigned int {name}_len = {};", data.len())?;
193 writeln!(out, "const char {name}[] = {{")?;
194 for chunk in data.chunks(CHUNK_LEN) {
195 write!(out, " ")?;
196 for i in chunk {
197 write!(out, "0x{i:02x}, ")?;
198 }
199
200 for _ in chunk.len()..CHUNK_LEN {
201 write!(out, " ")?;
202 }
203
204 write!(out, "/* ")?;
205 for i in chunk {
206 if check_printable(*i) {
207 write!(out, "{}", char::from(*i))?;
208 } else {
209 write!(out, "_")?;
210 }
211 }
212 write!(out, " */")?;
213
214 writeln!(out)?;
215 }
216 writeln!(out, "}};")?;
217
218 Ok(out)
219 }
220}
221
222/// Implementation of some extension methods for `Msg`s
223impl Msg {
224 /// Encode the Msg into a Protobuf byte stream returned as `Vec<[u8]>`
225 ///
226 /// # Arguments
227 ///
228 /// * `self` - A decoded USP Msg structure
229 ///
230 /// # Example
231 ///
232 /// ```
233 /// use rusp_lib::usp_decoder::try_decode_msg;
234 /// let bytes = &[
235 /// 0x0a, 0x08, 0x0a, 0x04, 0x74, 0x65, 0x73, 0x74,
236 /// 0x10, 0x03, 0x12, 0x28, 0x0a, 0x26, 0x42, 0x24,
237 /// 0x0a, 0x05, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x10,
238 /// 0x01, 0x42, 0x19, 0x0a, 0x06, 0x30, 0x30, 0x34,
239 /// 0x34, 0x46, 0x46, 0x12, 0x03, 0x46, 0x6f, 0x6f,
240 /// 0x1a, 0x05, 0x30, 0x31, 0x32, 0x33, 0x34, 0x22,
241 /// 0x03, 0x31, 0x2e, 0x33,
242 /// ];
243 /// let msg = try_decode_msg(bytes).unwrap();
244 /// assert_eq!(msg.to_vec().unwrap(), bytes);
245 /// ```
246 ///
247 /// # Errors
248 ///
249 /// This function will return `Err` containing a textual description of the encountered error if
250 /// the `Msg` cannot be serialized into a Protobuf representation
251 pub fn to_vec(&self) -> Result<Vec<u8>> {
252 let mut buf = Vec::new();
253 let mut writer = Writer::new(&mut buf);
254 self.write_message(&mut writer)
255 .context("Failed serializing USP Msg to Protobuf")?;
256
257 Ok(buf)
258 }
259
260 /// Render the `Msg` into a raw C string representation
261 ///
262 /// # Arguments
263 ///
264 /// * `self` - A USP `Msg` structure
265 ///
266 /// # Example
267 ///
268 /// ```
269 /// use rusp_lib::usp_decoder::try_decode_msg;
270 /// let bytes = &[
271 /// 0x0a, 0x08, 0x0a, 0x04, 0x74, 0x65, 0x73, 0x74,
272 /// 0x10, 0x03, 0x12, 0x28, 0x0a, 0x26, 0x42, 0x24,
273 /// 0x0a, 0x05, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x10,
274 /// 0x01, 0x42, 0x19, 0x0a, 0x06, 0x30, 0x30, 0x34,
275 /// 0x34, 0x46, 0x46, 0x12, 0x03, 0x46, 0x6f, 0x6f,
276 /// 0x1a, 0x05, 0x30, 0x31, 0x32, 0x33, 0x34, 0x22,
277 /// 0x03, 0x31, 0x2e, 0x33,
278 /// ];
279 /// let msg = try_decode_msg(bytes).unwrap();
280 /// assert_eq!(msg.to_c_str().unwrap(), "\"\\x0a\\x08\\x0a\\x04test\\x10\\x03\\x12(\\x0a\\x26B\\x24\\x0a\\x05notif\\x10\\x01B\\x19\\x0a\\x060044FF\\x12\\x03Foo\\x1a\\x0501234\\x22\\x031.3\"\n");
281 /// ```
282 ///
283 /// # Errors
284 ///
285 /// This function will return `Err` containing a textual description of the encountered error if
286 /// the `Msg` cannot be serialized into a C string representation
287 pub fn to_c_str(&self) -> Result<String> {
288 use std::fmt::Write as _;
289
290 const fn check_printable(c: u8) -> bool {
291 match c as char {
292 ' ' | '.' | '!' | '(' | ')' | '\'' | ',' | '*' | '[' | ']' | '=' | '<' | '>'
293 | '-' | '_' => true,
294 _ if c.is_ascii_alphanumeric() => true,
295 _ => false,
296 }
297 }
298
299 let data = self.to_vec()?;
300 let mut out = String::new();
301
302 write!(out, "\"")?;
303 for i in &data {
304 if check_printable(*i) {
305 write!(out, "{}", char::from(*i))?;
306 } else {
307 write!(out, "\\x{i:02x}")?;
308 }
309 }
310 writeln!(out, "\"")?;
311
312 Ok(out)
313 }
314
315 /// Render the `Msg` into a raw C array representation
316 ///
317 /// # Arguments
318 ///
319 /// * `self` - A USP `Msg` structure
320 ///
321 /// # Example
322 ///
323 /// ```
324 /// use rusp_lib::usp_decoder::try_decode_msg;
325 /// let bytes = &[
326 /// 0x0a, 0x08, 0x0a, 0x04, 0x74, 0x65, 0x73, 0x74,
327 /// 0x10, 0x03, 0x12, 0x28, 0x0a, 0x26, 0x42, 0x24,
328 /// 0x0a, 0x05, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x10,
329 /// 0x01, 0x42, 0x19, 0x0a, 0x06, 0x30, 0x30, 0x34,
330 /// 0x34, 0x46, 0x46, 0x12, 0x03, 0x46, 0x6f, 0x6f,
331 /// 0x1a, 0x05, 0x30, 0x31, 0x32, 0x33, 0x34, 0x22,
332 /// 0x03, 0x31, 0x2e, 0x33,
333 /// ];
334 /// let msg = try_decode_msg(bytes).unwrap();
335 /// assert_eq!(msg.to_c_array().unwrap(), "unsigned int pb_len = 52;\nconst char pb[] = {\n 0x0a, 0x08, 0x0a, 0x04, 0x74, 0x65, 0x73, 0x74, /* ____test */\n 0x10, 0x03, 0x12, 0x28, 0x0a, 0x26, 0x42, 0x24, /* ___(__B_ */\n 0x0a, 0x05, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x10, /* __notif_ */\n 0x01, 0x42, 0x19, 0x0a, 0x06, 0x30, 0x30, 0x34, /* _B___004 */\n 0x34, 0x46, 0x46, 0x12, 0x03, 0x46, 0x6f, 0x6f, /* 4FF__Foo */\n 0x1a, 0x05, 0x30, 0x31, 0x32, 0x33, 0x34, 0x22, /* __01234\" */\n 0x03, 0x31, 0x2e, 0x33, /* _1.3 */\n};\n");
336 /// ```
337 ///
338 /// # Errors
339 ///
340 /// This function will return `Err` containing a textual description of the encountered error if
341 /// the `Msg` cannot be serialized into a C array representation
342 pub fn to_c_array(&self) -> Result<String> {
343 self.to_c_array_custom("pb")
344 }
345
346 /// Render the `Msg` into a raw C array representation with a custom name
347 ///
348 /// # Arguments
349 ///
350 /// * `self` - A USP `Msg` structure
351 /// * `name` - The variable name prefix used in the rendered output
352 ///
353 /// # Example
354 ///
355 /// ```
356 /// use rusp_lib::usp_decoder::try_decode_msg;
357 /// let bytes = &[
358 /// 0x0a, 0x08, 0x0a, 0x04, 0x74, 0x65, 0x73, 0x74,
359 /// 0x10, 0x03, 0x12, 0x28, 0x0a, 0x26, 0x42, 0x24,
360 /// 0x0a, 0x05, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x10,
361 /// 0x01, 0x42, 0x19, 0x0a, 0x06, 0x30, 0x30, 0x34,
362 /// 0x34, 0x46, 0x46, 0x12, 0x03, 0x46, 0x6f, 0x6f,
363 /// 0x1a, 0x05, 0x30, 0x31, 0x32, 0x33, 0x34, 0x22,
364 /// 0x03, 0x31, 0x2e, 0x33,
365 /// ];
366 /// let msg = try_decode_msg(bytes).unwrap();
367 /// assert_eq!(msg.to_c_array_custom("msg").unwrap(), "unsigned int msg_len = 52;\nconst char msg[] = {\n 0x0a, 0x08, 0x0a, 0x04, 0x74, 0x65, 0x73, 0x74, /* ____test */\n 0x10, 0x03, 0x12, 0x28, 0x0a, 0x26, 0x42, 0x24, /* ___(__B_ */\n 0x0a, 0x05, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x10, /* __notif_ */\n 0x01, 0x42, 0x19, 0x0a, 0x06, 0x30, 0x30, 0x34, /* _B___004 */\n 0x34, 0x46, 0x46, 0x12, 0x03, 0x46, 0x6f, 0x6f, /* 4FF__Foo */\n 0x1a, 0x05, 0x30, 0x31, 0x32, 0x33, 0x34, 0x22, /* __01234\" */\n 0x03, 0x31, 0x2e, 0x33, /* _1.3 */\n};\n");
368 /// ```
369 ///
370 /// # Errors
371 ///
372 /// This function will return `Err` containing a textual description of the encountered error if
373 /// the `Msg` cannot be serialized into a C array representation
374 pub fn to_c_array_custom(&self, name: &str) -> Result<String> {
375 const CHUNK_LEN: usize = 8;
376 use std::fmt::Write as _;
377
378 const fn check_printable(c: u8) -> bool {
379 match c as char {
380 ' ' | '.' | '!' | '(' | ')' | '\'' | '"' | ',' | '*' | '[' | ']' | '=' | '<'
381 | '>' | '-' | '_' => true,
382 _ if c.is_ascii_alphanumeric() => true,
383 _ => false,
384 }
385 }
386
387 let data = self.to_vec()?;
388 let mut out = String::new();
389
390 writeln!(out, "unsigned int {name}_len = {};", data.len())?;
391 writeln!(out, "const char {name}[] = {{")?;
392 for chunk in data.chunks(CHUNK_LEN) {
393 write!(out, " ")?;
394 for i in chunk {
395 write!(out, "0x{i:02x}, ")?;
396 }
397
398 for _ in chunk.len()..CHUNK_LEN {
399 write!(out, " ")?;
400 }
401
402 write!(out, "/* ")?;
403 for i in chunk {
404 if check_printable(*i) {
405 write!(out, "{}", char::from(*i))?;
406 } else {
407 write!(out, "_")?;
408 }
409 }
410 write!(out, " */")?;
411
412 writeln!(out)?;
413 }
414 writeln!(out, "}};")?;
415
416 Ok(out)
417 }
418}