sml_rs/parser/mod.rs
1//! SML parsers.
2//!
3//! This module contains parsers for the SML protocol. The `complete` module contains an easy to use allocating
4//! parser. The `streaming` module contains a flexible non-allocating parser. See the discussion below for a
5//! comparison of the two parsers:
6//!
7//! # Which parser should I choose?
8//!
9//! The SML protocol defines two data structures that can hold multiple elements. An SML File can
10//! contain multiple SML Messages and the "SML_GetList.Res" message contains a list of values.
11//! Because of these two elements, the size of an SML File cannot be known at compile time.
12//!
13//! The parser in the `complete` module uses dynamic memory allocations `alloc::vec::Vec` for SML
14//! Messages and SML Values. This makes the usage straight-forward and if you're using `sml-rs` on
15//! a hosted platform, this is most likely the parser you'll want to use.
16//!
17//! The parser in the `streaming` module works differently and therefore doesn't require dynamic
18//! memory allocations. Instead of returning a single data structure representing the whole SML File,
19//! this parser produces a stream of events that each have a size known at compile time. Depending on the
20//! input, the parser will produce a different number of events, which is how different numbers of SML
21//! Messages / Values can be handled. If you're using `sml-rs` on a microcontroller and don't want to use
22//! an allocator, this is the parser you'll want to use.
23//!
24//! # Examples
25//!
26//! ## Using `complete::parse`
27//!
28//! ```
29//! # #[cfg(feature = "alloc")] {
30//! # use sml_rs::parser::complete;
31//! let bytes: &[u8] = &[ /*...*/ ];
32//!
33//! println!("{:#?}", complete::parse(&bytes).expect("error while parsing"));
34//! # }
35//! ```
36//!
37//! Output (stripped-down to the relevant parts):
38//! ```text
39//! File {
40//! messages: [
41//! Message {
42//! message_body: OpenResponse {
43//! ref_time: SecIndex(23876784),
44//! ...
45//! },
46//! ...
47//! },
48//! Message {
49//! message_body: GetListResponse {
50//! val_list: [
51//! ListEntry { ... },
52//! ListEntry { ... },
53//! ListEntry { ... },
54//! ],
55//! },
56//! ...
57//! },
58//! Message {
59//! message_body: CloseResponse,
60//! ...
61//! },
62//! ],
63//! }
64//! ```
65//!
66//! ## Using `streaming::Parser`
67//! ```rust
68//! # use sml_rs::parser::streaming;
69//! let bytes: &[u8] = &[ /*...*/ ];
70//!
71//! let parser = streaming::Parser::new(bytes);
72//! for item in parser {
73//! println!("- {:#?}", item.expect("error while parsing"));
74//! }
75//! ```
76//!
77//! Output (stripped-down to the relevant parts):
78//! ```text
79//! - MessageStart(MessageStart {
80//! message_body: OpenResponse {
81//! ref_time: SecIndex(23876784),
82//! ...
83//! },
84//! ...
85//! })
86//! - MessageStart(MessageStart {
87//! message_body: GetListResponseStart {
88//! num_values: 3,
89//! ...
90//! },
91//! ...
92//! })
93//! - ListEntry(ListEntry { ... })
94//! - ListEntry(ListEntry { ... })
95//! - ListEntry(ListEntry { ... })
96//! - GetListResponseEnd(GetListResponseEnd)
97//! - MessageStart(MessageStart {
98//! message_body: CloseResponse,
99//! ...
100//! })
101//! ```
102//!
103//!
104
105use core::{
106 fmt::{self, Debug},
107 ops::Deref,
108};
109
110use tlf::TypeLengthField;
111
112pub mod common;
113#[cfg(feature = "alloc")]
114pub mod complete;
115mod num;
116mod octet_string;
117pub mod streaming;
118mod tlf;
119
120pub use tlf::TlfParseError;
121
122pub use octet_string::OctetStr;
123
124/// Error type used by the parser
125#[derive(Clone, Debug, PartialEq, Eq)]
126pub enum ParseError {
127 /// There are additional bytes in the input while the parser expects EOF
128 LeftoverInput,
129 /// The parser expected additional bytes but encountered an EOF
130 UnexpectedEOF,
131 /// An error occurred while parsing a `TypeLengthField`
132 InvalidTlf(TlfParseError),
133 /// TLF mismatch while parsing struct / enum
134 TlfMismatch(&'static str),
135 /// CRC mismatch,
136 CrcMismatch,
137 /// Expected to find 0x00 as message end marker, got something else
138 MsgEndMismatch,
139 /// Got a variant id that isn't known. This means it's either invalid or not supported (yet) by the parser
140 UnexpectedVariant,
141}
142
143impl fmt::Display for ParseError {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 <Self as Debug>::fmt(self, f)
146 }
147}
148
149#[cfg(feature = "std")]
150impl std::error::Error for ParseError {}
151
152type ResTy<'i, O> = Result<(&'i [u8], O), ParseError>;
153#[allow(dead_code)]
154type ResTyComplete<'i, O> = Result<O, ParseError>;
155
156/// SmlParse is the main trait used to parse bytes into SML data structures.
157pub(crate) trait SmlParse<'i>
158where
159 Self: Sized,
160{
161 /// Tries to parse an instance of `Self` from a byte slice.
162 ///
163 /// On success, returns the remaining input and the parsed instance of `Self`.
164 fn parse(input: &'i [u8]) -> ResTy<Self>;
165
166 /// Tries to parse an instance of `Self` from a byte slice and returns an error if there are leftover bytes.
167 ///
168 /// On success, returns the parsed instance of `Self`.
169 fn parse_complete(input: &'i [u8]) -> ResTyComplete<Self> {
170 let (input, x) = Self::parse(input)?;
171 if !input.is_empty() {
172 return Err(ParseError::LeftoverInput);
173 }
174 Ok(x)
175 }
176}
177
178pub(crate) trait SmlParseTlf<'i>
179where
180 Self: Sized,
181{
182 fn check_tlf(tlf: &TypeLengthField) -> bool;
183
184 fn parse_with_tlf(input: &'i [u8], tlf: &TypeLengthField) -> ResTy<'i, Self>;
185}
186
187impl<'i, T: SmlParseTlf<'i>> SmlParse<'i> for T {
188 fn parse(input: &'i [u8]) -> ResTy<Self> {
189 let (input, tlf) = TypeLengthField::parse(input)?;
190 if !Self::check_tlf(&tlf) {
191 return Err(ParseError::TlfMismatch(core::any::type_name::<Self>()));
192 }
193 Self::parse_with_tlf(input, &tlf)
194 }
195}
196
197impl<'i, T: SmlParse<'i>> SmlParse<'i> for Option<T> {
198 fn parse(input: &'i [u8]) -> ResTy<Self> {
199 if let Some(0x01u8) = input.first() {
200 Ok((&input[1..], None))
201 } else {
202 let (input, x) = T::parse(input)?;
203 Ok((input, Some(x)))
204 }
205 }
206}
207
208fn take_byte(input: &[u8]) -> ResTy<u8> {
209 if input.is_empty() {
210 return Err(ParseError::UnexpectedEOF);
211 }
212 Ok((&input[1..], input[0]))
213}
214
215fn take<const N: usize>(input: &[u8]) -> ResTy<&[u8; N]> {
216 if input.len() < N {
217 return Err(ParseError::UnexpectedEOF);
218 }
219 Ok((&input[N..], input[..N].try_into().unwrap()))
220}
221
222fn take_n(input: &[u8], n: usize) -> ResTy<&[u8]> {
223 if input.len() < n {
224 return Err(ParseError::UnexpectedEOF);
225 }
226 Ok((&input[n..], &input[..n]))
227}
228
229fn map<O1, O2>(val: ResTy<O1>, mut f: impl FnMut(O1) -> O2) -> ResTy<O2> {
230 val.map(|(input, x)| (input, f(x)))
231}
232
233struct OctetStrFormatter<'i>(&'i [u8]);
234
235// formats a slice using the compact single-line output even when the parent element should be formatted using "{:#?}"
236impl<'i> Debug for OctetStrFormatter<'i> {
237 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
238 write!(f, "{:?}", self.0)
239 }
240}
241
242struct NumberFormatter<T: Debug, U: Deref<Target = T>>(U);
243
244impl<T: Debug, U: Deref<Target = T>> Debug for NumberFormatter<T, U> {
245 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
246 write!(f, "{:?}{}", self.0.deref(), core::any::type_name::<T>())
247 }
248}