1#![no_std]
2
3extern crate alloc;
4
5use alloc::{string::String, vec::Vec};
6use chumsky::prelude::*;
7
8#[derive(Debug)]
18pub struct CandumpFrame {
19 pub timestamp: core::time::Duration,
20 pub interface: String,
21 pub id: u32,
22 pub data: Vec<u8>,
23}
24
25pub fn parser() -> impl Parser<char, CandumpFrame, Error = Simple<char>> {
26 let time_unix = text::digits(10).map(|s: String| s.parse().unwrap());
27 let time_frac = text::digits(10).map(|s: String| s.parse().unwrap());
28
29 let timestamp = time_unix
30 .then_ignore(just("."))
31 .then(time_frac)
32 .map(|(unix, frac): (u64, u32)| core::time::Duration::new(unix, frac * 1000u32))
33 .delimited_by(just('('), just(')'));
34
35 let interface = text::ident();
36
37 let can_id = text::digits(16).map(|s: String| u32::from_str_radix(&s, 16).unwrap());
38 let can_data = text::digits(16).map(|s: String| {
39 s.as_bytes()
40 .chunks(2)
41 .map(|chunk| u8::from_str_radix(alloc::str::from_utf8(chunk).unwrap(), 16).unwrap())
42 .collect()
43 });
44
45 let frame = can_id.then_ignore(just("#")).then(can_data);
46
47 timestamp
48 .then_ignore(just(" "))
49 .then(interface)
50 .then_ignore(just(" "))
51 .then(frame)
52 .map(|((timestamp, interface), (id, data))| CandumpFrame {
53 timestamp,
54 interface,
55 id,
56 data,
57 })
58}
59
60#[cfg(test)]
61mod tests {
62 use alloc::vec;
63
64 use super::*;
65
66 #[test]
67 fn test_parser() {
68 let input = "(0000000000.100000) vcan0 211#616C75653A353131";
69 let result = parser().parse(input);
70 assert!(result.is_ok());
71 let frame = result.unwrap();
72 assert_eq!(frame.timestamp.as_secs(), 0);
73 assert_eq!(frame.timestamp.subsec_millis(), 100);
74 assert_eq!(frame.interface, "vcan0");
75 assert_eq!(frame.id, 0x211);
76 assert_eq!(
77 frame.data,
78 vec![0x61, 0x6C, 0x75, 0x65, 0x3A, 0x35, 0x31, 0x31]
79 );
80 }
81}