rust: esp8266at: Wrap parsing of responses to avoid exposing dependency on nom

Makes it possible to switch parser later for whatever reason,
it's undesirable to expose these implementation details to clients.
This commit is contained in:
Wladimir J. van der Laan 2019-06-01 23:04:31 +00:00
parent d20791330d
commit 610af76592
5 changed files with 41 additions and 27 deletions

View File

@ -2,7 +2,7 @@ use std::fs::File;
use std::io::BufRead; use std::io::BufRead;
use std::io::BufReader; use std::io::BufReader;
use esp8266at::response::parse_response; use esp8266at::response::{parse,ParseResult};
fn main() { fn main() {
let f = File::open("data/parses.txt").unwrap(); let f = File::open("data/parses.txt").unwrap();
@ -13,14 +13,16 @@ fn main() {
let mut lb = l[2..].as_bytes().to_vec(); let mut lb = l[2..].as_bytes().to_vec();
lb.push(13); lb.push(13);
lb.push(10); lb.push(10);
let res = parse_response(&lb); let res = parse(&lb);
match res { match res {
Err(x) => { ParseResult::Err => {
println!("failed command was: {}", l); println!("failed command was: {}", l);
println!("{:?}", x);
} }
Ok((res, x)) => { ParseResult::Incomplete => {
if res.is_empty() { println!("incomplete command was: {}", l);
}
ParseResult::Ok(res, x) => {
if res == lb.len() {
println!("{:?}", x); println!("{:?}", x);
} else { } else {
println!("non-empty residue command was: {}", l); println!("non-empty residue command was: {}", l);

View File

@ -1,10 +1,9 @@
/** Example synchronous serial receive event loop (for std) */ /** Example synchronous serial receive event loop (for std) */
use nom::Offset;
use std::fmt; use std::fmt;
use std::io; use std::io;
use crate::handler::{NetworkEvent, SerialNetworkHandler}; use crate::handler::{NetworkEvent, SerialNetworkHandler};
use crate::response::parse_response; use crate::response::{parse, ParseResult};
/** Mainloop handling serial input and dispatching network events */ /** Mainloop handling serial input and dispatching network events */
pub fn mainloop<P, F, X>( pub fn mainloop<P, F, X>(
@ -34,24 +33,24 @@ where
while start < ofs { while start < ofs {
// try parsing // try parsing
let tail = &serial_buf[start..ofs]; let tail = &serial_buf[start..ofs];
let erase = match parse_response(tail) { let erase = match parse(tail) {
Ok((residue, resp)) => { ParseResult::Ok(offset, resp) => {
h.message( h.message(
&resp, &resp,
&mut |a, b, debug| { |a, b, debug| {
running = f(a, b, debug); running = f(a, b, debug);
}, },
debug, debug,
)?; )?;
tail.offset(residue) offset
} }
Err(nom::Err::Incomplete(_)) => { ParseResult::Incomplete => {
// Incomplete, ignored, just retry after a new receive // Incomplete, ignored, just retry after a new receive
0 0
} }
Err(err) => { ParseResult::Err => {
writeln!(debug, "err: {:?}", err).unwrap(); writeln!(debug, "err: {:?}", tail).unwrap();
// Erase unparseable data to next line, if line is complete // Erase unparseable data to next line, if line is complete
if let Some(ofs) = tail.iter().position(|&x| x == b'\n') { if let Some(ofs) = tail.iter().position(|&x| x == b'\n') {
ofs + 1 ofs + 1

View File

@ -1,6 +1,6 @@
use core::str;
/** Parser for ESP8266 AT responses */ /** Parser for ESP8266 AT responses */
use nom::{digit, hex_digit}; use core::str;
use nom::{Offset, digit, hex_digit};
/** Connection type for CIPSTATUS etc */ /** Connection type for CIPSTATUS etc */
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
@ -73,6 +73,13 @@ pub enum Response<'a> {
RecvPrompt, RecvPrompt,
} }
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParseResult<'a> {
Ok(usize, Response<'a>),
Incomplete,
Err,
}
/* Decimal unsigned integer */ /* Decimal unsigned integer */
named!(num_u32<&[u8], u32>, named!(num_u32<&[u8], u32>,
// The unwrap() here is safe because digit will never return non-UTF8 // The unwrap() here is safe because digit will never return non-UTF8
@ -279,7 +286,7 @@ named!(ipd_data<&[u8],Response>,
); );
/* Parse response from line */ /* Parse response from line */
named!(pub parse_response<&[u8],Response>, named!(parse_response<&[u8],Response>,
alt!( alt!(
nl_terminated nl_terminated
| ipd_data | ipd_data
@ -288,6 +295,14 @@ named!(pub parse_response<&[u8],Response>,
) )
); );
pub fn parse(response: &[u8]) -> ParseResult {
match parse_response(response) {
Ok((residue, resp)) => ParseResult::Ok(response.offset(residue), resp),
Err(nom::Err::Incomplete(_)) => ParseResult::Incomplete,
Err(_) => ParseResult::Err,
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -13,4 +13,3 @@ riscv = "0.5"
k210-shared = { path = "../k210-shared" } k210-shared = { path = "../k210-shared" }
k210-console = { path = "../k210-console" } k210-console = { path = "../k210-console" }
esp8266at = { path = "../esp8266at", default-features = false } esp8266at = { path = "../esp8266at", default-features = false }
nom = { version = "4", default-features = false }

View File

@ -7,7 +7,7 @@
use core::str; use core::str;
use embedded_hal::serial; use embedded_hal::serial;
use esp8266at::handler::{NetworkEvent, SerialNetworkHandler}; use esp8266at::handler::{NetworkEvent, SerialNetworkHandler};
use esp8266at::response::{parse_response, ConnectionType}; use esp8266at::response::{parse, ConnectionType, ParseResult};
use esp8266at::traits::{self, Write}; use esp8266at::traits::{self, Write};
use k210_hal::pac; use k210_hal::pac;
use k210_hal::prelude::*; use k210_hal::prelude::*;
@ -21,7 +21,6 @@ use k210_shared::soc::sleep::usleep;
use k210_shared::soc::spi::SPIExt; use k210_shared::soc::spi::SPIExt;
use k210_shared::soc::sysctl; use k210_shared::soc::sysctl;
use nb::block; use nb::block;
use nom::Offset;
use riscv::register::mcycle; use riscv::register::mcycle;
use riscv_rt::entry; use riscv_rt::entry;
use k210_console::console::{Console, ScreenImage, DISP_HEIGHT, DISP_WIDTH}; use k210_console::console::{Console, ScreenImage, DISP_HEIGHT, DISP_WIDTH};
@ -159,8 +158,8 @@ fn main() -> ! {
while start < ofs { while start < ofs {
// try parsing // try parsing
let tail = &serial_buf[start..ofs]; let tail = &serial_buf[start..ofs];
let erase = match parse_response(tail) { let erase = match parse(tail) {
Ok((residue, resp)) => { ParseResult::Ok(offset, resp) => {
sh.message(&resp, |port, ev, _debug| { sh.message(&resp, |port, ev, _debug| {
match ev { match ev {
NetworkEvent::Ready => { NetworkEvent::Ready => {
@ -195,17 +194,17 @@ fn main() -> ! {
} }
}, &mut debug).unwrap(); }, &mut debug).unwrap();
tail.offset(residue) offset
} }
Err(nom::Err::Incomplete(_)) => { ParseResult::Incomplete => {
// Incomplete, ignored, just retry after a new receive // Incomplete, ignored, just retry after a new receive
0 0
} }
Err(err) => { ParseResult::Err => {
if tail.len() > 100 { if tail.len() > 100 {
writeln!(debug, "err: Error([too long ...])").unwrap(); writeln!(debug, "err: Error([too long ...])").unwrap();
} else { } else {
writeln!(debug, "err: {:?}", err).unwrap(); writeln!(debug, "err: {:?}", tail).unwrap();
} }
// Erase unparseable data to next line, if line is complete // Erase unparseable data to next line, if line is complete
if let Some(ofs) = tail.iter().position(|&x| x == b'\n') { if let Some(ofs) = tail.iter().position(|&x| x == b'\n') {