1use super::Message;
2use num::Integer;
3use std::fmt::Write;
4
5impl Message {
6 pub fn new() -> Self {
8 Message::default()
9 }
10
11 pub fn from_raw_slice(raw: &[u32]) -> Self {
13 Message {
14 raw: raw.to_vec(),
15 duty_cycle: None,
16 carrier: None,
17 }
18 }
19
20 pub fn extend(&mut self, other: &Message) {
22 assert!(self.raw.is_empty() || self.has_trailing_gap());
23
24 if self.carrier.is_none() {
25 self.carrier = other.carrier;
26 }
27
28 if self.duty_cycle.is_none() {
29 self.duty_cycle = other.duty_cycle;
30 }
31
32 self.raw.extend_from_slice(&other.raw);
33 }
34
35 pub fn has_trailing_gap(&self) -> bool {
37 let len = self.raw.len();
38
39 len > 0 && (len % 2) == 0
40 }
41
42 pub fn remove_trailing_gap(&mut self) {
44 if self.has_trailing_gap() {
45 self.raw.pop();
46 }
47 }
48
49 pub fn print_rawir(&self) -> String {
51 let mut s = String::new();
52
53 self.raw.iter().enumerate().for_each(|(i, v)| {
54 write!(
55 s,
56 "{}{}{}",
57 if i == 0 { "" } else { " " },
58 if i.is_even() { "+" } else { "-" },
59 v
60 )
61 .unwrap()
62 });
63
64 s
65 }
66
67 pub fn parse(s: &str) -> Result<Self, String> {
69 let mut raw = Vec::new();
70 let mut flash = true;
71
72 for e in s.split(|c: char| c.is_whitespace() || c == ',') {
73 if e.is_empty() {
74 continue;
75 }
76
77 let mut chars = e.chars().peekable();
78
79 match chars.peek() {
80 Some('+') => {
81 if !flash {
82 return Err("unexpected ‘+’ encountered".to_string());
83 }
84 chars.next();
85 }
86 Some('-') => {
87 if flash {
88 return Err("unexpected ‘-’ encountered".to_string());
89 }
90 chars.next();
91 }
92 Some(ch) if !ch.is_numeric() => {
93 return Err(format!("unexpected ‘{ch}’ encountered"));
94 }
95 _ => (),
96 }
97
98 let v = chars.collect::<String>();
99
100 let v = v.parse().map_err(|_| format!("invalid number ‘{v}’"))?;
101
102 if v == 0 {
103 return Err("nonsensical 0 length".to_string());
104 }
105
106 raw.push(v);
107
108 flash = !flash;
109 }
110
111 if raw.is_empty() {
112 return Err("missing length".to_string());
113 }
114
115 Ok(Message {
116 raw,
117 carrier: None,
118 duty_cycle: None,
119 })
120 }
121
122 pub fn parse_mode2(s: &str) -> Result<Message, (usize, String)> {
128 let mut res = Vec::new();
129 let mut carrier = None;
130 let mut line_no = 0;
131
132 for line in s.lines() {
133 line_no += 1;
134
135 let mut words = line.split_whitespace();
136
137 let is_pulse = match words.next() {
138 Some("pulse") => true,
139 Some("space") => false,
140 Some("timeout") => false,
141 Some("carrier") => {
142 match words.next() {
143 Some(w) => match w.parse() {
144 Ok(c) => {
145 if carrier.is_some() && carrier != Some(c) {
146 return Err((
147 line_no,
148 String::from("carrier specified more than once"),
149 ));
150 }
151
152 if c < 0 {
153 return Err((
154 line_no,
155 format!("negative carrier {c} does not make sense"),
156 ));
157 }
158
159 carrier = Some(c);
160 }
161 Err(_) => {
162 return Err((
163 line_no,
164 format!("carrier argument ‘{w}’ is not a number"),
165 ));
166 }
167 },
168 None => return Err((line_no, String::from("missing carrier value"))),
169 }
170
171 if let Some(w) = words.next() {
172 if !w.starts_with('#') && !w.starts_with("//") {
173 return Err((line_no, format!("unexpected ‘{w}’")));
174 }
175 }
176
177 continue;
178 }
179 Some(w) => {
180 if !w.starts_with('#') && !w.starts_with("//") {
181 return Err((line_no, format!("unexpected ‘{w}’")));
182 }
183 continue;
184 }
185 None => {
186 continue;
187 }
188 };
189
190 let value = match words.next() {
191 Some(w) => match w.parse() {
192 Ok(0) => {
193 return Err((line_no, "nonsensical 0 duration".to_string()));
194 }
195 Ok(n) => {
196 if n > 0xff_ff_ff {
197 return Err((line_no, format!("duration ‘{w}’ too long")));
198 }
199 n
200 }
201 Err(_) => {
202 return Err((line_no, format!("invalid duration ‘{w}’")));
203 }
204 },
205 None => {
206 return Err((line_no, "missing duration".to_string()));
207 }
208 };
209
210 if let Some(trailing) = words.next() {
211 return Err((line_no, format!("unexpected ‘{trailing}’")));
212 }
213
214 if is_pulse {
215 if res.len() % 2 == 1 {
216 *res.last_mut().unwrap() += value;
218 } else {
219 res.push(value);
220 }
221 } else if res.len() % 2 == 0 {
222 if let Some(last) = res.last_mut() {
224 *last += value;
225 }
226 } else {
227 res.push(value);
228 }
229 }
230
231 if res.is_empty() {
232 if line_no == 0 {
233 line_no = 1;
234 }
235 return Err((line_no, "missing pulse".to_string()));
236 }
237
238 Ok(Message {
239 duty_cycle: None,
240 carrier,
241 raw: res,
242 })
243 }
244}
245
246#[test]
247fn parse_mode2() {
248 assert_eq!(
249 Message::parse_mode2("").err(),
250 Some((1, "missing pulse".to_string()))
251 );
252 assert_eq!(
253 Message::parse_mode2("pulse 0").err(),
254 Some((1, "nonsensical 0 duration".to_string()))
255 );
256 assert_eq!(
257 Message::parse_mode2("pulse").err(),
258 Some((1, "missing duration".to_string()))
259 );
260 assert_eq!(
261 Message::parse_mode2("pulse abc").err(),
262 Some((1, "invalid duration ‘abc’".to_string()))
263 );
264 assert_eq!(
265 Message::parse_mode2("pulse 1\npulse 2").unwrap().raw,
266 vec!(3u32)
267 );
268 assert_eq!(
269 Message::parse_mode2("space 1\r\nspace 2\npulse 1\npulse 2")
270 .unwrap()
271 .raw,
272 vec!(3u32)
273 );
274 assert_eq!(
275 Message::parse_mode2("pulse 100\npulse 21\nspace 10\nspace 50")
276 .unwrap()
277 .raw,
278 vec!(121u32, 60u32)
279 );
280 assert_eq!(
281 Message::parse_mode2("polse 100\nspace 10\nspace 50").err(),
282 Some((1, "unexpected ‘polse’".to_string()))
283 );
284 assert_eq!(
285 Message::parse_mode2("pulse 100\nspace 10\npulse 50")
286 .unwrap()
287 .raw,
288 vec!(100u32, 10u32, 50u32)
289 );
290 assert_eq!(
291 Message::parse_mode2("pulse 100\nspace 10\npulse 50\nspace 34134134").err(),
292 Some((4, "duration ‘34134134’ too long".to_string()))
293 );
294 assert_eq!(
295 Message::parse_mode2("pulse 100\nspace 10\npulse 50 foobar\nspace 34134134").err(),
296 Some((3, "unexpected ‘foobar’".to_string()))
297 );
298 assert_eq!(
299 Message::parse_mode2("pulse 100\nspace 10\ncarrier foobar\nspace 34134134").err(),
300 Some((3, "carrier argument ‘foobar’ is not a number".to_string()))
301 );
302 assert_eq!(
303 Message::parse_mode2("pulse 100\nspace 10\ncarrier\nspace 34134134").err(),
304 Some((3, "missing carrier value".to_string()))
305 );
306 assert_eq!(
307 Message::parse_mode2("pulse 100\nspace 10\ncarrier 500 x\nspace 34134134").err(),
308 Some((3, "unexpected ‘x’".to_string()))
309 );
310 assert_eq!(
311 Message::parse_mode2("pulse 100\nspace 10\npulse 50\ncarrier 500 // hiya\ntimeout 100000")
312 .unwrap(),
313 Message {
314 carrier: Some(500),
315 duty_cycle: None,
316 raw: vec!(100u32, 10u32, 50u32, 100000u32)
317 }
318 );
319}
320
321#[test]
322fn parse_test() {
323 assert_eq!(
324 Message::parse("+100 +100"),
325 Err("unexpected ‘+’ encountered".to_string())
326 );
327
328 assert_eq!(
329 Message::parse("+100 -100 -1"),
330 Err("unexpected ‘-’ encountered".to_string())
331 );
332
333 assert_eq!(
334 Message::parse("+100 -100"),
335 Ok(Message {
336 raw: vec!(100, 100),
337 duty_cycle: None,
338 carrier: None
339 })
340 );
341
342 assert_eq!(Message::parse(""), Err("missing length".to_string()));
343
344 assert_eq!(Message::parse("+a"), Err("invalid number ‘a’".to_string()));
345
346 assert_eq!(
347 Message::parse("+0"),
348 Err("nonsensical 0 length".to_string())
349 );
350
351 assert_eq!(
352 Message::parse("100 \n100\r +1"),
353 Ok(Message {
354 raw: vec!(100u32, 100u32, 1u32),
355 duty_cycle: None,
356 carrier: None
357 })
358 );
359 assert_eq!(
360 Message::parse("100,100,+1,-20000"),
361 Ok(Message {
362 raw: vec!(100u32, 100u32, 1u32, 20000u32),
363 duty_cycle: None,
364 carrier: None
365 })
366 );
367}
368
369#[test]
370fn print_test() {
371 let m = Message {
372 raw: vec![100, 50, 75],
373 carrier: None,
374 duty_cycle: None,
375 };
376
377 assert_eq!(m.print_rawir(), "+100 -50 +75");
378}