ogn_parser/
server_comment.rs

1use std::net::IpAddr;
2use std::str::FromStr;
3
4use chrono::prelude::*;
5use serde::Serialize;
6
7use crate::AprsError;
8
9#[derive(PartialEq, Debug, Clone, Serialize)]
10pub struct ServerComment {
11    pub version: String,
12    pub timestamp: DateTime<Utc>,
13    pub server: String,
14    pub ip_address: IpAddr,
15    pub port: u16,
16}
17
18impl FromStr for ServerComment {
19    type Err = AprsError;
20
21    fn from_str(s: &str) -> Result<Self, Self::Err> {
22        let mut parts = s.split_whitespace();
23
24        // Check for "# aprsc" prefix
25        if parts.next() != Some("#") || parts.next() != Some("aprsc") {
26            return Err(AprsError::InvalidServerComment(s.to_string()));
27        }
28
29        // Get the software version
30        let version = parts
31            .next()
32            .ok_or_else(|| AprsError::InvalidServerComment(s.to_string()))?;
33
34        // Parse the timestamp
35        let timestamp_str = parts.by_ref().take(5).collect::<Vec<_>>().join(" ");
36        let timestamp = DateTime::parse_from_rfc2822(&timestamp_str)
37            .map_err(|_| AprsError::InvalidServerComment(s.to_string()))?
38            .with_timezone(&Utc);
39
40        // Get the server name
41        let server = parts
42            .next()
43            .ok_or_else(|| AprsError::InvalidServerComment(s.to_string()))?;
44
45        // Get the IP address and port
46        let address = parts
47            .next()
48            .ok_or_else(|| AprsError::InvalidServerComment(s.to_string()))?;
49        let (ip_address, port) = address
50            .split_once(':')
51            .ok_or_else(|| AprsError::InvalidServerComment(address.to_string()))?;
52        let ip_address = ip_address
53            .parse::<IpAddr>()
54            .map_err(|_| AprsError::InvalidServerComment(s.to_string()))?;
55        let port = port
56            .parse::<u16>()
57            .map_err(|_| AprsError::InvalidServerComment(s.to_string()))?;
58
59        Ok(ServerComment {
60            version: version.to_string(),
61            timestamp,
62            server: server.to_string(),
63            ip_address,
64            port,
65        })
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72    use std::net::{IpAddr, Ipv4Addr};
73
74    #[test]
75    fn test_server_comment() {
76        let raw_message =
77            r"# aprsc 2.1.4-g408ed49 17 Mar 2018 09:30:36 GMT GLIDERN1 37.187.40.234:10152";
78        let result = raw_message.parse::<ServerComment>().unwrap();
79
80        assert_eq!(result.version, "2.1.4-g408ed49");
81        assert_eq!(
82            result.timestamp.to_string(),
83            "2018-03-17 09:30:36 UTC".to_string()
84        );
85        assert_eq!(result.server, "GLIDERN1");
86        assert_eq!(
87            result.ip_address,
88            IpAddr::V4(Ipv4Addr::new(37, 187, 40, 234))
89        );
90        assert_eq!(result.port, 10152);
91    }
92}