dcan_db/
parser.rs

1use crate::errors::*;
2use ariadne::{sources, Color, Label, Report, ReportKind};
3use chumsky::prelude::*;
4use std::{collections::HashMap, fmt, fs, path::Path};
5
6// Error handling
7type Result<T> = std::result::Result<T, DbcError>;
8
9/// External CAN DBC representation
10#[derive(Clone, Debug, PartialEq, Default)]
11pub struct Dbc {
12    /// DBC file version
13    pub version: String,
14
15    /// Bus baud rate and BTR settings
16    pub speed: BusSpeed,
17
18    /// List of new symbols
19    pub symbols: Vec<String>,
20
21    /// List of nodes
22    pub nodes: Vec<String>,
23
24    /// Global value tables
25    pub value_tables: Vec<ValueTable>,
26
27    /// Bus frames
28    pub frames: Vec<Frame>,
29
30    /// Signal value descriptions
31    pub signal_values: Vec<ValueDescription>,
32}
33
34/// External frame representation
35#[derive(Debug, Default, Clone, PartialEq)]
36pub struct Frame {
37    /// Frame name (identifier)
38    pub name: String,
39
40    /// Transmitting node
41    pub transmitter: String,
42
43    /// Frame transmission ID
44    pub id: u32,
45
46    /// Frame length in bytes
47    pub length: u32,
48
49    /// List of signals within the frame
50    pub signals: Vec<Signal>,
51}
52
53/// External signal representation
54#[derive(Debug, Default, Clone, PartialEq)]
55pub struct Signal {
56    /// Signal name (identifier)
57    pub name: String,
58
59    /// Signal multiplexer
60    pub multiplexer: Option<String>,
61
62    /// Start bit
63    pub start: u32,
64
65    /// Length in bits
66    pub length: u32,
67
68    /// Endianness (little = true)
69    pub little_endian: bool,
70
71    /// Signed/unsigned (signed = true)
72    pub signed: bool,
73
74    /// Display scale
75    pub scale: f32,
76
77    /// Display offset
78    pub offset: f32,
79
80    /// Display min
81    pub min: f32,
82
83    /// Display max
84    pub max: f32,
85
86    /// Display unit
87    pub unit: String,
88
89    /// Signal receiver
90    pub receiver: Vec<String>,
91}
92
93/// External value table representation
94#[derive(Debug, Default, Clone, PartialEq)]
95pub struct ValueTable {
96    /// Value table name (identifier)
97    pub name: String,
98
99    /// Enumerated values
100    pub values: HashMap<u32, String>,
101}
102
103#[derive(Debug, Default, Clone, PartialEq)]
104pub struct ValueDescription {
105    /// Frame transmission ID
106    pub id: u32,
107
108    /// Signal table name (identifier)
109    pub name: String,
110
111    /// Enumerated values
112    pub values: HashMap<u32, String>,
113}
114
115/// Internal value representation (for enumerated value tables)
116#[derive(Debug, Default, Clone, PartialEq)]
117struct Value {
118    /// Raw enumeration
119    value: u32,
120
121    /// Value description
122    description: String,
123}
124
125#[derive(Debug, Default, Clone, PartialEq)]
126pub struct BusSpeed {
127    /// Bus baud rate
128    baud: u32,
129
130    /// BTR1 register value
131    btr1: u32,
132
133    /// BTR2 register value
134    btr2: u32,
135}
136
137/// Top level DBC parser
138///
139/// A wrapper for the DBC parser
140pub fn parse_dbc(path: &'static str) -> Result<Dbc> {
141    let path = Path::new(path);
142    let fname = path
143        .file_name()
144        .unwrap_or_default()
145        .to_string_lossy()
146        .to_string();
147
148    let contents = match fs::read_to_string(path) {
149        Ok(val) => val,
150        Err(_) => return Err(DbcError::Read(fname)),
151    };
152
153    let result = dbc_parser()
154        .parse(&contents)
155        .into_result()
156        .unwrap_or_else(|errs| parse_failure(&errs[0], fname, &contents));
157
158    Ok(result)
159}
160
161/// DBC parser
162fn dbc_parser<'src>() -> impl Parser<'src, &'src str, Dbc, extra::Err<Rich<'src, char>>> {
163    version_parser()
164        .then(symbol_parser())
165        .then(baud_parser())
166        .then(node_parser())
167        .then(value_table_parser().repeated().collect::<Vec<_>>())
168        .then(frame_parser().repeated().collect::<Vec<_>>())
169        .then(value_description_parser().repeated().collect::<Vec<_>>())
170        .then_ignore(end())
171        .map(
172            |((((((version, symbols), speed), nodes), value_tables), frames), signal_values)| Dbc {
173                version,
174                symbols,
175                speed,
176                nodes,
177                value_tables,
178                frames,
179                signal_values,
180            },
181        )
182        .labelled("DBC")
183}
184
185/// Frame parser
186fn frame_parser<'src>() -> impl Parser<'src, &'src str, Frame, extra::Err<Rich<'src, char>>> {
187    let ident = text::ascii::ident().padded();
188
189    just("BO_")
190        .padded()
191        .ignore_then(int_parser())
192        .then(ident)
193        .then_ignore(just(":"))
194        .padded()
195        .then(int_parser())
196        .then(ident)
197        .padded()
198        .then(signal_parser().repeated().collect::<Vec<_>>())
199        .map(|((((id, name), length), node), signals)| Frame {
200            name: name.to_owned(),
201            id: id.try_into().unwrap(),
202            length: length.try_into().unwrap(),
203            transmitter: node.to_owned(),
204            signals,
205        })
206        .labelled("frame")
207}
208
209/// Signal parser
210fn signal_parser<'src>() -> impl Parser<'src, &'src str, Signal, extra::Err<Rich<'src, char>>> {
211    let ident = text::ascii::ident().and_is(just("SG_").or(just("BO_")).or(just("VAL_")).not()).padded();
212    // TODO (DWM): Can be int or float
213    let scale_offset = just("(")
214        .ignore_then(float_parser())
215        .then_ignore(just(","))
216        .then(float_parser())
217        .then_ignore(just(")"))
218        .padded()
219        .labelled("scale/offset");
220    // TODO (DWM): Can be int or float
221    let min_max = just("[")
222        .ignore_then(float_parser())
223        .then_ignore(just("|"))
224        .then(float_parser())
225        .then_ignore(just("]"))
226        .padded()
227        .labelled("min/max");
228
229    just("SG_")
230        .padded()
231        .ignore_then(ident)
232        .padded()
233        .then(ident.map(|val: &str| val.to_string()).padded().or_not())
234        .then_ignore(just(":"))
235        .padded()
236        .then(int_parser())
237        .then_ignore(just("|"))
238        .then(int_parser())
239        .then_ignore(just("@"))
240        .then(int_parser())
241        .then(just("+").or(just("-")))
242        .padded()
243        .then(scale_offset)
244        .then(min_max)
245        .padded()
246        .then(str_parser())
247        .padded()
248        .then(ident.then_ignore(just(",").or_not()).map(|val| val.to_string()).repeated().collect::<Vec<_>>())
249        .padded()
250        .map(
251            |(
252                ((((((((name, multiplexer), start), length), endianness), signed), scale_offset), min_max), unit),
253                receivers,
254            )| {
255                Signal {
256                    name: name.to_string(),
257                    multiplexer,
258                    start: start.try_into().unwrap(),
259                    length: length.try_into().unwrap(),
260                    little_endian: endianness == 1,
261                    signed: signed == "-",
262                    scale: scale_offset.0,
263                    offset: scale_offset.1,
264                    min: min_max.0,
265                    max: min_max.1,
266                    unit: unit.to_string(),
267                    receiver: receivers,
268                }
269            },
270        )
271        .labelled("signal")
272}
273
274/// Version parser
275fn version_parser<'src>() -> impl Parser<'src, &'src str, String, extra::Err<Rich<'src, char>>> {
276    just("VERSION")
277        .padded()
278        .ignore_then(str_parser())
279        .padded()
280        .map(|ver| ver)
281        .labelled("version")
282}
283
284/// Bus baud parser
285fn baud_parser<'src>() -> impl Parser<'src, &'src str, BusSpeed, extra::Err<Rich<'src, char>>> {
286    let speed = int_parser()
287        .then_ignore(just(":"))
288        .padded()
289        .then(int_parser())
290        .then_ignore(just(","))
291        .padded()
292        .then(int_parser())
293        .padded()
294        .then_ignore(just(";"))
295        // NOTE: No padding here as we might not match w/ this parsing block
296        .map(|((baud, btr1), btr2)| BusSpeed {
297            baud: baud.try_into().unwrap(),
298            btr1: btr1.try_into().unwrap(),
299            btr2: btr2.try_into().unwrap(),
300        });
301
302    just("BS_")
303        .padded()
304        .ignore_then(just(":"))
305        .padded()
306        .ignore_then(speed.or_not())
307        .padded()
308        .map(|result| result.unwrap_or_default())
309        .labelled("bus speed")
310}
311
312/// Symbol list parser
313fn symbol_parser<'src>() -> impl Parser<'src, &'src str, Vec<String>, extra::Err<Rich<'src, char>>>
314{
315    let ident = text::ascii::ident()
316        .and_is(just("BS_").not())
317        .padded()
318        .map(|val: &str| val.to_owned());
319    let nodes = ident.repeated().collect::<Vec<_>>();
320
321    just("NS_")
322        .padded()
323        .ignore_then(just(":"))
324        .padded()
325        .ignore_then(nodes)
326        .labelled("symbols")
327}
328
329/// Node list parser
330fn node_parser<'src>() -> impl Parser<'src, &'src str, Vec<String>, extra::Err<Rich<'src, char>>> {
331    let ident = text::ascii::ident()
332        .and_is(just("VAL_TABLE_").not())
333        .padded()
334        .map(|val: &str| val.to_owned());
335    let nodes = ident.repeated().collect::<Vec<_>>();
336
337    just("BU_")
338        .padded()
339        .ignore_then(just(":"))
340        .padded()
341        .ignore_then(nodes)
342        .padded()
343        .labelled("nodes")
344}
345
346/// Global value table parser
347fn value_table_parser<'src>(
348) -> impl Parser<'src, &'src str, ValueTable, extra::Err<Rich<'src, char>>> {
349    let ident = text::ascii::ident().padded();
350    let value = int_parser()
351        .padded()
352        .then(str_parser())
353        .map(|(value, description)| Value {
354            description,
355            value: value.try_into().unwrap(),
356        });
357
358    just("VAL_TABLE_")
359        .padded()
360        .ignore_then(ident)
361        .padded()
362        .then(value.repeated().collect::<Vec<_>>())
363        .then_ignore(just(";"))
364        .padded()
365        .map(|(name, values)| ValueTable {
366            name: name.to_owned(),
367            values: {
368                let vals = values.iter().map(|expr| expr.value.clone());
369                let descs = values.iter().map(|expr| expr.description.clone());
370
371                vals.zip(descs).collect()
372            },
373        })
374        .labelled("global value table")
375}
376
377/// Signal value table parser
378fn value_description_parser<'src>(
379) -> impl Parser<'src, &'src str, ValueDescription, extra::Err<Rich<'src, char>>> {
380    let ident = text::ascii::ident().padded();
381    let value = int_parser()
382        .padded()
383        .then(str_parser())
384        .map(|(value, description)| Value {
385            description,
386            value: value.try_into().unwrap(),
387        });
388
389    just("VAL_")
390        .padded()
391        .ignore_then(int_parser())
392        .then(ident)
393        .padded()
394        .then(value.repeated().collect::<Vec<_>>())
395        .then_ignore(just(";"))
396        .padded()
397        .map(|((id, name), values)| ValueDescription {
398            id: id.try_into().unwrap(),
399            name: name.to_owned(),
400            values: {
401                let vals = values.iter().map(|expr| expr.value.clone());
402                let descs = values.iter().map(|expr| expr.description.clone());
403
404                vals.zip(descs).collect()
405            },
406        })
407        .labelled("signal value description")
408}
409
410/// Integer parser
411fn int_parser<'src>() -> impl Parser<'src, &'src str, i32, extra::Err<Rich<'src, char>>> {
412    let sign = just("-").or_not();
413
414    sign.then(text::int(10))
415        .padded()
416        .map(|(s, i)| {
417            let s = s.unwrap_or_default().to_owned() + i;
418
419            s.parse::<i32>().unwrap()
420        })
421        .labelled("integer")
422}
423
424/// Floating point parser
425fn float_parser<'src>() -> impl Parser<'src, &'src str, f32, extra::Err<Rich<'src, char>>> {
426    let digits = text::digits::<_, extra::Err<Rich<'src, char>>>(10).to_slice();
427
428    let fraction = just(".")
429        .ignore_then(digits)
430        .or_not()
431        .map(|val| val.unwrap_or_default())
432        .labelled("denominator");
433
434    let exponent = just("E")
435        .then(just("-").or(just("+")).or_not())
436        .then(digits)
437        .or_not()
438        .map(|e| {
439            let ((e, sign), int) = e.unwrap_or_default();
440
441            e.to_owned() + sign.unwrap_or_default() + int
442        })
443        .labelled("exponent");
444
445    int_parser()
446        .then(fraction)
447        .then(exponent)
448        .padded()
449        .map(|((int, frac), exp)| {
450            let val = int.to_string() + "." + &frac + &exp;
451            val.parse::<f32>().unwrap()
452        })
453        .labelled("float")
454}
455
456/// String literal parser
457fn str_parser<'src>() -> impl Parser<'src, &'src str, String, extra::Err<Rich<'src, char>>> {
458    just('"')
459        .ignore_then(none_of('"').repeated().to_slice())
460        .then_ignore(just('"'))
461        .padded()
462        .map(|val: &str| val.to_owned())
463        .labelled("string")
464}
465
466/// Failure parser
467/// Taken directly from Chumsky examples:
468///   https://github.com/zesterer/chumsky/blob/main/examples/mini_ml.rs#L407
469fn parse_failure(err: &Rich<impl fmt::Display>, fname: String, src: &str) -> ! {
470    failure(
471        err.reason().to_string(),
472        fname,
473        (
474            err.found()
475                .map(|c| c.to_string())
476                .unwrap_or_else(|| "end of input".to_string()),
477            *err.span(),
478        ),
479        err.contexts()
480            .map(|(l, s)| (format!("while parsing this {l}"), *s)),
481        src,
482    )
483}
484
485/// Failure builder
486/// Taken directly from Chumsky examples:
487///   https://github.com/zesterer/chumsky/blob/main/examples/mini_ml.rs#L407
488fn failure(
489    msg: String,
490    fname: String,
491    label: (String, SimpleSpan),
492    extra_labels: impl IntoIterator<Item = (String, SimpleSpan)>,
493    src: &str,
494) -> ! {
495    Report::build(ReportKind::Error, (fname.clone(), label.1.into_range()))
496        .with_config(ariadne::Config::new().with_index_type(ariadne::IndexType::Byte))
497        .with_message(&msg)
498        .with_label(
499            Label::new((fname.clone(), label.1.into_range()))
500                .with_message(label.0)
501                .with_color(Color::Red),
502        )
503        .with_labels(extra_labels.into_iter().map(|label2| {
504            Label::new((fname.clone(), label2.1.into_range()))
505                .with_message(label2.0)
506                .with_color(Color::Yellow)
507        }))
508        .finish()
509        .print(sources([(fname, src)]))
510        .unwrap();
511    std::process::exit(1)
512}
513
514#[cfg(test)]
515mod top_level_parsing {
516    use super::*;
517
518    /// Confirm the top-level parser runs
519    #[test]
520    fn does_run() {
521        let result = parse_dbc("./dbcs/simple.dbc");
522        assert!(result.is_ok());
523    }
524}
525
526#[cfg(test)]
527mod frame_parser {
528    use super::*;
529
530    /// Ensure simple frames are parsed correctly
531    #[test]
532    fn parse_frame() {
533        let result = frame_parser()
534            .parse("BO_ 780 DriverSeat: 8 XXX\n SG_ occupancyStatus : 16|3@1+ (1,0) [0|7] \"unit\" XXX\n")
535            .into_result();
536        assert!(result.is_ok());
537        let result = result.unwrap();
538        assert_eq!(result.name, "DriverSeat");
539        assert_eq!(result.id, 780);
540        assert_eq!(result.length, 8);
541        assert_eq!(result.transmitter, "XXX");
542        assert_eq!(result.signals[0].name, "occupancyStatus");
543    }
544}
545
546#[cfg(test)]
547mod signal_parser {
548    use super::*;
549
550    /// Ensure signals with integer values are parsed correctly
551    #[test]
552    fn parse_signal_int() {
553        let result = signal_parser()
554            .parse("SG_ test1 : 0|4@1+ (1,0) [0|7] \"units\" XXX\n")
555            .into_result();
556        assert!(result.is_ok());
557        let result = result.unwrap();
558        assert_eq!(result.name, "test1");
559        assert_eq!(result.multiplexer, None);
560        assert_eq!(result.start, 0);
561        assert_eq!(result.length, 4);
562        assert_eq!(result.little_endian, true);
563        assert_eq!(result.signed, false);
564        assert_eq!(result.scale, 1.);
565        assert_eq!(result.offset, 0.);
566        assert_eq!(result.min, 0.);
567        assert_eq!(result.max, 7.);
568        assert_eq!(result.unit, "units");
569        assert_eq!(result.receiver[0], "XXX");
570    }
571
572    /// Ensure signals with floating point values are parsed correctly
573    #[test]
574    fn parse_signal_float() {
575        let result = signal_parser()
576            .parse("SG_ test2 : 16|3@0- (1.0,16.3) [0.7|32.9] \"units\" XXX\n")
577            .into_result();
578        assert!(result.is_ok());
579        let result = result.unwrap();
580        assert_eq!(result.name, "test2");
581        assert_eq!(result.multiplexer, None);
582        assert_eq!(result.start, 16);
583        assert_eq!(result.length, 3);
584        assert_eq!(result.little_endian, false);
585        assert_eq!(result.signed, true);
586        assert_eq!(result.scale, 1.);
587        assert_eq!(result.offset, 16.3);
588        assert_eq!(result.min, 0.7);
589        assert_eq!(result.max, 32.9);
590        assert_eq!(result.unit, "units");
591        assert_eq!(result.receiver[0], "XXX");
592    }
593
594    #[test]
595    fn parse_signal_float_extended() {
596        let result = signal_parser()
597            .parse("SG_ DAS_jerkMin : 18|9@1+ (0.03,-15.232) [-15.232|0.098] \"m/s^3\" NEO\n")
598            .into_result();
599        assert!(result.is_ok());
600        let result = result.unwrap();
601        assert_eq!(result.name, "DAS_jerkMin");
602        assert_eq!(result.multiplexer, None);
603        assert_eq!(result.start, 18);
604        assert_eq!(result.length, 9);
605        assert_eq!(result.little_endian, true);
606        assert_eq!(result.signed, false);
607        assert_eq!(result.scale, 0.03);
608        assert_eq!(result.offset, -15.232);
609        assert_eq!(result.min, -15.232);
610        assert_eq!(result.max, 0.098);
611        assert_eq!(result.unit, "m/s^3");
612        assert_eq!(result.receiver[0], "NEO");
613    }
614
615    #[test]
616    fn parse_signal_float_exponent() {
617        let result = signal_parser()
618            .parse("SG_ DAS_virtualLaneC2 : 32|8@1+ (2E-05,-0.0025) [-0.0025|0.0025] \"m-1\" NEO")
619            .into_result();
620        assert!(result.is_ok());
621        let result = result.unwrap();
622        assert_eq!(result.name, "DAS_virtualLaneC2");
623        assert_eq!(result.multiplexer, None);
624        assert_eq!(result.start, 32);
625        assert_eq!(result.length, 8);
626        assert_eq!(result.little_endian, true);
627        assert_eq!(result.signed, false);
628        assert_eq!(result.scale, 2E-05);
629        assert_eq!(result.offset, -0.0025);
630        assert_eq!(result.min, -0.0025);
631        assert_eq!(result.max, 0.0025);
632        assert_eq!(result.unit, "m-1");
633        assert_eq!(result.receiver[0], "NEO");
634    }
635
636    #[test]
637    fn parse_signal_multi_modes() {
638        let result = signal_parser()
639            .parse("SG_ ESP_BChecksum : 39|8@0+ (1,0) [0|255] \"\"  NEO,EPAS")
640            .into_result();
641        assert!(result.is_ok());
642        let result = result.unwrap();
643        assert_eq!(result.name, "ESP_BChecksum");
644        assert_eq!(result.multiplexer, None);
645        assert_eq!(result.start, 39);
646        assert_eq!(result.length, 8);
647        assert_eq!(result.little_endian, false);
648        assert_eq!(result.signed, false);
649        assert_eq!(result.scale, 1.);
650        assert_eq!(result.offset, 0.);
651        assert_eq!(result.min, 0.);
652        assert_eq!(result.max, 255.);
653        assert_eq!(result.unit, "");
654        assert_eq!(result.receiver[0], "NEO");
655        assert_eq!(result.receiver[1], "EPAS");
656    }
657
658    #[test]
659    fn parse_signal_multiplexed() {
660        let result: std::result::Result<Signal, Vec<Rich<'_, char>>> = signal_parser()
661            .parse("SG_ UI_autopilotControlIndex M : 0|3@1+ (1,0) [0|7] \"\" APP,APS")
662            .into_result();
663        assert!(result.is_ok());
664        let result = result.unwrap();
665        assert_eq!(result.name, "UI_autopilotControlIndex");
666        assert_eq!(result.multiplexer, Some("M".to_string()));
667        assert_eq!(result.start, 0);
668        assert_eq!(result.length, 3);
669        assert_eq!(result.little_endian, true);
670        assert_eq!(result.signed, false);
671        assert_eq!(result.scale, 1.);
672        assert_eq!(result.offset, 0.);
673        assert_eq!(result.min, 0.);
674        assert_eq!(result.max, 7.);
675        assert_eq!(result.unit, "");
676        assert_eq!(result.receiver[0], "APP");
677        assert_eq!(result.receiver[1], "APS");
678    }
679}
680
681#[cfg(test)]
682mod value_table_parser {
683    use super::*;
684
685    /// Ensure simple value tables are parsed correctly
686    #[test]
687    fn parse_value_table_simple() {
688        let result = value_table_parser()
689            .parse("VAL_TABLE_ StW_AnglHP_Spd 16383 \"SNA\" ;\n")
690            .into_result();
691        assert!(result.is_ok());
692        let result = result.unwrap();
693        assert_eq!(result.name, "StW_AnglHP_Spd");
694        let values = result.values;
695        assert_eq!(values[&16383], "SNA");
696    }
697
698    /// Ensure value tables are parsed correctly
699    #[test]
700    fn parse_value_table() {
701        let result = value_table_parser()
702            .parse("VAL_TABLE_ DI_velocityEstimatorState 4 \"VE_STATE_BACKUP_MOTOR\" 3 \"VE_STATE_BACKUP_WHEELS_B\" 2 \"VE_STATE_BACKUP_WHEELS_A\" 1 \"VE_STATE_WHEELS_NORMAL\" 0 \"VE_STATE_NOT_INITIALIZED\" ;\n")
703            .into_result();
704        assert!(result.is_ok());
705        let result = result.unwrap();
706        assert_eq!(result.name, "DI_velocityEstimatorState");
707        let values = result.values;
708        assert_eq!(values[&0], "VE_STATE_NOT_INITIALIZED");
709        assert_eq!(values[&4], "VE_STATE_BACKUP_MOTOR");
710    }
711}
712
713#[cfg(test)]
714mod misc_parsers {
715    use super::*;
716
717    /// Ensure basic versions are parsed correctly
718    #[test]
719    fn parse_version() {
720        let result = version_parser()
721            .parse("VERSION \"SIMPLE-DBC\"\n")
722            .into_result();
723        assert!(result.is_ok());
724        let result = result.unwrap();
725        assert_eq!(result, "SIMPLE-DBC");
726    }
727
728    /// Ensure empty baud rates are parsed correctly
729    #[test]
730    fn parse_baud_empty() {
731        let result = baud_parser().parse("BS_:\n").into_result();
732        assert!(result.is_ok());
733    }
734
735    /// Ensure full baud rates are parsed correctly
736    #[test]
737    fn parse_baud() {
738        let result = baud_parser()
739            .parse("BS_: 500000 : 32 , 16 ;\n")
740            .into_result();
741        assert!(result.is_ok());
742        let result = result.unwrap();
743        assert_eq!(result.baud, 500000);
744        assert_eq!(result.btr1, 32);
745        assert_eq!(result.btr2, 16);
746    }
747
748    /// Ensure basic symbol lists are parsed correctly
749    #[test]
750    fn parse_symbols() {
751        let result = symbol_parser()
752            .parse("NS_ : \n\tNS_DESC_\n\tCM_\n\tBA_DEF_\n\tBA_\n\tVAL_\n\tCAT_DEF_\n\tCAT_\n\tFILTER\n\tBA_DEF_DEF_\n\tEV_DATA_\n\tENVVAR_DATA_\n\tSGTYPE_\n\tSGTYPE_VAL_\n\tBA_DEF_SGTYPE_\n\tBA_SGTYPE_\n\tSIG_TYPE_REF_\n\tVAL_TABLE_\n\tSIG_GROUP_\n\tSIG_VALTYPE_\n\tSIGTYPE_VALTYPE_\n\tBO_TX_BU_\n\tBA_DEF_REL_\n\tBA_REL_\n\tBA_DEF_DEF_REL_\n\tBU_SG_REL_\n\tBU_EV_REL_\n\tBU_BO_REL_\n\tSG_MUL_VAL_\n")
753            .into_result();
754        assert!(result.is_ok());
755        let result = result.unwrap();
756        assert_eq!(result[0], "NS_DESC_");
757        assert_eq!(result[27], "SG_MUL_VAL_");
758    }
759
760    /// Ensure basic node lists are parsed correctly
761    #[test]
762    fn parse_nodes() {
763        let result = node_parser()
764            .parse("BU_:\n\tNEO\n\tMCU\n\tGTW\n\tEPAS\n\tDI\n\tESP\n\tSBW\n\tSTW\n\tAPP\n\tDAS\n\tXXX\n")
765            .into_result();
766        assert!(result.is_ok());
767        let result = result.unwrap();
768        assert_eq!(result[0], "NEO");
769        assert_eq!(result[10], "XXX");
770    }
771
772    /// Ensure signal value descriptions are parsed correctly
773    #[test]
774    fn parse_signal_descriptions() {
775        let result = value_description_parser()
776            .parse("VAL_ 1001 DAS_turnIndicatorRequestReason 6 \"DAS_ACTIVE_COMMANDED_LANE_CHANGE\" 5 \"DAS_CANCEL_FORK\" 4 \"DAS_CANCEL_LANE_CHANGE\" 3 \"DAS_ACTIVE_FORK\" 2 \"DAS_ACTIVE_SPEED_LANE_CHANGE\" 1 \"DAS_ACTIVE_NAV_LANE_CHANGE\" 0 \"DAS_NONE\" ;\n")
777            .into_result();
778        assert!(result.is_ok());
779        let result = result.unwrap();
780        assert_eq!(result.id, 1001);
781        assert_eq!(result.name, "DAS_turnIndicatorRequestReason");
782        let values = result.values;
783        assert_eq!(values[&0], "DAS_NONE");
784        assert_eq!(values[&6], "DAS_ACTIVE_COMMANDED_LANE_CHANGE");
785    }
786}
787
788#[cfg(test)]
789mod int_parsing {
790    use super::*;
791
792    /// Ensure unsigned ints are parsed correctly
793    #[test]
794    fn parse_int_unsigned() {
795        let result = int_parser().parse("16").into_result();
796        assert!(result.is_ok());
797        let result = result.unwrap();
798        assert_eq!(result, 16);
799    }
800
801    /// Ensure signed ints are parsed correctly
802    #[test]
803    fn parse_int_signed() {
804        let result = int_parser().parse("-32").into_result();
805        assert!(result.is_ok());
806        let result = result.unwrap();
807        assert_eq!(result, -32);
808    }
809}
810
811#[cfg(test)]
812mod float_parsing {
813    use super::*;
814
815    /// Ensure unsigned floats are parsed correctly
816    #[test]
817    fn parse_float_unsigned() {
818        let result = float_parser().parse("1.5").into_result().unwrap();
819        assert_eq!(result, 1.5);
820    }
821
822    /// Ensure signed floats are parsed correctly
823    #[test]
824    fn parse_float_signed() {
825        let result = float_parser().parse("-1.5").into_result().unwrap();
826        assert_eq!(result, -1.5);
827    }
828
829    /// Ensure simple floats (no numerator) are parsed correctly
830    #[test]
831    fn parse_float_simple() {
832        let result = float_parser().parse("10").into_result().unwrap();
833        assert_eq!(result, 10.);
834    }
835}