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}