use crate::{
message::NmeaMessageError,
sentence::{NmeaSentenceError, Sentence},
};
#[derive(Debug, thiserror::Error)]
pub enum NmeaParserError {
#[error("Sentence was too long for buffer")]
SentenceTooLong,
#[error("Sentence Error: {0}")]
Sentence(#[from] NmeaSentenceError),
}
pub struct NmeaParser<const N: usize> {
buf: [u8; N],
pos: usize,
}
impl<const N: usize> Default for NmeaParser<N> {
fn default() -> Self {
Self {
buf: [0u8; N],
pos: 0,
}
}
}
impl<const N: usize> NmeaParser<N> {
pub fn buffer(&mut self) -> &mut [u8] {
&mut self.buf[self.pos..]
}
pub fn commit<F: FnMut(Result<Sentence<'_>, NmeaParserError>)>(
&mut self,
n: usize,
mut on_sentence: F,
) {
let available = N - self.pos;
assert!(
n <= available,
"mark_written: n={n} exceeds available space={available}"
);
self.pos += n;
while let Some(nl_index) = memchr::memchr(b'\n', &self.buf[..self.pos]) {
let end = nl_index + 1;
let raw_line = &self.buf[..end];
let line = raw_line
.strip_suffix(b"\r\n")
.or_else(|| raw_line.strip_suffix(b"\n"))
.unwrap_or(raw_line);
if line.first() == Some(&b'$') {
on_sentence(Sentence::parse(line).map_err(NmeaParserError::Sentence));
}
self.buf.copy_within(end..self.pos, 0);
self.pos -= end;
}
if self.pos == N {
self.pos = 0;
on_sentence(Err(NmeaParserError::SentenceTooLong));
}
}
}
pub trait NmeaParse<'a>: Sized {
fn parse(fields: &'a str) -> Result<Self, NmeaMessageError>;
}