dbc_rs/dbc/
parse.rs

1use crate::{
2    BitTiming, Dbc, Error, ExtendedMultiplexing, MAX_EXTENDED_MULTIPLEXING, MAX_MESSAGES,
3    MAX_NODES, MAX_SIGNALS_PER_MESSAGE, Message, Nodes, Parser, Result, Signal, ValueDescriptions,
4    Version,
5    compat::{BTreeMap, Comment, Name, ValueDescEntries, Vec},
6    dbc::{Messages, Validate, ValueDescriptionsMap},
7};
8#[cfg(feature = "attributes")]
9use crate::{
10    MAX_ATTRIBUTE_DEFINITIONS, MAX_ATTRIBUTE_VALUES,
11    attribute::{
12        AttributeDefinition, AttributeTarget, AttributeValue,
13        parse::{parse_attribute_assignment, parse_attribute_default},
14    },
15    dbc::{AttributeDefaultsMap, AttributeDefinitionsMap, AttributeValuesMap},
16};
17
18impl Dbc {
19    /// Parse a DBC file from a string slice
20    ///
21    /// # Examples
22    ///
23    /// ```rust,no_run
24    /// use dbc_rs::Dbc;
25    ///
26    /// let dbc_content = r#"VERSION "1.0"
27    ///
28    /// BU_: ECM
29    ///
30    /// BO_ 256 EngineData : 8 ECM
31    ///  SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm""#;
32    ///
33    /// let dbc = Dbc::parse(dbc_content)?;
34    /// assert_eq!(dbc.messages().len(), 1);
35    /// # Ok::<(), dbc_rs::Error>(())
36    /// ```
37    pub fn parse(data: &str) -> Result<Self> {
38        let mut parser = Parser::new(data.as_bytes())?;
39
40        let mut messages_buffer: Vec<Message, { MAX_MESSAGES }> = Vec::new();
41
42        let mut message_count_actual = 0;
43
44        // Parse version, nodes, and messages
45        use crate::{
46            BA_, BA_DEF_, BA_DEF_DEF_, BO_, BO_TX_BU_, BS_, BU_, CM_, EV_, NS_, SG_, SG_MUL_VAL_,
47            SIG_GROUP_, SIG_VALTYPE_, VAL_, VAL_TABLE_, VERSION,
48        };
49
50        let mut version: Option<Version> = None;
51        let mut bit_timing: Option<BitTiming> = None;
52        let mut nodes: Option<Nodes> = None;
53
54        // Type aliases for parsing buffers
55        type ValueDescBufferEntry = (Option<u32>, Name, ValueDescEntries);
56        type ValueDescBuffer = Vec<ValueDescBufferEntry, { MAX_MESSAGES }>;
57        type ExtMuxBuffer = Vec<ExtendedMultiplexing, { MAX_EXTENDED_MULTIPLEXING }>;
58
59        // Comment buffers - CM_ entries can appear anywhere in the file
60        // so we collect them first and apply after parsing messages
61        type MessageCommentBuffer = Vec<(u32, Comment), { MAX_MESSAGES }>;
62        // Signal comments: (message_id, signal_name, comment)
63        type SignalCommentBuffer = Vec<(u32, Name, Comment), { MAX_MESSAGES * 4 }>;
64
65        let mut value_descriptions_buffer: ValueDescBuffer = ValueDescBuffer::new();
66        let mut extended_multiplexing_buffer: ExtMuxBuffer = ExtMuxBuffer::new();
67
68        // Comment buffers
69        let mut db_comment: Option<Comment> = None;
70        // Node comments: (node_name, comment)
71        type NodeCommentBuffer = Vec<(Name, Comment), { MAX_NODES }>;
72        let mut node_comments_buffer: NodeCommentBuffer = NodeCommentBuffer::new();
73        let mut message_comments_buffer: MessageCommentBuffer = MessageCommentBuffer::new();
74        let mut signal_comments_buffer: SignalCommentBuffer = SignalCommentBuffer::new();
75
76        // Attribute buffers - BA_DEF_, BA_DEF_DEF_, BA_ entries can appear anywhere
77        #[cfg(feature = "attributes")]
78        type AttrDefBuffer = Vec<AttributeDefinition, { MAX_ATTRIBUTE_DEFINITIONS }>;
79        #[cfg(feature = "attributes")]
80        type AttrDefaultBuffer = Vec<(Name, AttributeValue), { MAX_ATTRIBUTE_DEFINITIONS }>;
81        #[cfg(feature = "attributes")]
82        type AttrValueBuffer =
83            Vec<(Name, AttributeTarget, AttributeValue), { MAX_ATTRIBUTE_VALUES }>;
84
85        #[cfg(feature = "attributes")]
86        let mut attribute_definitions_buffer: AttrDefBuffer = AttrDefBuffer::new();
87        #[cfg(feature = "attributes")]
88        let mut attribute_defaults_buffer: AttrDefaultBuffer = AttrDefaultBuffer::new();
89        #[cfg(feature = "attributes")]
90        let mut attribute_values_buffer: AttrValueBuffer = AttrValueBuffer::new();
91
92        loop {
93            // Skip comments (lines starting with //)
94            parser.skip_newlines_and_spaces();
95            if parser.starts_with(b"//") {
96                parser.skip_to_end_of_line();
97                continue;
98            }
99
100            let keyword_result = parser.peek_next_keyword();
101            let keyword = match keyword_result {
102                Ok(kw) => kw,
103                Err(Error::UnexpectedEof { .. }) => break,
104                Err(Error::Expected { .. }) => {
105                    if parser.starts_with(b"//") {
106                        parser.skip_to_end_of_line();
107                        continue;
108                    }
109                    return Err(keyword_result.unwrap_err());
110                }
111                Err(e) => return Err(e),
112            };
113
114            // Save position after peek_next_keyword (which skips whitespace, so we're at the keyword)
115            let pos_at_keyword = parser.pos();
116
117            match keyword {
118                NS_ => {
119                    // Consume NS_ keyword
120                    let line = parser.line();
121                    parser
122                        .expect(crate::NS_.as_bytes())
123                        .map_err(|_| Error::expected_at("Failed to consume NS_ keyword", line))?;
124                    parser.skip_newlines_and_spaces();
125                    let _ = parser.expect(b":").ok();
126                    loop {
127                        parser.skip_newlines_and_spaces();
128                        if parser.is_empty() {
129                            break;
130                        }
131                        if parser.starts_with(b" ") || parser.starts_with(b"\t") {
132                            parser.skip_to_end_of_line();
133                            continue;
134                        }
135                        if parser.starts_with(b"//") {
136                            parser.skip_to_end_of_line();
137                            continue;
138                        }
139                        if parser.starts_with(BS_.as_bytes())
140                            || parser.starts_with(BU_.as_bytes())
141                            || parser.starts_with(BO_.as_bytes())
142                            || parser.starts_with(SG_.as_bytes())
143                            || parser.starts_with(VERSION.as_bytes())
144                        {
145                            break;
146                        }
147                        parser.skip_to_end_of_line();
148                    }
149                    continue;
150                }
151                BS_ => {
152                    // Parse bit timing section (usually empty)
153                    let parsed = BitTiming::parse(&mut parser)?;
154                    // Only store if not empty (has actual values)
155                    if !parsed.is_empty() {
156                        bit_timing = Some(parsed);
157                    }
158                    parser.skip_to_end_of_line();
159                    continue;
160                }
161                #[cfg(feature = "attributes")]
162                BA_DEF_ => {
163                    // Parse attribute definition: BA_DEF_ [object_type] "attr_name" value_type ;
164                    let _ = parser.expect(BA_DEF_.as_bytes()).ok();
165                    if let Some(def) = AttributeDefinition::parse(&mut parser) {
166                        let _ = attribute_definitions_buffer.push(def);
167                    }
168                    parser.skip_to_end_of_line();
169                    continue;
170                }
171                #[cfg(feature = "attributes")]
172                BA_DEF_DEF_ => {
173                    // Parse attribute default: BA_DEF_DEF_ "attr_name" value ;
174                    let _ = parser.expect(BA_DEF_DEF_.as_bytes()).ok();
175                    if let Some((name, value)) = parse_attribute_default(&mut parser) {
176                        let _ = attribute_defaults_buffer.push((name, value));
177                    }
178                    parser.skip_to_end_of_line();
179                    continue;
180                }
181                #[cfg(feature = "attributes")]
182                BA_ => {
183                    // Parse attribute value: BA_ "attr_name" [object_ref] value ;
184                    let _ = parser.expect(BA_.as_bytes()).ok();
185                    if let Some((name, target, value)) = parse_attribute_assignment(&mut parser) {
186                        let _ = attribute_values_buffer.push((name, target, value));
187                    }
188                    parser.skip_to_end_of_line();
189                    continue;
190                }
191                #[cfg(not(feature = "attributes"))]
192                BA_DEF_ | BA_DEF_DEF_ | BA_ => {
193                    // Skip attribute entries when feature is disabled
194                    let _ = parser.expect(keyword.as_bytes()).ok();
195                    parser.skip_to_end_of_line();
196                    continue;
197                }
198                VAL_TABLE_ | SIG_GROUP_ | SIG_VALTYPE_ | EV_ | BO_TX_BU_ => {
199                    // TODO: These DBC sections are recognized but not parsed:
200                    //   VAL_TABLE_   - Global value tables (rarely used)
201                    //   SIG_GROUP_   - Signal groups (rarely used)
202                    //   SIG_VALTYPE_ - Signal extended value types: float/double (medium priority)
203                    //   EV_          - Environment variables (rarely used)
204                    //   BO_TX_BU_    - Multiple message transmitters (rarely used)
205                    //
206                    // Not yet recognized (rarely used):
207                    //   ENVVAR_DATA_, SGTYPE_, BA_DEF_SGTYPE_, BA_SGTYPE_, SIG_TYPE_REF_,
208                    //   BA_DEF_REL_, BA_REL_, BA_DEF_DEF_REL_, BU_SG_REL_, BU_EV_REL_, BU_BO_REL_
209                    //
210                    // Consume keyword then skip to end of line
211                    let _ = parser.expect(keyword.as_bytes()).ok();
212                    parser.skip_to_end_of_line();
213                    continue;
214                }
215                CM_ => {
216                    // Parse CM_ comment entry
217                    // Formats:
218                    //   CM_ "general comment";
219                    //   CM_ BU_ node_name "comment";
220                    //   CM_ BO_ message_id "comment";
221                    //   CM_ SG_ message_id signal_name "comment";
222                    let _ = parser.expect(crate::CM_.as_bytes()).ok();
223                    parser.skip_newlines_and_spaces();
224
225                    // Determine comment type by peeking next token
226                    if parser.starts_with(b"\"") {
227                        // General database comment: CM_ "string";
228                        if parser.expect(b"\"").is_ok() {
229                            if let Ok(comment_bytes) = parser.take_until_quote(false, 1024) {
230                                if let Ok(comment_str) = core::str::from_utf8(comment_bytes) {
231                                    if let Ok(comment) = Comment::try_from(comment_str) {
232                                        db_comment = Some(comment);
233                                    }
234                                }
235                            }
236                        }
237                        parser.skip_to_end_of_line();
238                    } else if parser.starts_with(BU_.as_bytes()) {
239                        // Node comment: CM_ BU_ node_name "string";
240                        let _ = parser.expect(BU_.as_bytes()).ok();
241                        parser.skip_newlines_and_spaces();
242                        if let Ok(node_name_bytes) = parser.parse_identifier() {
243                            if let Ok(node_name) = Name::try_from(node_name_bytes) {
244                                parser.skip_newlines_and_spaces();
245                                if parser.expect(b"\"").is_ok() {
246                                    if let Ok(comment_bytes) = parser.take_until_quote(false, 1024)
247                                    {
248                                        if let Ok(comment_str) = core::str::from_utf8(comment_bytes)
249                                        {
250                                            if let Ok(comment) = Comment::try_from(comment_str) {
251                                                let _ =
252                                                    node_comments_buffer.push((node_name, comment));
253                                            }
254                                        }
255                                    }
256                                }
257                            }
258                        }
259                        parser.skip_to_end_of_line();
260                    } else if parser.starts_with(BO_.as_bytes()) {
261                        // Message comment: CM_ BO_ message_id "string";
262                        let _ = parser.expect(BO_.as_bytes()).ok();
263                        parser.skip_newlines_and_spaces();
264                        if let Ok(message_id) = parser.parse_u32() {
265                            parser.skip_newlines_and_spaces();
266                            if parser.expect(b"\"").is_ok() {
267                                if let Ok(comment_bytes) = parser.take_until_quote(false, 1024) {
268                                    if let Ok(comment_str) = core::str::from_utf8(comment_bytes) {
269                                        if let Ok(comment) = Comment::try_from(comment_str) {
270                                            let _ =
271                                                message_comments_buffer.push((message_id, comment));
272                                        }
273                                    }
274                                }
275                            }
276                        }
277                        parser.skip_to_end_of_line();
278                    } else if parser.starts_with(SG_.as_bytes()) {
279                        // Signal comment: CM_ SG_ message_id signal_name "string";
280                        let _ = parser.expect(SG_.as_bytes()).ok();
281                        parser.skip_newlines_and_spaces();
282                        if let Ok(message_id) = parser.parse_u32() {
283                            parser.skip_newlines_and_spaces();
284                            if let Ok(signal_name_bytes) = parser.parse_identifier() {
285                                if let Ok(signal_name) = Name::try_from(signal_name_bytes) {
286                                    parser.skip_newlines_and_spaces();
287                                    if parser.expect(b"\"").is_ok() {
288                                        if let Ok(comment_bytes) =
289                                            parser.take_until_quote(false, 1024)
290                                        {
291                                            if let Ok(comment_str) =
292                                                core::str::from_utf8(comment_bytes)
293                                            {
294                                                if let Ok(comment) = Comment::try_from(comment_str)
295                                                {
296                                                    let _ = signal_comments_buffer.push((
297                                                        message_id,
298                                                        signal_name,
299                                                        comment,
300                                                    ));
301                                                }
302                                            }
303                                        }
304                                    }
305                                }
306                            }
307                        }
308                        parser.skip_to_end_of_line();
309                    } else {
310                        // Unknown comment type, skip
311                        parser.skip_to_end_of_line();
312                    }
313                    continue;
314                }
315                SG_MUL_VAL_ => {
316                    // Consume SG_MUL_VAL_ keyword
317                    let line = parser.line();
318                    parser.expect(SG_MUL_VAL_.as_bytes()).map_err(|_| {
319                        Error::expected_at("Failed to consume SG_MUL_VAL_ keyword", line)
320                    })?;
321
322                    // Parse the extended multiplexing entry
323                    if let Some(ext_mux) = ExtendedMultiplexing::parse(&mut parser) {
324                        if extended_multiplexing_buffer.push(ext_mux).is_err() {
325                            // Buffer full - return error instead of silently dropping entries
326                            return Err(Error::Validation(Error::EXTENDED_MULTIPLEXING_TOO_MANY));
327                        }
328                    } else {
329                        // Parsing failed, skip to end of line
330                        parser.skip_to_end_of_line();
331                    }
332                    continue;
333                }
334                VAL_ => {
335                    // Consume VAL_ keyword
336                    let _ = parser.expect(crate::VAL_.as_bytes()).ok();
337                    // Parse VAL_ statement: VAL_ message_id signal_name value1 "desc1" value2 "desc2" ... ;
338                    // Note: message_id of -1 (0xFFFFFFFF) means the value descriptions apply to
339                    // all signals with this name in ANY message (global value descriptions)
340                    parser.skip_newlines_and_spaces();
341                    let message_id = match parser.parse_i64() {
342                        Ok(id) => {
343                            // -1 (0xFFFFFFFF) is the magic number for global value descriptions
344                            if id == -1 {
345                                None
346                            } else if id >= 0 && id <= u32::MAX as i64 {
347                                Some(id as u32)
348                            } else {
349                                parser.skip_to_end_of_line();
350                                continue;
351                            }
352                        }
353                        Err(_) => {
354                            parser.skip_to_end_of_line();
355                            continue;
356                        }
357                    };
358                    parser.skip_newlines_and_spaces();
359                    let signal_name = match parser.parse_identifier() {
360                        Ok(name) => match Name::try_from(name) {
361                            Ok(s) => s,
362                            Err(_) => {
363                                parser.skip_to_end_of_line();
364                                continue;
365                            }
366                        },
367                        Err(_) => {
368                            parser.skip_to_end_of_line();
369                            continue;
370                        }
371                    };
372                    // Parse value-description pairs
373                    let mut entries: ValueDescEntries = ValueDescEntries::new();
374                    loop {
375                        parser.skip_newlines_and_spaces();
376                        // Check for semicolon (end of VAL_ statement)
377                        if parser.starts_with(b";") {
378                            parser.expect(b";").ok();
379                            break;
380                        }
381                        // Parse value (as i64 first to handle negative values like -1, then convert to u64)
382                        // Note: -1 (0xFFFFFFFF) is the magic number for global value descriptions in message_id,
383                        // but values in VAL_ can also be negative
384                        let value = match parser.parse_i64() {
385                            Ok(v) => {
386                                // Handle -1 specially: convert to 0xFFFFFFFF (u32::MAX) instead of large u64
387                                if v == -1 { 0xFFFF_FFFFu64 } else { v as u64 }
388                            }
389                            Err(_) => {
390                                parser.skip_to_end_of_line();
391                                break;
392                            }
393                        };
394                        parser.skip_newlines_and_spaces();
395                        // Parse description string (expect quote, then take until quote)
396                        if parser.expect(b"\"").is_err() {
397                            parser.skip_to_end_of_line();
398                            break;
399                        }
400                        let description_bytes = match parser.take_until_quote(false, 1024) {
401                            Ok(bytes) => bytes,
402                            Err(_) => {
403                                parser.skip_to_end_of_line();
404                                break;
405                            }
406                        };
407                        let description = match core::str::from_utf8(description_bytes)
408                            .ok()
409                            .and_then(|s| Name::try_from(s).ok())
410                        {
411                            Some(desc) => desc,
412                            None => {
413                                parser.skip_to_end_of_line();
414                                break;
415                            }
416                        };
417                        let _ = entries.push((value, description));
418                    }
419                    if !entries.is_empty() {
420                        let _ = value_descriptions_buffer.push((message_id, signal_name, entries));
421                    }
422                    continue;
423                }
424                VERSION => {
425                    // Version::parse expects VERSION keyword, don't consume it here
426                    version = Some(Version::parse(&mut parser)?);
427                    continue;
428                }
429                BU_ => {
430                    // Nodes::parse expects BU_ keyword, create parser from original input including it
431                    parser.skip_to_end_of_line();
432                    let bu_input = &data.as_bytes()[pos_at_keyword..parser.pos()];
433                    let mut bu_parser = Parser::new(bu_input)?;
434                    nodes = Some(Nodes::parse(&mut bu_parser)?);
435                    continue;
436                }
437                BO_ => {
438                    // Check limit using MAX_MESSAGES constant
439                    if message_count_actual >= MAX_MESSAGES {
440                        return Err(parser.err_nodes(Error::NODES_TOO_MANY));
441                    }
442
443                    // Save parser position (at BO_ keyword, so Message::parse can consume it)
444                    let message_start_pos = pos_at_keyword;
445
446                    // Don't manually parse - just find where the header ends by looking for the colon and sender
447                    // We need to find the end of the header line to separate it from signals
448                    let header_line_end = {
449                        // Skip to end of line to find where header ends
450                        let mut temp_parser = Parser::new(&data.as_bytes()[pos_at_keyword..])?;
451                        // Skip BO_ keyword
452                        temp_parser.expect(crate::BO_.as_bytes()).ok();
453                        temp_parser.skip_whitespace().ok();
454                        temp_parser.parse_u32().ok(); // ID
455                        temp_parser.skip_whitespace().ok();
456                        temp_parser.parse_identifier().ok(); // name
457                        temp_parser.skip_whitespace().ok();
458                        temp_parser.expect(b":").ok(); // colon
459                        temp_parser.skip_whitespace().ok();
460                        temp_parser.parse_u8().ok(); // DLC
461                        temp_parser.skip_whitespace().ok();
462                        temp_parser.parse_identifier().ok(); // sender
463                        pos_at_keyword + temp_parser.pos()
464                    };
465
466                    // Now parse signals from the original parser
467                    parser.skip_to_end_of_line(); // Skip past header line
468
469                    let mut signals_array: Vec<Signal, { MAX_SIGNALS_PER_MESSAGE }> = Vec::new();
470
471                    // Parse signals until we find a non-signal line
472                    loop {
473                        parser.skip_newlines_and_spaces();
474
475                        // Use peek_next_keyword to check for SG_ keyword
476                        // peek_next_keyword correctly distinguishes SG_ from SG_MUL_VAL_ (checks longer keywords first)
477                        let keyword_result = parser.peek_next_keyword();
478                        let keyword = match keyword_result {
479                            Ok(kw) => kw,
480                            Err(Error::UnexpectedEof { .. }) => break,
481                            Err(_) => break, // Not a keyword, no more signals
482                        };
483
484                        // Only process SG_ signals here (SG_MUL_VAL_ is handled in main loop)
485                        if keyword != SG_ {
486                            break; // Not a signal, exit signal parsing loop
487                        }
488
489                        // Check limit before parsing
490                        if signals_array.len() >= MAX_SIGNALS_PER_MESSAGE {
491                            return Err(parser.err_message(Error::MESSAGE_TOO_MANY_SIGNALS));
492                        }
493
494                        // Parse signal - Signal::parse consumes SG_ itself
495                        match Signal::parse(&mut parser) {
496                            Ok(signal) => {
497                                signals_array.push(signal).map_err(|_| {
498                                    parser.err_receivers(Error::SIGNAL_RECEIVERS_TOO_MANY)
499                                })?;
500                                // Receivers::parse stops at newline but doesn't consume it
501                                // Consume it so next iteration starts at the next line
502                                if parser.at_newline() {
503                                    parser.skip_to_end_of_line();
504                                }
505                            }
506                            Err(_) => {
507                                // Parsing failed, skip to end of line and stop
508                                parser.skip_to_end_of_line();
509                                break;
510                            }
511                        }
512                    }
513
514                    // Restore parser to start of message line and use Message::parse
515                    // Create a new parser from the original input, but only up to the end of the header
516                    // (not including signals, so Message::parse doesn't complain about extra content)
517                    let message_input = &data.as_bytes()[message_start_pos..header_line_end];
518                    let mut message_parser = Parser::new(message_input)?;
519
520                    // Use Message::parse which will parse the header and use our signals
521                    let message = Message::parse(&mut message_parser, signals_array.as_slice())?;
522
523                    messages_buffer
524                        .push(message)
525                        .map_err(|_| parser.err_message(Error::NODES_TOO_MANY))?;
526                    message_count_actual += 1;
527                    continue;
528                }
529                SG_ => {
530                    // Orphaned signal (not inside a message) - skip it
531                    parser.skip_to_end_of_line();
532                    continue;
533                }
534                _ => {
535                    parser.skip_to_end_of_line();
536                    continue;
537                }
538            }
539        }
540
541        // Allow empty nodes (DBC spec allows empty BU_: line)
542        let mut nodes = nodes.unwrap_or_default();
543
544        // Apply node comments to nodes (consume buffer to avoid cloning)
545        for (node_name, comment) in node_comments_buffer {
546            nodes.set_node_comment(node_name.as_str(), comment);
547        }
548
549        // If no version was parsed, default to empty version
550        let version = version.or_else(|| {
551            static EMPTY_VERSION: &[u8] = b"VERSION \"\"";
552            let mut parser = Parser::new(EMPTY_VERSION).ok()?;
553            Version::parse(&mut parser).ok()
554        });
555
556        // Build value descriptions map for storage in Dbc (consume buffer to avoid cloning)
557        let value_descriptions_map = {
558            let mut map: BTreeMap<(Option<u32>, Name), ValueDescriptions, { MAX_MESSAGES }> =
559                BTreeMap::new();
560            for (message_id, signal_name, entries) in value_descriptions_buffer {
561                let key = (message_id, signal_name);
562                let value_descriptions = ValueDescriptions::new(entries);
563                let _ = map.insert(key, value_descriptions);
564            }
565            ValueDescriptionsMap::new(map)
566        };
567
568        // Build attribute maps from buffers (consume buffers to avoid cloning)
569        #[cfg(feature = "attributes")]
570        let (attribute_definitions, attribute_defaults, attribute_values) = {
571            use crate::attribute::AttributeDefinitions;
572
573            let attribute_definitions = AttributeDefinitionsMap::from_vec({
574                let mut defs: AttributeDefinitions = AttributeDefinitions::new();
575                for def in attribute_definitions_buffer {
576                    let _ = defs.push(def);
577                }
578                defs
579            });
580
581            let attribute_defaults = AttributeDefaultsMap::from_map({
582                let mut map: BTreeMap<Name, AttributeValue, { MAX_ATTRIBUTE_DEFINITIONS }> =
583                    BTreeMap::new();
584                for (name, value) in attribute_defaults_buffer {
585                    let _ = map.insert(name, value);
586                }
587                map
588            });
589
590            let attribute_values = AttributeValuesMap::from_map({
591                let mut map: BTreeMap<
592                    (Name, AttributeTarget),
593                    AttributeValue,
594                    { MAX_ATTRIBUTE_VALUES },
595                > = BTreeMap::new();
596                for (name, target, value) in attribute_values_buffer {
597                    let _ = map.insert((name, target), value);
598                }
599                map
600            });
601
602            (attribute_definitions, attribute_defaults, attribute_values)
603        };
604
605        // Apply comments to messages and signals (consume buffers to avoid cloning)
606        // Message comments are applied by matching message_id
607        for (message_id, comment) in message_comments_buffer {
608            for msg in messages_buffer.iter_mut() {
609                if msg.id() == message_id || msg.id_with_flag() == message_id {
610                    msg.set_comment(comment);
611                    break;
612                }
613            }
614        }
615
616        // Signal comments are applied by matching (message_id, signal_name)
617        for (message_id, signal_name, comment) in signal_comments_buffer {
618            for msg in messages_buffer.iter_mut() {
619                if msg.id() == message_id || msg.id_with_flag() == message_id {
620                    if let Some(signal) = msg.signals_mut().find_mut(signal_name.as_str()) {
621                        signal.set_comment(comment);
622                    }
623                    break;
624                }
625            }
626        }
627
628        // Validate messages (duplicate IDs, sender in nodes, etc.)
629        Validate::validate(
630            &nodes,
631            messages_buffer.as_slice(),
632            Some(&value_descriptions_map),
633            Some(extended_multiplexing_buffer.as_slice()),
634        )
635        .map_err(|e| {
636            crate::error::map_val_error(e, Error::message, || {
637                Error::message(Error::MESSAGE_ERROR_PREFIX)
638            })
639        })?;
640
641        // Construct directly from owned buffer (avoids cloning all messages)
642        let messages = Messages::from_vec(messages_buffer)?;
643
644        #[cfg(feature = "attributes")]
645        return Ok(Dbc::new(
646            version,
647            bit_timing,
648            nodes,
649            messages,
650            value_descriptions_map,
651            extended_multiplexing_buffer,
652            db_comment,
653            attribute_definitions,
654            attribute_defaults,
655            attribute_values,
656        ));
657
658        #[cfg(not(feature = "attributes"))]
659        Ok(Dbc::new(
660            version,
661            bit_timing,
662            nodes,
663            messages,
664            value_descriptions_map,
665            extended_multiplexing_buffer,
666            db_comment,
667        ))
668    }
669
670    /// Parse a DBC file from a byte slice
671    ///
672    /// # Examples
673    ///
674    /// ```rust,no_run
675    /// use dbc_rs::Dbc;
676    ///
677    /// let dbc_bytes = b"VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM";
678    /// let dbc = Dbc::parse_bytes(dbc_bytes)?;
679    /// println!("Parsed {} messages", dbc.messages().len());
680    /// # Ok::<(), dbc_rs::Error>(())
681    /// ```
682    pub fn parse_bytes(data: &[u8]) -> Result<Self> {
683        let content =
684            core::str::from_utf8(data).map_err(|_e| Error::expected(Error::INVALID_UTF8))?;
685        Dbc::parse(content)
686    }
687}
688
689#[cfg(test)]
690mod tests {
691    use crate::Dbc;
692
693    #[test]
694    fn test_parse_basic() {
695        let dbc_content = r#"VERSION "1.0"
696
697BU_: ECM
698
699BO_ 256 Engine : 8 ECM
700 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
701"#;
702        let dbc = Dbc::parse(dbc_content).unwrap();
703        assert_eq!(dbc.version().map(|v| v.as_str()), Some("1.0"));
704        assert!(dbc.nodes().contains("ECM"));
705        assert_eq!(dbc.messages().len(), 1);
706    }
707
708    #[test]
709    fn test_parse_bytes() {
710        let dbc_bytes = b"VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM";
711        let dbc = Dbc::parse_bytes(dbc_bytes).unwrap();
712        assert_eq!(dbc.version().map(|v| v.as_str()), Some("1.0"));
713        assert!(dbc.nodes().contains("ECM"));
714        assert_eq!(dbc.messages().len(), 1);
715    }
716
717    #[test]
718    fn test_parse_empty_nodes() {
719        let dbc_content = r#"VERSION "1.0"
720
721BU_:
722
723BO_ 256 Engine : 8 ECM
724"#;
725        let dbc = Dbc::parse(dbc_content).unwrap();
726        assert!(dbc.nodes().is_empty());
727    }
728
729    #[test]
730    fn test_parse_no_version() {
731        let dbc_content = r#"BU_: ECM
732
733BO_ 256 Engine : 8 ECM
734"#;
735        let dbc = Dbc::parse(dbc_content).unwrap();
736        // Should default to empty version
737        assert!(dbc.version().is_some());
738    }
739
740    #[test]
741    fn parses_real_dbc() {
742        let data = r#"VERSION "1.0"
743
744BU_: ECM TCM
745
746BO_ 256 Engine : 8 ECM
747 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
748 SG_ Temp : 16|8@1- (1,-40) [-40|215] "°C"
749
750BO_ 512 Brake : 4 TCM
751 SG_ Pressure : 0|16@1+ (0.1,0) [0|1000] "bar""#;
752
753        let dbc = Dbc::parse(data).unwrap();
754        assert_eq!(dbc.messages().len(), 2);
755        let mut messages_iter = dbc.messages().iter();
756        let msg0 = messages_iter.next().unwrap();
757        assert_eq!(msg0.signals().len(), 2);
758        let mut signals_iter = msg0.signals().iter();
759        assert_eq!(signals_iter.next().unwrap().name(), "RPM");
760        assert_eq!(signals_iter.next().unwrap().name(), "Temp");
761        let msg1 = messages_iter.next().unwrap();
762        assert_eq!(msg1.signals().len(), 1);
763        assert_eq!(msg1.signals().iter().next().unwrap().name(), "Pressure");
764    }
765
766    #[test]
767    fn test_parse_duplicate_message_id() {
768        use crate::Error;
769        // Test that parse also validates duplicate message IDs
770        let data = r#"VERSION "1.0"
771
772BU_: ECM
773
774BO_ 256 EngineData1 : 8 ECM
775 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
776
777BO_ 256 EngineData2 : 8 ECM
778 SG_ Temp : 16|8@0- (1,-40) [-40|215] "°C"
779"#;
780
781        let result = Dbc::parse(data);
782        assert!(result.is_err());
783        match result.unwrap_err() {
784            Error::Message { msg, .. } => {
785                assert!(msg.contains(Error::DUPLICATE_MESSAGE_ID));
786            }
787            _ => panic!("Expected Error::Message"),
788        }
789    }
790
791    #[test]
792    fn test_parse_sender_not_in_nodes() {
793        use crate::Error;
794        // Test that parse also validates message senders are in nodes list
795        let data = r#"VERSION "1.0"
796
797BU_: ECM
798
799BO_ 256 EngineData : 8 TCM
800 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
801"#;
802
803        let result = Dbc::parse(data);
804        assert!(result.is_err());
805        match result.unwrap_err() {
806            Error::Message { msg, .. } => {
807                assert!(msg.contains(Error::SENDER_NOT_IN_NODES));
808            }
809            _ => panic!("Expected Error::Message"),
810        }
811    }
812
813    #[test]
814    fn test_parse_empty_file() {
815        use crate::Error;
816        // Test parsing an empty file
817        let result = Dbc::parse("");
818        assert!(result.is_err());
819        match result.unwrap_err() {
820            Error::UnexpectedEof { .. } => {
821                // Empty file should result in unexpected EOF
822            }
823            _ => panic!("Expected Error::UnexpectedEof"),
824        }
825    }
826
827    #[test]
828    fn test_parse_bytes_invalid_utf8() {
829        use crate::Error;
830        // Invalid UTF-8 sequence
831        let invalid_bytes = &[0xFF, 0xFE, 0xFD];
832        let result = Dbc::parse_bytes(invalid_bytes);
833        assert!(result.is_err());
834        match result.unwrap_err() {
835            Error::Expected { msg, .. } => {
836                assert_eq!(msg, Error::INVALID_UTF8);
837            }
838            _ => panic!("Expected Error::Expected with INVALID_UTF8"),
839        }
840    }
841
842    #[test]
843    fn test_parse_without_version_with_comment() {
844        // DBC file with comment and no VERSION line
845        let data = r#"// This is a comment
846BU_: ECM
847
848BO_ 256 Engine : 8 ECM
849 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
850"#;
851        let dbc = Dbc::parse(data).unwrap();
852        assert_eq!(dbc.version().map(|v| v.as_str()), Some(""));
853    }
854
855    #[test]
856    fn test_parse_with_strict_boundary_check() {
857        // Test that strict mode (default) rejects signals that extend beyond boundaries
858        let data = r#"VERSION "1.0"
859
860BU_: ECM
861
862BO_ 256 Test : 8 ECM
863 SG_ CHECKSUM : 63|8@1+ (1,0) [0|255] ""
864"#;
865
866        // Default (strict) mode should fail
867        let result = Dbc::parse(data);
868        assert!(result.is_err());
869    }
870
871    #[cfg(feature = "std")]
872    #[test]
873    fn test_parse_val_value_descriptions() {
874        let data = r#"VERSION ""
875
876NS_ :
877
878BS_:
879
880BU_: Node1 Node2
881
882BO_ 100 Message1 : 8 Node1
883 SG_ Signal : 32|8@1- (1,0) [-1|4] "Gear" Node2
884
885VAL_ 100 Signal -1 "Reverse" 0 "Neutral" 1 "First" 2 "Second" 3 "Third" 4 "Fourth" ;
886"#;
887
888        let dbc = match Dbc::parse(data) {
889            Ok(dbc) => dbc,
890            Err(e) => panic!("Failed to parse DBC: {:?}", e),
891        };
892
893        // Verify basic structure
894        assert_eq!(dbc.messages().len(), 1);
895        let message = dbc.messages().iter().find(|m| m.id() == 100).unwrap();
896        assert_eq!(message.name(), "Message1");
897        assert_eq!(message.sender(), "Node1");
898
899        // Verify value descriptions
900        let value_descriptions = dbc
901            .value_descriptions_for_signal(100, "Signal")
902            .expect("Value descriptions should exist");
903        assert_eq!(value_descriptions.get(0xFFFFFFFF), Some("Reverse")); // -1 as u64
904        assert_eq!(value_descriptions.get(0), Some("Neutral"));
905        assert_eq!(value_descriptions.get(1), Some("First"));
906        assert_eq!(value_descriptions.get(2), Some("Second"));
907        assert_eq!(value_descriptions.get(3), Some("Third"));
908        assert_eq!(value_descriptions.get(4), Some("Fourth"));
909    }
910
911    #[cfg(feature = "std")]
912    #[test]
913    fn test_parse_val_global_value_descriptions() {
914        // Test global value descriptions (VAL_ -1) that apply to all signals with the same name
915        let data = r#"VERSION "1.0"
916
917NS_ :
918
919    VAL_
920
921BS_:
922
923BU_: ECU DASH
924
925BO_ 256 EngineData: 8 ECU
926 SG_ EngineRPM : 0|16@1+ (0.125,0) [0|8000] "rpm" Vector__XXX
927 SG_ DI_gear : 24|3@1+ (1,0) [0|7] "" Vector__XXX
928
929BO_ 512 DashboardDisplay: 8 DASH
930 SG_ DI_gear : 0|3@1+ (1,0) [0|7] "" Vector__XXX
931 SG_ SpeedDisplay : 8|16@1+ (0.01,0) [0|300] "km/h" Vector__XXX
932
933VAL_ -1 DI_gear 0 "INVALID" 1 "P" 2 "R" 3 "N" 4 "D" 5 "S" 6 "L" 7 "SNA" ;
934"#;
935
936        let dbc = match Dbc::parse(data) {
937            Ok(dbc) => dbc,
938            Err(e) => panic!("Failed to parse DBC: {:?}", e),
939        };
940
941        // Verify basic structure
942        assert_eq!(dbc.messages().len(), 2);
943
944        // Verify first message (EngineData)
945        let engine_msg = dbc.messages().iter().find(|m| m.id() == 256).unwrap();
946        assert_eq!(engine_msg.name(), "EngineData");
947        assert_eq!(engine_msg.sender(), "ECU");
948        let di_gear_signal1 = engine_msg.signals().find("DI_gear").unwrap();
949        assert_eq!(di_gear_signal1.name(), "DI_gear");
950        assert_eq!(di_gear_signal1.start_bit(), 24);
951
952        // Verify second message (DashboardDisplay)
953        let dash_msg = dbc.messages().iter().find(|m| m.id() == 512).unwrap();
954        assert_eq!(dash_msg.name(), "DashboardDisplay");
955        assert_eq!(dash_msg.sender(), "DASH");
956        let di_gear_signal2 = dash_msg.signals().find("DI_gear").unwrap();
957        assert_eq!(di_gear_signal2.name(), "DI_gear");
958        assert_eq!(di_gear_signal2.start_bit(), 0);
959
960        // Verify global value descriptions apply to DI_gear in message 256
961        let value_descriptions1 = dbc
962            .value_descriptions_for_signal(256, "DI_gear")
963            .expect("Global value descriptions should exist for DI_gear in message 256");
964
965        assert_eq!(value_descriptions1.get(0), Some("INVALID"));
966        assert_eq!(value_descriptions1.get(1), Some("P"));
967        assert_eq!(value_descriptions1.get(2), Some("R"));
968        assert_eq!(value_descriptions1.get(3), Some("N"));
969        assert_eq!(value_descriptions1.get(4), Some("D"));
970        assert_eq!(value_descriptions1.get(5), Some("S"));
971        assert_eq!(value_descriptions1.get(6), Some("L"));
972        assert_eq!(value_descriptions1.get(7), Some("SNA"));
973
974        // Verify global value descriptions also apply to DI_gear in message 512
975        let value_descriptions2 = dbc
976            .value_descriptions_for_signal(512, "DI_gear")
977            .expect("Global value descriptions should exist for DI_gear in message 512");
978
979        // Both should return the same value descriptions (same reference or same content)
980        assert_eq!(value_descriptions2.get(0), Some("INVALID"));
981        assert_eq!(value_descriptions2.get(1), Some("P"));
982        assert_eq!(value_descriptions2.get(2), Some("R"));
983        assert_eq!(value_descriptions2.get(3), Some("N"));
984        assert_eq!(value_descriptions2.get(4), Some("D"));
985        assert_eq!(value_descriptions2.get(5), Some("S"));
986        assert_eq!(value_descriptions2.get(6), Some("L"));
987        assert_eq!(value_descriptions2.get(7), Some("SNA"));
988
989        // Verify they should be the same instance (both reference the global entry)
990        // Since we store by (Option<u32>, &str), both should return the same entry
991        assert_eq!(value_descriptions1.len(), value_descriptions2.len());
992        assert_eq!(value_descriptions1.len(), 8);
993
994        // Verify other signals don't have value descriptions
995        assert_eq!(dbc.value_descriptions_for_signal(256, "EngineRPM"), None);
996        assert_eq!(dbc.value_descriptions_for_signal(512, "SpeedDisplay"), None);
997    }
998
999    // ============================================================================
1000    // Specification Compliance Tests
1001    // These tests verify against exact requirements from dbc/SPECIFICATIONS.md
1002    // ============================================================================
1003
1004    /// Verify Section 8.3: DLC = 0 is valid
1005    /// "CAN 2.0: 0 to 8 bytes"
1006    /// "CAN FD: 0 to 64 bytes"
1007    #[test]
1008    fn test_spec_section_8_3_dlc_zero_is_valid() {
1009        // DLC = 0 is valid per spec (e.g., for control messages without data payload)
1010        let data = r#"VERSION "1.0"
1011
1012BU_: ECM
1013
1014BO_ 256 ControlMessage : 0 ECM
1015"#;
1016        let dbc = Dbc::parse(data).unwrap();
1017        assert_eq!(dbc.messages().len(), 1);
1018        let msg = dbc.messages().iter().next().unwrap();
1019        assert_eq!(msg.dlc(), 0);
1020    }
1021
1022    /// Verify Section 8.1: Extended CAN ID format
1023    /// "Extended ID in DBC = 0x80000000 | actual_extended_id"
1024    /// "Example: 0x80001234 represents extended ID 0x1234"
1025    #[test]
1026    fn test_spec_section_8_1_extended_can_id_format() {
1027        // Extended ID 0x494 is stored as 0x80000000 | 0x494 = 0x80000494 = 2147484820
1028        // 0x80000000 = 2147483648, 0x494 = 1172, 2147483648 + 1172 = 2147484820
1029        let data = r#"VERSION "1.0"
1030
1031BU_: ECM
1032
1033BO_ 2147484820 ExtendedMessage : 8 ECM
1034"#;
1035        let dbc = Dbc::parse(data).unwrap();
1036        assert_eq!(dbc.messages().len(), 1);
1037        let msg = dbc.messages().iter().next().unwrap();
1038        // id() returns the raw CAN ID without the extended flag
1039        assert_eq!(msg.id(), 0x494); // Raw extended ID
1040        assert!(msg.is_extended()); // is_extended() tells if it's a 29-bit ID
1041    }
1042
1043    /// Verify Section 8.3: Maximum extended ID (0x1FFFFFFF) with bit 31 flag
1044    #[test]
1045    fn test_spec_section_8_1_max_extended_id() {
1046        // Maximum extended ID: 0x80000000 | 0x1FFFFFFF = 0x9FFFFFFF = 2684354559
1047        let data = r#"VERSION "1.0"
1048
1049BU_: ECM
1050
1051BO_ 2684354559 MaxExtendedId : 8 ECM
1052"#;
1053        let dbc = Dbc::parse(data).unwrap();
1054        assert_eq!(dbc.messages().len(), 1);
1055        let msg = dbc.messages().iter().next().unwrap();
1056        // id() returns the raw 29-bit CAN ID without the extended flag
1057        assert_eq!(msg.id(), 0x1FFFFFFF);
1058        assert!(msg.is_extended());
1059    }
1060
1061    /// Verify Section 8.4: Vector__XXX as transmitter
1062    /// "Vector__XXX - No sender / unknown sender"
1063    #[test]
1064    fn test_spec_section_8_4_vector_xxx_transmitter() {
1065        let data = r#"VERSION "1.0"
1066
1067BU_: Gateway
1068
1069BO_ 256 UnknownSender : 8 Vector__XXX
1070 SG_ Signal1 : 0|8@1+ (1,0) [0|255] "" Gateway
1071"#;
1072        let dbc = Dbc::parse(data).unwrap();
1073        assert_eq!(dbc.messages().len(), 1);
1074        let msg = dbc.messages().iter().next().unwrap();
1075        assert_eq!(msg.sender(), "Vector__XXX");
1076    }
1077
1078    /// Verify Section 9.5: Receivers format
1079    /// Parser accepts both comma-separated (per spec) and space-separated (tool extension)
1080    #[test]
1081    fn test_spec_section_9_5_receivers_comma_separated() {
1082        // Comma-separated receivers (per spec)
1083        // Note: The parser identifier function stops at commas, so we test that comma-separated
1084        // receiver parsing works correctly
1085        use crate::{Parser, Signal};
1086
1087        // Test comma-separated receivers directly via Signal::parse
1088        let signal = Signal::parse(
1089            &mut Parser::new(b"SG_ RPM : 0|16@1+ (0.25,0) [0|8000] \"rpm\" Gateway,Dashboard")
1090                .unwrap(),
1091        )
1092        .unwrap();
1093        assert_eq!(signal.receivers().len(), 2);
1094        let mut receivers = signal.receivers().iter();
1095        assert_eq!(receivers.next(), Some("Gateway"));
1096        assert_eq!(receivers.next(), Some("Dashboard"));
1097    }
1098
1099    /// Verify Section 9.4: Multiplexer indicator patterns
1100    /// "M" for multiplexer switch, "m0", "m1", etc. for multiplexed signals
1101    #[test]
1102    fn test_spec_section_9_4_multiplexer_indicators() {
1103        let data = r#"VERSION "1.0"
1104
1105BU_: ECM Gateway
1106
1107BO_ 400 MultiplexedMsg : 8 ECM
1108 SG_ MuxSwitch M : 0|8@1+ (1,0) [0|255] "" Gateway
1109 SG_ Signal_0 m0 : 8|16@1+ (0.1,0) [0|1000] "kPa" Gateway
1110 SG_ Signal_1 m1 : 8|16@1+ (0.01,0) [0|100] "degC" Gateway
1111"#;
1112        let dbc = Dbc::parse(data).unwrap();
1113        let msg = dbc.messages().iter().next().unwrap();
1114
1115        // Find signals by name
1116        let mux_switch = msg.signals().find("MuxSwitch").unwrap();
1117        let signal_0 = msg.signals().find("Signal_0").unwrap();
1118        let signal_1 = msg.signals().find("Signal_1").unwrap();
1119
1120        // Verify multiplexer switch
1121        assert!(mux_switch.is_multiplexer_switch());
1122        assert_eq!(mux_switch.multiplexer_switch_value(), None);
1123
1124        // Verify multiplexed signals
1125        assert!(!signal_0.is_multiplexer_switch());
1126        assert_eq!(signal_0.multiplexer_switch_value(), Some(0));
1127
1128        assert!(!signal_1.is_multiplexer_switch());
1129        assert_eq!(signal_1.multiplexer_switch_value(), Some(1));
1130    }
1131
1132    #[test]
1133    fn test_error_includes_line_number() {
1134        // Test that parsing errors include line numbers
1135        let data = r#"VERSION "1.0"
1136
1137BU_: ECM
1138
1139BO_ invalid EngineData : 8 ECM
1140"#;
1141
1142        let result = Dbc::parse(data);
1143        assert!(result.is_err());
1144        let err = result.unwrap_err();
1145        // The error should have line information
1146        assert!(err.line().is_some(), "Error should include line number");
1147    }
1148
1149    // ============================================================================
1150    // CM_ Comment Parsing Tests (Section 14 of SPECIFICATIONS.md)
1151    // ============================================================================
1152
1153    /// Test parsing general database comment: CM_ "string";
1154    #[test]
1155    fn test_parse_cm_database_comment() {
1156        let data = r#"VERSION "1.0"
1157
1158BU_: ECM
1159
1160BO_ 256 Engine : 8 ECM
1161
1162CM_ "This is the database comment";
1163"#;
1164        let dbc = Dbc::parse(data).unwrap();
1165        assert_eq!(dbc.comment(), Some("This is the database comment"));
1166    }
1167
1168    /// Test parsing node comment: CM_ BU_ node_name "string";
1169    #[test]
1170    fn test_parse_cm_node_comment() {
1171        let data = r#"VERSION "1.0"
1172
1173BU_: ECM
1174
1175BO_ 256 Engine : 8 ECM
1176
1177CM_ BU_ ECM "Engine Control Module";
1178"#;
1179        let dbc = Dbc::parse(data).unwrap();
1180        assert_eq!(dbc.node_comment("ECM"), Some("Engine Control Module"));
1181    }
1182
1183    /// Test parsing message comment: CM_ BO_ message_id "string";
1184    #[test]
1185    fn test_parse_cm_message_comment() {
1186        let data = r#"VERSION "1.0"
1187
1188BU_: ECM
1189
1190BO_ 256 Engine : 8 ECM
1191
1192CM_ BO_ 256 "Engine status message";
1193"#;
1194        let dbc = Dbc::parse(data).unwrap();
1195        let msg = dbc.messages().iter().next().unwrap();
1196        assert_eq!(msg.comment(), Some("Engine status message"));
1197    }
1198
1199    /// Test parsing signal comment: CM_ SG_ message_id signal_name "string";
1200    #[test]
1201    fn test_parse_cm_signal_comment() {
1202        let data = r#"VERSION "1.0"
1203
1204BU_: ECM
1205
1206BO_ 256 Engine : 8 ECM
1207 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1208
1209CM_ SG_ 256 RPM "Engine rotations per minute";
1210"#;
1211        let dbc = Dbc::parse(data).unwrap();
1212        let msg = dbc.messages().iter().next().unwrap();
1213        let signal = msg.signals().find("RPM").unwrap();
1214        assert_eq!(signal.comment(), Some("Engine rotations per minute"));
1215    }
1216
1217    /// Test multiple comments in one file
1218    #[test]
1219    fn test_parse_cm_multiple_comments() {
1220        let data = r#"VERSION "1.0"
1221
1222BU_: ECM TCM
1223
1224BO_ 256 Engine : 8 ECM
1225 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1226
1227BO_ 512 Trans : 8 TCM
1228 SG_ Gear : 0|8@1+ (1,0) [0|6] ""
1229
1230CM_ "Vehicle CAN database";
1231CM_ BU_ ECM "Engine Control Module";
1232CM_ BU_ TCM "Transmission Control Module";
1233CM_ BO_ 256 "Engine status message";
1234CM_ BO_ 512 "Transmission status";
1235CM_ SG_ 256 RPM "Engine rotations per minute";
1236CM_ SG_ 512 Gear "Current gear position";
1237"#;
1238        let dbc = Dbc::parse(data).unwrap();
1239
1240        // Database comment
1241        assert_eq!(dbc.comment(), Some("Vehicle CAN database"));
1242
1243        // Node comments
1244        assert_eq!(dbc.node_comment("ECM"), Some("Engine Control Module"));
1245        assert_eq!(dbc.node_comment("TCM"), Some("Transmission Control Module"));
1246
1247        // Message comments
1248        let engine = dbc.messages().iter().find(|m| m.id() == 256).unwrap();
1249        let trans = dbc.messages().iter().find(|m| m.id() == 512).unwrap();
1250        assert_eq!(engine.comment(), Some("Engine status message"));
1251        assert_eq!(trans.comment(), Some("Transmission status"));
1252
1253        // Signal comments
1254        let rpm = engine.signals().find("RPM").unwrap();
1255        let gear = trans.signals().find("Gear").unwrap();
1256        assert_eq!(rpm.comment(), Some("Engine rotations per minute"));
1257        assert_eq!(gear.comment(), Some("Current gear position"));
1258    }
1259
1260    /// Test CM_ appearing before the entities they describe
1261    #[test]
1262    fn test_parse_cm_before_entity() {
1263        let data = r#"VERSION "1.0"
1264
1265BU_: ECM
1266
1267CM_ BO_ 256 "Engine status message";
1268CM_ SG_ 256 RPM "Engine RPM";
1269
1270BO_ 256 Engine : 8 ECM
1271 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1272"#;
1273        let dbc = Dbc::parse(data).unwrap();
1274        let msg = dbc.messages().iter().next().unwrap();
1275        assert_eq!(msg.comment(), Some("Engine status message"));
1276        let signal = msg.signals().find("RPM").unwrap();
1277        assert_eq!(signal.comment(), Some("Engine RPM"));
1278    }
1279
1280    /// Test multiple CM_ entries for same entity - last wins
1281    #[test]
1282    fn test_parse_cm_last_wins() {
1283        let data = r#"VERSION "1.0"
1284
1285BU_: ECM
1286
1287BO_ 256 Engine : 8 ECM
1288 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1289
1290CM_ BO_ 256 "First message comment";
1291CM_ BO_ 256 "Second message comment";
1292CM_ SG_ 256 RPM "First signal comment";
1293CM_ SG_ 256 RPM "Second signal comment";
1294CM_ BU_ ECM "First node comment";
1295CM_ BU_ ECM "Second node comment";
1296"#;
1297        let dbc = Dbc::parse(data).unwrap();
1298
1299        // Last comment wins for each entity
1300        let msg = dbc.messages().iter().next().unwrap();
1301        assert_eq!(msg.comment(), Some("Second message comment"));
1302        let signal = msg.signals().find("RPM").unwrap();
1303        assert_eq!(signal.comment(), Some("Second signal comment"));
1304        assert_eq!(dbc.node_comment("ECM"), Some("Second node comment"));
1305    }
1306
1307    /// Test comment round-trip (parse -> serialize -> parse)
1308    #[test]
1309    #[cfg(feature = "std")]
1310    fn test_parse_cm_round_trip() {
1311        let data = r#"VERSION "1.0"
1312
1313BU_: ECM TCM
1314
1315BO_ 256 Engine : 8 ECM
1316 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1317
1318CM_ "Database comment";
1319CM_ BU_ ECM "Engine Control Module";
1320CM_ BO_ 256 "Engine status message";
1321CM_ SG_ 256 RPM "Engine rotations per minute";
1322"#;
1323        let dbc = Dbc::parse(data).unwrap();
1324
1325        // Serialize and re-parse
1326        let serialized = dbc.to_dbc_string();
1327        let dbc2 = Dbc::parse(&serialized).unwrap();
1328
1329        // Verify comments are preserved
1330        assert_eq!(dbc2.comment(), Some("Database comment"));
1331        assert_eq!(dbc2.node_comment("ECM"), Some("Engine Control Module"));
1332        let msg = dbc2.messages().iter().next().unwrap();
1333        assert_eq!(msg.comment(), Some("Engine status message"));
1334        let signal = msg.signals().find("RPM").unwrap();
1335        assert_eq!(signal.comment(), Some("Engine rotations per minute"));
1336    }
1337
1338    /// Test CM_ serialization in output
1339    #[test]
1340    #[cfg(feature = "std")]
1341    fn test_serialize_cm_comments() {
1342        let data = r#"VERSION "1.0"
1343
1344BU_: ECM
1345
1346BO_ 256 Engine : 8 ECM
1347 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1348
1349CM_ "Database comment";
1350CM_ BU_ ECM "Engine Control Module";
1351CM_ BO_ 256 "Engine status";
1352CM_ SG_ 256 RPM "RPM signal";
1353"#;
1354        let dbc = Dbc::parse(data).unwrap();
1355        let serialized = dbc.to_dbc_string();
1356
1357        // Verify CM_ lines are present in output
1358        assert!(serialized.contains("CM_ \"Database comment\";"));
1359        assert!(serialized.contains("CM_ BU_ ECM \"Engine Control Module\";"));
1360        assert!(serialized.contains("CM_ BO_ 256 \"Engine status\";"));
1361        assert!(serialized.contains("CM_ SG_ 256 RPM \"RPM signal\";"));
1362    }
1363}