candump_parse/
lib.rs

1#![no_std]
2
3extern crate alloc;
4
5use alloc::{string::String, vec::Vec};
6use chumsky::prelude::*;
7
8/// The Linux `candump` file format frame.
9///
10/// # Example
11///
12/// Input:
13/// ```bash
14/// (0000000000.000000) vcan0 211#616C75653A353131
15/// (0000000000.000000) vcan0 212#0D0A4104
16/// ```
17#[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}