sml_rs/parser/
complete.rs

1//! Simple to use SML parser that uses dynamic memory allocations (requires `alloc` feature)
2//!
3//! # Examples
4//!
5//! ```
6//! use sml_rs::parser::{complete::{parse, File, Message, MessageBody}, common::CloseResponse};
7//!
8//! let bytes = [0x76, 0x5, 0xdd, 0x43, 0x44, 0x0, 0x62, 0x0, 0x62, 0x0, 0x72, 0x63, 0x2, 0x1, 0x71, 0x1, 0x63, 0xfd, 0x56, 0x0];
9//!
10//! // parse the input data
11//! let result = parse(&bytes);
12//!
13//! let expected = File {
14//!     messages: vec![
15//!         Message {
16//!             transaction_id: &[221, 67, 68, 0],
17//!             group_no: 0,
18//!             abort_on_error: 0,
19//!             message_body: MessageBody::CloseResponse(CloseResponse {
20//!                 global_signature: None
21//!             })
22//!         }
23//!     ]
24//! };
25//! assert_eq!(result, Ok(expected))
26//! ```
27
28use alloc::vec::Vec;
29use core::fmt::Debug;
30
31use super::{
32    common::{CloseResponse, EndOfSmlMessage, ListEntry, OpenResponse, Signature, Time},
33    tlf::{Ty, TypeLengthField},
34    OctetStr, OctetStrFormatter, ParseError, ResTy, SmlParse, SmlParseTlf,
35};
36
37#[derive(Debug, PartialEq, Eq, Clone)]
38/// Top-level SML type. Holds multiple `Messages`.
39pub struct File<'i> {
40    /// Vector of `Messsages`
41    pub messages: Vec<Message<'i>>,
42}
43
44impl<'i> SmlParse<'i> for File<'i> {
45    fn parse(mut input: &'i [u8]) -> ResTy<Self> {
46        let mut messages = Vec::new();
47        while !input.is_empty() {
48            let (new_input, msg) = Message::parse(input)?;
49            messages.push(msg);
50            input = new_input;
51        }
52
53        Ok((input, File { messages }))
54    }
55}
56
57#[derive(PartialEq, Eq, Clone)]
58/// An SML message
59pub struct Message<'i> {
60    /// transaction identifier
61    pub transaction_id: OctetStr<'i>,
62    /// allows grouping of SML messages
63    pub group_no: u8,
64    /// describes how to handle the Message in case of errors
65    // this should probably be an enum
66    pub abort_on_error: u8,
67    /// main content of the message
68    pub message_body: MessageBody<'i>,
69}
70
71impl<'i> SmlParse<'i> for Message<'i> {
72    fn parse(input: &'i [u8]) -> ResTy<Self> {
73        let input_orig = input;
74        let (input, tlf) = TypeLengthField::parse(input)?;
75        if tlf.ty != super::tlf::Ty::ListOf || tlf.len != 6 {
76            return Err(ParseError::TlfMismatch("Message"));
77        }
78        let (input, transaction_id) = OctetStr::parse(input)?;
79        let (input, group_no) = u8::parse(input)?;
80        let (input, abort_on_error) = u8::parse(input)?;
81        let (input, message_body) = MessageBody::parse(input)?;
82
83        let num_bytes_read = input_orig.len() - input.len();
84
85        let (input, crc) = u16::parse(input)?;
86        let (input, _) = EndOfSmlMessage::parse(input)?;
87
88        // validate crc16
89        let digest = crate::util::CRC_X25
90            .checksum(&input_orig[0..num_bytes_read])
91            .swap_bytes();
92        if digest != crc {
93            return Err(ParseError::CrcMismatch);
94        }
95
96        let val = Message {
97            transaction_id,
98            group_no,
99            abort_on_error,
100            message_body,
101        };
102        Ok((input, val))
103    }
104}
105
106impl<'i> Debug for Message<'i> {
107    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
108        let mut x = f.debug_struct("Message");
109        x.field("transaction_id", &OctetStrFormatter(self.transaction_id));
110        x.field("group_no", &self.group_no);
111        x.field("abort_on_error", &self.abort_on_error);
112        x.field("message_body", &self.message_body);
113        x.finish()
114    }
115}
116
117#[cfg(feature = "alloc")]
118#[derive(PartialEq, Eq, Clone)]
119/// SML message body
120///
121/// Hint: this type only implements the message types specified by SML that are
122/// used in real-world power meters.
123pub enum MessageBody<'i> {
124    /// `SML_PublicOpen.Res` message
125    OpenResponse(OpenResponse<'i>),
126    /// `SML_PublicClose.Res` message
127    CloseResponse(CloseResponse<'i>),
128    /// `SML_GetList.Res` message
129    GetListResponse(GetListResponse<'i>),
130}
131
132#[cfg(feature = "alloc")]
133impl<'i> core::fmt::Debug for MessageBody<'i> {
134    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
135        match self {
136            Self::OpenResponse(arg0) => arg0.fmt(f),
137            Self::CloseResponse(arg0) => arg0.fmt(f),
138            Self::GetListResponse(arg0) => arg0.fmt(f),
139        }
140    }
141}
142
143impl<'i> SmlParseTlf<'i> for MessageBody<'i> {
144    fn check_tlf(tlf: &TypeLengthField) -> bool {
145        tlf.ty == Ty::ListOf && tlf.len == 2
146    }
147
148    fn parse_with_tlf(input: &'i [u8], _tlf: &TypeLengthField) -> ResTy<'i, Self> {
149        let (input, tag) = u32::parse(input)?;
150        match tag {
151            0x00000101 => {
152                let (input, x) = <OpenResponse<'i>>::parse(input)?;
153                Ok((input, MessageBody::OpenResponse(x)))
154            }
155            0x00000201 => {
156                let (input, x) = <CloseResponse<'i>>::parse(input)?;
157                Ok((input, MessageBody::CloseResponse(x)))
158            }
159            0x00000701 => {
160                let (input, x) = <GetListResponse<'i>>::parse(input)?;
161                Ok((input, MessageBody::GetListResponse(x)))
162            }
163            _ => Err(ParseError::UnexpectedVariant),
164        }
165    }
166}
167
168#[derive(PartialEq, Eq, Clone)]
169/// `SML_GetList.Res` message
170pub struct GetListResponse<'i> {
171    /// identification of the client
172    pub client_id: Option<OctetStr<'i>>,
173    /// identification of the server
174    pub server_id: OctetStr<'i>,
175    /// name of the list
176    pub list_name: Option<OctetStr<'i>>,
177    /// optional sensor time information
178    pub act_sensor_time: Option<Time>,
179    /// list of data values
180    pub val_list: List<'i>,
181    /// signature of the list - whatever that means?!
182    pub list_signature: Option<Signature<'i>>,
183    /// optional gateway time information
184    pub act_gateway_time: Option<Time>,
185}
186
187impl<'i> SmlParseTlf<'i> for GetListResponse<'i> {
188    fn check_tlf(tlf: &TypeLengthField) -> bool {
189        *tlf == TypeLengthField::new(Ty::ListOf, 7usize as u32)
190    }
191
192    fn parse_with_tlf(input: &'i [u8], _tlf: &TypeLengthField) -> ResTy<'i, Self> {
193        let (input, client_id) = <Option<OctetStr<'i>>>::parse(input)?;
194        let (input, server_id) = <OctetStr<'i>>::parse(input)?;
195        let (input, list_name) = <Option<OctetStr<'i>>>::parse(input)?;
196        let (input, act_sensor_time) = <Option<Time>>::parse(input)?;
197        let (input, val_list) = <List<'i>>::parse(input)?;
198        let (input, list_signature) = <Option<Signature<'i>>>::parse(input)?;
199        let (input, act_gateway_time) = <Option<Time>>::parse(input)?;
200        let val = GetListResponse {
201            client_id,
202            server_id,
203            list_name,
204            act_sensor_time,
205            val_list,
206            list_signature,
207            act_gateway_time,
208        };
209        Ok((input, val))
210    }
211}
212
213impl<'i> core::fmt::Debug for GetListResponse<'i> {
214    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
215        let mut x = f.debug_struct("GetListResponse");
216        if let Some(e) = &self.client_id {
217            x.field("client_id", &OctetStrFormatter(e));
218        }
219        x.field("server_id", &OctetStrFormatter(self.server_id));
220        if let Some(e) = &self.list_name {
221            x.field("list_name", &OctetStrFormatter(e));
222        }
223        if let Some(e) = &self.act_sensor_time {
224            x.field("act_sensor_time", &e);
225        }
226        x.field("val_list", &self.val_list);
227        if let Some(e) = &self.list_signature {
228            x.field("list_signature", &e);
229        }
230        if let Some(e) = &self.act_gateway_time {
231            x.field("act_gateway_time", &e);
232        }
233        x.finish()
234    }
235}
236
237/// Vector of SML list entries
238pub type List<'i> = Vec<ListEntry<'i>>;
239
240impl<'i> SmlParseTlf<'i> for List<'i> {
241    fn check_tlf(tlf: &TypeLengthField) -> bool {
242        matches!(tlf.ty, super::tlf::Ty::ListOf)
243    }
244
245    fn parse_with_tlf(mut input: &'i [u8], tlf: &TypeLengthField) -> ResTy<'i, Self> {
246        let mut v = Vec::with_capacity(tlf.len as usize);
247        for _ in 0..tlf.len {
248            let (new_input, x) = ListEntry::parse(input)?;
249            v.push(x);
250            input = new_input;
251        }
252        Ok((input, v))
253    }
254}
255
256/// Parses a slice of bytes into an SML File.
257///
258/// *This function is available only if sml-rs is built with the `"alloc"` feature.*
259pub fn parse(input: &[u8]) -> Result<File, ParseError> {
260    File::parse_complete(input)
261}