1use std::fmt::{Display, Formatter};
11use std::str::FromStr;
12
13use serde::Serialize;
14
15use crate::AprsError;
16use crate::Timestamp;
17use crate::status_comment::StatusComment;
18
19#[derive(PartialEq, Debug, Clone, Serialize)]
20pub struct AprsStatus {
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub timestamp: Option<Timestamp>,
23 #[serde(flatten)]
24 pub comment: StatusComment,
25}
26
27impl FromStr for AprsStatus {
28 type Err = AprsError;
29
30 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
31 let timestamp = if s.len() >= 7 {
34 s[0..7].parse::<Timestamp>().ok()
35 } else {
36 None
37 };
38 let comment = if timestamp.is_some() { &s[7..] } else { s };
39
40 Ok(AprsStatus {
41 timestamp,
42 comment: comment.parse::<StatusComment>().unwrap(),
43 })
44 }
45}
46
47impl Display for AprsStatus {
48 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
49 write!(f, ">")?;
50
51 if let Some(ts) = &self.timestamp {
52 write!(f, "{ts}")?;
53 }
54 write!(f, "{:#?}", self.comment)?;
55
56 Ok(())
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use csv::WriterBuilder;
63 use std::io::stdout;
64
65 use super::*;
66
67 #[test]
68 fn parse_without_timestamp_or_comment() {
69 let result = "".parse::<AprsStatus>().unwrap();
70 assert_eq!(result.timestamp, None);
71 assert_eq!(result.comment, StatusComment::default());
72 }
73
74 #[test]
75 fn parse_with_timestamp_without_comment() {
76 let result = "312359z".parse::<AprsStatus>().unwrap();
77 assert_eq!(result.timestamp, Some(Timestamp::DDHHMM(31, 23, 59)));
78 assert_eq!(result.comment, StatusComment::default());
79 }
80
81 #[test]
82 fn parse_without_timestamp_with_comment() {
83 let result = "Hi there!".parse::<AprsStatus>().unwrap();
84 assert_eq!(result.timestamp, None);
85 assert_eq!(result.comment.unparsed.unwrap(), "Hi there!");
86 }
87
88 #[test]
89 fn parse_with_timestamp_and_comment() {
90 let result = "235959hHi there!".parse::<AprsStatus>().unwrap();
91 assert_eq!(result.timestamp, Some(Timestamp::HHMMSS(23, 59, 59)));
92 assert_eq!(result.comment.unparsed.unwrap(), "Hi there!");
93 }
94
95 #[ignore = "status_comment serialization not implemented"]
96 #[test]
97 fn test_serialize() {
98 let aprs_position = "235959hHi there!".parse::<AprsStatus>().unwrap();
99 let mut wtr = WriterBuilder::new().from_writer(stdout());
100 wtr.serialize(aprs_position).unwrap();
101 wtr.flush().unwrap();
102 }
103}