1use std::fmt;
2
3use crate::util::DisplayOption;
4use crate::util::Manufacturer;
5use crate::util::ParseError;
6
7#[derive(Debug, PartialEq, Eq)]
9pub struct ARecord<'a> {
10 pub manufacturer: Manufacturer<'a>,
11 pub unique_id: &'a str,
12 pub id_extension: Option<&'a str>,
13}
14
15impl<'a> ARecord<'a> {
16 pub fn new(
17 manufacturer: Manufacturer<'a>,
18 unique_id: &'a str,
19 id_extension: Option<&'a str>,
20 ) -> ARecord<'a> {
21 ARecord {
22 manufacturer,
23 unique_id,
24 id_extension,
25 }
26 }
27
28 pub fn parse(line: &'a str) -> Result<Self, ParseError> {
39 assert_eq!(&line[0..1], "A");
40
41 if line.len() < 7 {
42 return Err(ParseError::SyntaxError);
43 }
44
45 if line.bytes().skip(2).take(5).all(|b| b.is_ascii_digit()) {
50 let manufacturer_byte = line.as_bytes()[1];
51 if !manufacturer_byte.is_ascii() {
52 return Err(ParseError::NonASCIICharacters);
53 }
54
55 let id_extension = if line.len() > 7 {
56 Some(&line[7..])
57 } else {
58 None
59 };
60
61 let manufacturer = Manufacturer::parse_single_char(manufacturer_byte);
62 return Ok(ARecord::new(manufacturer, &line[2..7], id_extension));
63 }
64
65 if line.len() >= 9 && line.bytes().skip(4).take(5).all(|b| b.is_ascii_digit()) {
70 if !line.bytes().take(4).all(|b| b.is_ascii()) {
71 return Err(ParseError::NonASCIICharacters);
72 }
73
74 let id_extension = if line.len() > 9 {
75 Some(&line[9..])
76 } else {
77 None
78 };
79
80 let manufacturer = Manufacturer::parse_triple_char(&line[1..4]);
81 return Ok(ARecord::new(manufacturer, &line[4..9], id_extension));
82 }
83
84 if !line.bytes().take(7).all(|b| b.is_ascii()) {
85 return Err(ParseError::NonASCIICharacters);
86 }
87
88 let manufacturer = Manufacturer::parse_triple_char(&line[1..4]);
89 let unique_id = &line[4..7];
90 let id_extension = if line.len() > 7 {
91 Some(&line[7..])
92 } else {
93 None
94 };
95
96 Ok(ARecord::new(manufacturer, unique_id, id_extension))
97 }
98}
99
100impl<'a> fmt::Display for ARecord<'a> {
101 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
103 write!(
104 f,
105 "A{}{}{}",
106 DisplayOption(self.manufacturer.to_triple_char()),
107 self.unique_id,
108 DisplayOption(self.id_extension)
109 )
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::{ARecord, Manufacturer};
116
117 #[test]
118 fn arecord_parse() {
119 assert_eq!(
120 ARecord::parse("ACAMWatFoo").unwrap(),
121 ARecord::new(Manufacturer::CambridgeAeroInstruments, "Wat", Some("Foo"))
122 );
123
124 assert_eq!(
125 ARecord::parse("ACAMWatFoo").unwrap(),
126 ARecord::new(Manufacturer::CambridgeAeroInstruments, "Wat", Some("Foo"))
127 );
128
129 assert_eq!(
131 ARecord::parse("AFLA6NG").unwrap(),
132 ARecord::new(Manufacturer::Flarm, "6NG", None)
133 );
134
135 assert_eq!(
137 ARecord::parse("AC00069").unwrap(),
138 ARecord::new(Manufacturer::CambridgeAeroInstruments, "00069", None)
139 );
140
141 assert_eq!(
143 ARecord::parse("ALXVK4AFLIGHT:1").unwrap(),
144 ARecord::new(Manufacturer::LxNav, "K4A", Some("FLIGHT:1"))
145 );
146
147 assert_eq!(
149 ARecord::parse("AFIL01460FLIGHT:1").unwrap(),
150 ARecord::new(Manufacturer::Filser, "01460", Some("FLIGHT:1"))
151 );
152
153 assert_eq!(
154 ARecord::parse("AX00000").unwrap(),
155 ARecord::new(Manufacturer::UnknownSingle(b'X'), "00000", None)
156 );
157
158 assert_eq!(
159 ARecord::parse("AXYZABC:foobar").unwrap(),
160 ARecord::new(Manufacturer::UnknownTriple("XYZ"), "ABC", Some(":foobar"))
161 );
162
163 assert_eq!(
164 ARecord::parse("AWIN000").unwrap(),
165 ARecord::new(Manufacturer::UnknownTriple("WIN"), "000", None)
166 );
167 }
168
169 #[test]
170 fn parse_with_invalid_char_boundary() {
171 assert!(ARecord::parse("A0ꢀ").is_err());
172 }
173
174 #[test]
175 fn arecord_fmt() {
176 assert_eq!(
177 format!(
178 "{}",
179 ARecord::new(Manufacturer::CambridgeAeroInstruments, "Wat", Some("Foo"))
180 ),
181 "ACAMWatFoo"
182 );
183 }
184
185 proptest! {
186 #[test]
187 #[allow(unused_must_use)]
188 fn parse_doesnt_crash(s in "A\\PC*") {
189 ARecord::parse(&s);
190 }
191 }
192}