nmeasis 26.4.1

A memory-safe NMEA 0183 parser with a C FFI
Documentation
#[cfg_attr(feature = "c", repr(C))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct NmeaNumber {
    pub value: i64,
    pub scale: u8,
}

impl NmeaNumber {
    #[must_use]
    pub fn new(value: i64, scale: u8) -> Self {
        Self { value, scale }
    }

    #[must_use]
    pub fn parse(raw: &str) -> Option<Self> {
        let (int_part, frac_part) = match raw.find('.') {
            Some(i) => (&raw[..i], &raw[i + 1..]),
            None => (raw, ""),
        };

        let scale: u8 = frac_part.len().try_into().ok()?;

        let int_val: i64 = int_part.parse().ok()?;
        let frac_val: i64 = frac_part.parse().ok()?;

        let negative = int_part.starts_with('-');
        let value =
            int_val * 10i64.pow(u32::from(scale)) + if negative { -frac_val } else { frac_val };

        Some(Self { value, scale })
    }
}

#[cfg(feature = "float")]
impl From<NmeaNumber> for f32 {
    #[allow(
        clippy::cast_precision_loss,
        reason = "f32 precision loss is intentional"
    )]
    fn from(num: NmeaNumber) -> Self {
        let scale = 10i64.checked_pow(u32::from(num.scale)).unwrap_or(i64::MAX);
        num.value as f32 / scale as f32
    }
}

#[cfg(feature = "float")]
impl From<NmeaNumber> for f64 {
    #[allow(
        clippy::cast_precision_loss,
        reason = "f64 precision loss is intentional"
    )]
    fn from(num: NmeaNumber) -> Self {
        let scale = 10i64.checked_pow(u32::from(num.scale)).unwrap_or(i64::MAX);
        num.value as f64 / scale as f64
    }
}