nmeasis 26.4.1

A memory-safe NMEA 0183 parser with a C FFI
Documentation
use crate::{
    coordinates::NmeaCoordinates,
    encoder::NmeaEncode,
    faa::FaaMode,
    macros::{write_byte, write_str},
    message::NmeaMessageError,
    parser::NmeaParse,
    time::NmeaTime,
};

/// GLL - Geographic Position - Latitude/Longitude
#[derive(Debug)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Gll<'a> {
    /// 1. Latitude (DDMM.MMMM)
    pub latitude: &'a str,
    /// 2. N or S
    pub latitude_dir: &'a str,
    /// 3. Longitude (DDDMM.MMMM)
    pub longitude: &'a str,
    /// 4. E or W
    pub longitude_dir: &'a str,
    /// 5. UTC of this Position
    pub time: &'a str,
    /// 6. Status (A = Valid, V = Invalid)
    pub status: &'a str,
    /// 7. Faa Mode (>= NMEA 2.3)
    pub faa_mode: Option<&'a str>,
}

impl<'a> NmeaParse<'a> for Gll<'a> {
    fn parse(fields: &'a str) -> Result<Self, NmeaMessageError> {
        let mut f = fields.splitn(7, ',');
        Ok(Self {
            latitude: f.next().ok_or(NmeaMessageError::MissingField)?,
            latitude_dir: f.next().ok_or(NmeaMessageError::MissingField)?,
            longitude: f.next().ok_or(NmeaMessageError::MissingField)?,
            longitude_dir: f.next().ok_or(NmeaMessageError::MissingField)?,
            time: f.next().ok_or(NmeaMessageError::MissingField)?,
            status: f.next().ok_or(NmeaMessageError::MissingField)?,
            faa_mode: f.next().filter(|s| !s.is_empty()),
        })
    }
}

impl NmeaEncode for Gll<'_> {
    fn encoded_len(&self) -> usize {
        self.latitude.len()
            + self.latitude_dir.len()
            + self.longitude.len()
            + self.longitude_dir.len()
            + self.time.len()
            + self.status.len()
            + self.faa_mode.map_or(0, |fa| fa.len() + 1)
            + 5
    }

    fn encode(&self, buf: &mut [u8]) -> usize {
        let mut pos = 0;
        write_str!(buf, pos, self.latitude);
        write_byte!(buf, pos, b',');
        write_str!(buf, pos, self.latitude_dir);
        write_byte!(buf, pos, b',');
        write_str!(buf, pos, self.longitude);
        write_byte!(buf, pos, b',');
        write_str!(buf, pos, self.longitude_dir);
        write_byte!(buf, pos, b',');
        write_str!(buf, pos, self.time);
        write_byte!(buf, pos, b',');
        write_str!(buf, pos, self.status);

        if let Some(faa_mode) = self.faa_mode {
            write_byte!(buf, pos, b',');
            write_str!(buf, pos, faa_mode);
        }

        pos
    }
}

impl Gll<'_> {
    #[must_use]
    pub fn coordinates(&self) -> Option<NmeaCoordinates> {
        NmeaCoordinates::parse(
            self.latitude,
            self.latitude_dir,
            self.longitude,
            self.longitude_dir,
        )
    }

    #[must_use]
    pub fn time(&self) -> Option<NmeaTime> {
        NmeaTime::parse(self.time)
    }

    #[must_use]
    pub fn is_valid(&self) -> bool {
        self.status == "A"
    }

    pub fn faa_mode(&self) -> Option<FaaMode> {
        self.faa_mode.and_then(FaaMode::parse)
    }
}