esp8266_wifi_serial/
parser.rs

1use core::str::FromStr;
2
3use nom::{alt, char, character::streaming::digit1, do_parse, named, opt, tag, IResult};
4
5use crate::net::{IpAddr, Ipv4Addr};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum CommandResponse {
9    Connected { link_id: u16 },
10    Closed { link_id: u16 },
11    DataAvailable { link_id: u16, size: u64 },
12    WifiDisconnect,
13}
14
15fn parse_error(input: &[u8]) -> nom::Err<nom::error::Error<&[u8]>> {
16    nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Digit))
17}
18
19fn atoi<T: FromStr>(input: &[u8]) -> Result<T, nom::Err<nom::error::Error<&[u8]>>> {
20    let s = core::str::from_utf8(input).map_err(|_| parse_error(input))?;
21    s.parse().map_err(|_| parse_error(input))
22}
23
24fn parse_link_id(input: &[u8]) -> IResult<&[u8], u16> {
25    let (input, digits) = digit1(input)?;
26    let num = atoi(digits)?;
27    IResult::Ok((input, num))
28}
29
30fn parse_u64(input: &[u8]) -> IResult<&[u8], u64> {
31    let (input, digits) = digit1(input)?;
32    let num = atoi(digits)?;
33    IResult::Ok((input, num))
34}
35
36fn parse_u8(input: &[u8]) -> IResult<&[u8], u8> {
37    let (input, digits) = digit1(input)?;
38    let num = atoi(digits)?;
39    IResult::Ok((input, num))
40}
41
42named!(crlf, tag!("\r\n"));
43
44named!(
45    connected<CommandResponse>,
46    do_parse!(
47        opt!(crlf)
48            >> link_id: parse_link_id
49            >> tag!(",CONNECT")
50            >> crlf
51            >> (CommandResponse::Connected { link_id })
52    )
53);
54
55named!(
56    closed<CommandResponse>,
57    do_parse!(
58        opt!(crlf)
59            >> link_id: parse_link_id
60            >> tag!(",CLOSED")
61            >> crlf
62            >> (CommandResponse::Closed { link_id })
63    )
64);
65
66named!(
67    data_available<CommandResponse>,
68    do_parse!(
69        opt!(crlf)
70            >> tag!("+IPD,")
71            >> link_id: parse_link_id
72            >> char!(',')
73            >> size: parse_u64
74            >> char!(':')
75            >> opt!(crlf)
76            >> (CommandResponse::DataAvailable { link_id, size })
77    )
78);
79
80named!(
81    wifi_disconnect<CommandResponse>,
82    do_parse!(
83        opt!(crlf) >> tag!("WIFI DISCONNECT") >> opt!(crlf) >> (CommandResponse::WifiDisconnect)
84    )
85);
86
87named!(
88    parse<CommandResponse>,
89    alt!(connected | closed | data_available | wifi_disconnect)
90);
91
92impl CommandResponse {
93    pub fn parse(input: &[u8]) -> Option<(&[u8], Self)> {
94        parse(input).ok()
95    }
96}
97
98pub struct CifsrResponse {
99    pub ap_ip: Option<IpAddr>,
100    pub sta_ip: Option<IpAddr>,
101}
102
103named!(
104    parse_ip4_addr<IpAddr>,
105    do_parse!(
106        opt!(crlf)
107            >> a: parse_u8
108            >> char!('.')
109            >> b: parse_u8
110            >> char!('.')
111            >> c: parse_u8
112            >> char!('.')
113            >> d: parse_u8
114            >> (IpAddr::V4(Ipv4Addr::new(a, b, c, d)))
115    )
116);
117
118named!(
119    parse_apip<IpAddr>,
120    do_parse!(
121        opt!(crlf)
122            >> tag!("+CIFSR:APIP,")
123            >> char!('"')
124            >> ip_addr: parse_ip4_addr
125            >> char!('"')
126            >> opt!(crlf)
127            >> (ip_addr)
128    )
129);
130
131named!(
132    parse_staip<IpAddr>,
133    do_parse!(
134        opt!(crlf)
135            >> tag!("+CIFSR:STAIP,")
136            >> char!('"')
137            >> ip_addr: parse_ip4_addr
138            >> char!('"')
139            >> opt!(crlf)
140            >> (ip_addr)
141    )
142);
143
144named!(
145    cifsr_response<CifsrResponse>,
146    do_parse!(
147        opt!(crlf)
148            >> ap_ip: opt!(parse_apip)
149            >> sta_ip: opt!(parse_staip)
150            >> (CifsrResponse { ap_ip, sta_ip })
151    )
152);
153
154impl CifsrResponse {
155    pub fn parse(input: &[u8]) -> Option<(&[u8], Self)> {
156        cifsr_response(input).ok()
157    }
158}
159
160#[test]
161fn test_parse_connect() {
162    let raw = b"1,CONNECT\r\n";
163    let event = CommandResponse::parse(raw.as_ref()).unwrap().1;
164
165    assert_eq!(event, CommandResponse::Connected { link_id: 1 })
166}
167
168#[test]
169fn test_parse_close() {
170    let raw = b"1,CLOSED\r\n";
171    let event = CommandResponse::parse(raw.as_ref()).unwrap().1;
172
173    assert_eq!(event, CommandResponse::Closed { link_id: 1 })
174}
175
176#[test]
177fn test_parse_data_available() {
178    let raw = b"+IPD,12,6:hello\r\n";
179    let event = CommandResponse::parse(raw.as_ref()).unwrap().1;
180
181    assert_eq!(
182        event,
183        CommandResponse::DataAvailable {
184            link_id: 12,
185            size: 6
186        }
187    )
188}