1use std::fmt::{Display, Formatter};
2use std::str::FromStr;
3
4use serde::Serialize;
5
6use crate::AprsError;
7
8#[derive(Eq, PartialEq, Debug, Clone, Serialize)]
9#[serde(into = "String")]
10pub struct Callsign {
11 pub call: String,
12 pub ssid: Option<u8>,
13}
14
15impl From<Callsign> for String {
16 fn from(val: Callsign) -> Self {
17 if let Some(ssid) = val.ssid {
18 format!("{}-{}", val.call, ssid)
19 } else {
20 val.call
21 }
22 }
23}
24
25impl Callsign {
26 pub fn new<T: Into<String>>(call: T, ssid: Option<u8>) -> Callsign {
27 Callsign {
28 call: call.into(),
29 ssid,
30 }
31 }
32}
33
34impl FromStr for Callsign {
35 type Err = AprsError;
36
37 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
38 let delimiter = s.find('-'); if delimiter.is_none() {
40 return Ok(Callsign::new(s, None));
41 }
42
43 let delimiter = delimiter.unwrap();
44 if delimiter == 0 {
45 return Err(AprsError::EmptyCallsign(s.to_owned()));
46 }
47
48 let (call, rest) = s.split_at(delimiter);
49 let part = &rest[1..rest.len()];
50
51 if part.is_empty() {
52 return Err(AprsError::EmptySSID(s.to_owned()));
53 }
54
55 let ssid = part.parse::<u8>().ok();
56
57 if ssid.is_some() {
58 Ok(Callsign::new(call, ssid))
59 } else {
60 Err(AprsError::InvalidSSID(s.to_owned()))
61 }
62 }
63}
64
65impl Display for Callsign {
66 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
67 write!(f, "{}", self.call)?;
68
69 if let Some(ssid) = &self.ssid {
70 write!(f, "-{}", ssid)?;
71 }
72
73 Ok(())
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn parse_callsign() {
83 assert_eq!("ABCDEF".parse(), Ok(Callsign::new("ABCDEF", None)));
84 }
85
86 #[test]
87 fn parse_with_ssid() {
88 assert_eq!("ABCDEF-42".parse(), Ok(Callsign::new("ABCDEF", Some(42))));
89 }
90
91 #[test]
92 fn empty_callsign() {
93 assert_eq!(
94 "-42".parse::<Callsign>(),
95 Err(AprsError::EmptyCallsign("-42".to_owned()))
96 );
97 }
98
99 #[test]
100 fn empty_ssid() {
101 assert_eq!(
102 "ABCDEF-".parse::<Callsign>(),
103 Err(AprsError::EmptySSID("ABCDEF-".to_owned()))
104 );
105 }
106
107 #[test]
108 fn invalid_ssid() {
109 assert_eq!(
110 "D-EKDF".parse::<Callsign>(),
111 Err(AprsError::InvalidSSID("D-EKDF".to_owned()))
112 );
113 }
114}