use crate::encoder::NmeaEncode;
use crate::macros::{write_byte, write_str};
use crate::message::NmeaMessageError;
use crate::number::NmeaNumber;
use crate::parser::NmeaParse;
#[derive(Debug)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Aam<'a> {
pub arrival_circle_entered: &'a str,
pub perpendicular_passed: &'a str,
pub radius: &'a str,
pub radius_units: &'a str,
pub waypoint_id: &'a str,
}
impl<'a> NmeaParse<'a> for Aam<'a> {
fn parse(fields: &'a str) -> Result<Self, NmeaMessageError> {
let mut f = fields.splitn(5, ',');
Ok(Self {
arrival_circle_entered: f.next().ok_or(NmeaMessageError::MissingField)?,
perpendicular_passed: f.next().ok_or(NmeaMessageError::MissingField)?,
radius: f.next().ok_or(NmeaMessageError::MissingField)?,
radius_units: f.next().ok_or(NmeaMessageError::MissingField)?,
waypoint_id: f.next().unwrap_or(""),
})
}
}
impl NmeaEncode for Aam<'_> {
fn encoded_len(&self) -> usize {
self.arrival_circle_entered.len()
+ self.perpendicular_passed.len()
+ self.radius.len()
+ self.radius_units.len()
+ self.waypoint_id.len()
+ 4
}
fn encode(&self, buf: &mut [u8]) -> usize {
let mut pos = 0;
write_str!(buf, pos, self.arrival_circle_entered);
write_byte!(buf, pos, b',');
write_str!(buf, pos, self.perpendicular_passed);
write_byte!(buf, pos, b',');
write_str!(buf, pos, self.radius);
write_byte!(buf, pos, b',');
write_str!(buf, pos, self.radius_units);
write_byte!(buf, pos, b',');
write_str!(buf, pos, self.waypoint_id);
pos
}
}
impl Aam<'_> {
#[must_use]
pub fn arrival_circle_entered(&self) -> bool {
self.arrival_circle_entered == "A"
}
#[must_use]
pub fn perpendicular_passed(&self) -> bool {
self.perpendicular_passed == "A"
}
#[must_use]
pub fn arrived(&self) -> bool {
self.arrival_circle_entered() || self.perpendicular_passed()
}
#[must_use]
pub fn radius(&self) -> Option<NmeaNumber> {
NmeaNumber::parse(self.radius)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn both_conditions_met() {
let aam = Aam::parse("A,A,0.10,N,WPTNME").unwrap();
assert!(aam.arrival_circle_entered());
assert!(aam.perpendicular_passed());
assert!(aam.arrived());
}
#[test]
fn only_circle_entered() {
let aam = Aam::parse("A,V,0.10,N,WPTNME").unwrap();
assert!(aam.arrival_circle_entered());
assert!(!aam.perpendicular_passed());
assert!(aam.arrived());
}
#[test]
fn only_perpendicular_passed() {
let aam = Aam::parse("V,A,0.10,N,WPTNME").unwrap();
assert!(!aam.arrival_circle_entered());
assert!(aam.perpendicular_passed());
assert!(aam.arrived());
}
#[test]
fn neither_condition_met() {
let aam = Aam::parse("V,V,0.50,N,HOME").unwrap();
assert!(!aam.arrival_circle_entered());
assert!(!aam.perpendicular_passed());
assert!(!aam.arrived());
}
#[test]
fn waypoint_id_parsed() {
let aam = Aam::parse("A,A,0.10,N,WPTNME").unwrap();
assert_eq!(aam.waypoint_id, "WPTNME");
}
#[test]
fn empty_waypoint_id() {
let aam = Aam::parse("A,V,0.10,N,").unwrap();
assert_eq!(aam.waypoint_id, "");
}
#[test]
fn missing_waypoint_id_field() {
let aam = Aam::parse("A,V,0.10,N").unwrap();
assert_eq!(aam.waypoint_id, "");
}
#[test]
fn missing_required_field_errors() {
assert!(Aam::parse("A,A,0.10").is_err());
assert!(Aam::parse("A").is_err());
assert!(Aam::parse("").is_err());
}
#[test]
fn nonstandard_status_value() {
let aam = Aam::parse("D,D,0.10,N,WPT").unwrap();
assert!(!aam.arrival_circle_entered());
assert!(!aam.perpendicular_passed());
}
#[test]
fn radius_parsed() {
let aam = Aam::parse("A,A,0.10,N,WPT").unwrap();
assert_eq!(aam.radius(), Some(NmeaNumber::new(10, 2)));
}
#[test]
fn encodes_fields() {
let aam = Aam::parse("A,A,0.10,N,WPTNME").unwrap();
let mut buf = [0u8; 64];
let len = aam.encode(&mut buf);
assert_eq!(&buf[..len], b"A,A,0.10,N,WPTNME");
}
#[test]
fn encode_round_trip() {
let input = "A,A,0.10,N,WPTNME";
let aam = Aam::parse(input).unwrap();
let mut buf = [0u8; 64];
let len = aam.encode(&mut buf);
let encoded = core::str::from_utf8(&buf[..len]).unwrap();
let aam2 = Aam::parse(encoded).unwrap();
assert_eq!(aam.arrival_circle_entered, aam2.arrival_circle_entered);
assert_eq!(aam.perpendicular_passed, aam2.perpendicular_passed);
assert_eq!(aam.radius, aam2.radius);
assert_eq!(aam.radius_units, aam2.radius_units);
assert_eq!(aam.waypoint_id, aam2.waypoint_id);
}
#[test]
fn encoded_len_matches_actual() {
let aam = Aam::parse("A,A,0.10,N,WPTNME").unwrap();
let mut buf = [0u8; 64];
let len = aam.encode(&mut buf);
assert_eq!(len, aam.encoded_len());
}
}