use arrayvec::ArrayString;
use chrono::{Duration, NaiveTime};
use nom::{bytes::complete::is_not, character::complete::char, combinator::opt};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "serde")]
use serde_with::As;
use crate::{
parse::{NmeaSentence, TEXT_PARAMETER_MAX_LEN},
sentences::utils::{array_string, parse_duration_hms, parse_hms},
Error, SentenceType,
};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
#[derive(Debug, PartialEq, Eq)]
pub struct ZfoData {
#[cfg_attr(feature = "defmt-03", defmt(Debug2Format))]
pub fix_time: Option<NaiveTime>,
#[cfg_attr(
feature = "serde",
serde(with = "As::<Option<serde_with::DurationSecondsWithFrac<f64>>>")
)]
#[cfg_attr(feature = "defmt-03", defmt(Debug2Format))]
pub fix_duration: Option<Duration>,
#[cfg_attr(feature = "defmt-03", defmt(Debug2Format))]
pub waypoint_id: Option<ArrayString<TEXT_PARAMETER_MAX_LEN>>,
}
fn do_parse_zfo(i: &str) -> Result<ZfoData, Error> {
let (i, fix_time) = opt(parse_hms)(i)?;
let (i, _) = char(',')(i)?;
let (i, fix_duration) = opt(parse_duration_hms)(i)?;
let (i, _) = char(',')(i)?;
let (_i, waypoint_id) = opt(is_not(",*"))(i)?;
let waypoint_id = waypoint_id
.map(array_string::<TEXT_PARAMETER_MAX_LEN>)
.transpose()?;
Ok(ZfoData {
fix_time,
fix_duration,
waypoint_id,
})
}
pub fn parse_zfo(sentence: NmeaSentence) -> Result<ZfoData, Error> {
if sentence.message_id != SentenceType::ZFO {
Err(Error::WrongSentenceHeader {
expected: SentenceType::ZFO,
found: sentence.message_id,
})
} else {
Ok(do_parse_zfo(sentence.data)?)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{parse::parse_nmea_sentence, Error};
fn run_parse_zfo(line: &str) -> Result<ZfoData, Error> {
let s = parse_nmea_sentence(line).expect("ZFO sentence initial parse failed");
assert_eq!(s.checksum, s.calc_checksum());
parse_zfo(s)
}
#[test]
fn test_parse_zfo() {
assert_eq!(
ZfoData {
fix_duration: Some(
Duration::hours(4)
+ Duration::minutes(23)
+ Duration::seconds(59)
+ Duration::milliseconds(170)
),
fix_time: NaiveTime::from_hms_milli_opt(14, 58, 32, 120),
waypoint_id: Some(ArrayString::from("WPT").unwrap()),
},
run_parse_zfo("$GPZFO,145832.12,042359.17,WPT*3E").unwrap()
);
assert_eq!(
ZfoData {
fix_duration: None,
fix_time: None,
waypoint_id: None,
},
run_parse_zfo("$GPZFO,,,*68").unwrap()
);
assert_eq!(
ZfoData {
fix_duration: Some(
Duration::hours(4)
+ Duration::minutes(23)
+ Duration::seconds(59)
+ Duration::milliseconds(170)
),
fix_time: None,
waypoint_id: None,
},
run_parse_zfo("$GPZFO,,042359.17,*49").unwrap()
);
}
#[test]
fn test_parse_zfo_with_too_long_waypoint() {
assert_eq!(
Error::ParameterLength { max_length: 64, parameter_length: 72 },
run_parse_zfo("$GPZFO,145832.12,042359.17,ABCDEFGHIJKLMNOPRSTUWXYZABCDEFGHIJKLMNOPRSTUWXYZABCDEFGHIJKLMNOPRSTUWXYZ*71").unwrap_err()
);
}
}