nmeasis 26.4.1

A memory-safe NMEA 0183 parser with a C FFI
Documentation
pub mod aam;
pub mod gga;
pub mod gll;
pub mod gns;
pub mod gsa;
pub mod gst;
pub mod gsv;
pub mod rmc;
pub mod txt;
pub mod vtg;
pub mod zda;

use crate::encoder::NmeaEncode;
use crate::message::aam::Aam;
use crate::message::gga::Gga;
use crate::message::gll::Gll;
use crate::message::gns::Gns;
use crate::message::gsa::Gsa;
use crate::message::gst::Gst;
use crate::message::gsv::Gsv;
use crate::message::rmc::Rmc;
use crate::message::txt::Txt;
use crate::message::vtg::Vtg;
use crate::message::zda::Zda;
use crate::parser::NmeaParse;

#[derive(Debug, thiserror::Error)]
pub enum NmeaMessageError {
    #[error("Message is not UTF8")]
    NotUtf8(#[from] core::str::Utf8Error),
    #[error("Message is missing field")]
    MissingField,
    #[error("Message has an invalid field")]
    InvalidField,
    #[error("Number is malformed")]
    MalformedNumber,
}

/// Defines a `Message` and `MessageType` for the given lines.
/// Just allows us to automate the process of adding new Messages.
macro_rules! define_messages {
    ($($variant:ident => $type:ty, $code:literal),* $(,)?) => {
        #[derive(Debug)]
        pub enum Message<'a> {
            $($variant($type),)*
            Unknown(&'a str),
        }

        #[repr(u8)]
        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
        pub enum MessageType {
            $($variant,)*
            Unknown,
        }

        impl<'a> From<&Message<'a>> for MessageType {
            fn from(value: &Message<'a>) -> Self {
                match value {
                    $(Message::$variant(_) => Self::$variant,)*
                    Message::Unknown(_) => Self::Unknown,
                }
            }
        }

        impl<'a> Message<'a> {
            pub(crate) fn parse(code: &'a str, fields: &'a str) -> Result<Self, NmeaMessageError> {
                Ok(match code {
                    $($code => Message::$variant(<$type>::parse(fields)?),)*
                    _ => Message::Unknown(code),
                })
            }

            pub fn code(&self) -> &str {
                match self {
                    $(Message::$variant(_) => $code,)*
                    Message::Unknown(code) => code,
                }
            }
        }

        impl NmeaEncode for Message<'_> {
            fn encoded_len(&self) -> usize {
                match self {
                    $(Message::$variant(m) => m.encoded_len(),)*
                    Message::Unknown(_) => 0,
                }
            }

            fn encode(&self, buf: &mut [u8]) -> usize {
                match self {
                    $(Message::$variant(m) => m.encode(buf),)*
                    Message::Unknown(_) => 0,
                }
            }
        }
    }
}

define_messages!(
    Aam => Aam<'a>, "AAM",
    Gga => Gga<'a>, "GGA",
    Gll => Gll<'a>, "GLL",
    Gns => Gns<'a>, "GNS",
    Gsa => Gsa<'a>, "GSA",
    Gst => Gst<'a>, "GST",
    Gsv => Gsv<'a>, "GSV",
    Rmc => Rmc<'a>, "RMC",
    Txt => Txt<'a>, "TXT",
    Vtg => Vtg<'a>, "VTG",
    Zda => Zda<'a>, "ZDA",
);