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}