Skip to main content

dbc_codegen/
lib.rs

1#![deny(missing_docs)]
2#![deny(clippy::arithmetic_side_effects)]
3#![allow(clippy::needless_doctest_main)]
4#![cfg_attr(feature = "std", doc = include_str!("../README.md"))]
5#![cfg_attr(
6    not(feature = "std"),
7    doc = "Documentation is only available with the `std` feature."
8)]
9
10mod feature_config;
11mod keywords;
12mod pad;
13mod signal_type;
14mod utils;
15
16use std::collections::{BTreeMap, BTreeSet, HashMap};
17use std::fs::OpenOptions;
18use std::io::{BufWriter, Write};
19use std::path::Path;
20
21use anyhow::{Context, Error, Result, anyhow, ensure};
22use can_dbc::ByteOrder::{BigEndian, LittleEndian};
23use can_dbc::MultiplexIndicator::{
24    MultiplexedSignal, Multiplexor, MultiplexorAndMultiplexedSignal, Plain,
25};
26use can_dbc::ValueType::Signed;
27use can_dbc::{
28    AttributeValue, AttributeValueForRelationType, Dbc, Message, MessageId, Signal, ValDescription,
29    ValueDescription,
30};
31use heck::ToSnakeCase;
32use quote::ToTokens;
33use typed_builder::TypedBuilder;
34
35pub use crate::feature_config::FeatureConfig;
36use crate::pad::PadAdapter;
37use crate::signal_type::{IntSize, ValType};
38use crate::utils::{
39    MessageExt as _, SignalExt as _, enum_name, enum_variant_name, is_screaming_snake_case,
40    is_valid_ident, is_valid_type_path, multiplex_enum_name, multiplexed_enum_variant_name,
41    multiplexed_enum_variant_wrapper_name, node_field_name,
42};
43
44static ALLOW_DEADCODE: &str = "#[allow(dead_code)]";
45static ALLOW_LINTS: &str = r"#[allow(
46    clippy::absurd_extreme_comparisons,
47    clippy::excessive_precision,
48    clippy::manual_range_contains,
49    clippy::unnecessary_cast,
50    clippy::useless_conversion,
51    unused_comparisons,
52    unused_variables,
53)]";
54
55/// Code generator configuration. See module-level docs for an example.
56#[derive(TypedBuilder)]
57#[non_exhaustive]
58pub struct Config<'a> {
59    /// Name of the dbc-file. Used for generated docs only.
60    pub dbc_name: &'a str,
61
62    /// Content of a dbc-file as a UTF-8 string. Use [`can_dbc::decode_cp1252`] or other encodings if needed.
63    pub dbc_content: &'a str,
64
65    /// Optional: Print debug info to stdout while generating code. Default: `false`.
66    #[builder(default)]
67    pub debug_prints: bool,
68
69    /// Optional: `impl Debug` for generated types. Default: `Never`.
70    #[builder(default)]
71    pub impl_debug: FeatureConfig<'a>,
72
73    /// Optional: `impl defmt::Format` for generated types. Default: `Never`.
74    #[builder(default)]
75    pub impl_defmt: FeatureConfig<'a>,
76
77    /// Optional: `impl Arbitrary` for generated types. Default: `Never`.
78    #[builder(default)]
79    pub impl_arbitrary: FeatureConfig<'a>,
80
81    /// Optional: `impl Serialize` and `impl Deserialize` for generated types.. Default: `Never`.
82    #[builder(default)]
83    pub impl_serde: FeatureConfig<'a>,
84
85    /// Optional: `impl core::error::Error` for the generated `CanError` type. Default: `Never`.
86    #[builder(default)]
87    pub impl_error: FeatureConfig<'a>,
88
89    /// Optional: Generate `embedded_can::Frame` impl for each frame. Default: `Always`
90    #[builder(default = FeatureConfig::Always)]
91    pub impl_embedded_can_frame: FeatureConfig<'a>,
92
93    /// Optional: Validate min and max values in generated signal setters. Default: `Always`
94    #[builder(default = FeatureConfig::Always)]
95    pub check_ranges: FeatureConfig<'a>,
96
97    /// Optional: Allow dead code in the generated module. Default: `false`.
98    #[builder(default)]
99    pub allow_dead_code: bool,
100
101    /// Optional: Padding bit value used; set all unused bits to 1 if true else to 0. Default: `false`.
102    #[builder(default)]
103    pub padding_bit_value: bool,
104
105    /// Optional: User-defined structs populated from DBC attributes.
106    /// These are emitted as associated constants in the generated message types.
107    /// Default: empty.
108    #[builder(default)]
109    pub attribute_structs: &'a [AttributeStruct<'a>],
110}
111
112/// A user-defined struct that [`Config`] fills from DBC attributes and emits as an
113/// associated constant in the generated message type.
114#[derive(Debug, Clone)]
115pub struct AttributeStruct<'a> {
116    /// Fully-qualified path of the target Rust type.
117    pub type_path: &'a str,
118
119    /// Base name of the emitted associated constant.
120    pub const_name: &'a str,
121
122    /// Whether one constant is emitted per message or per matching signal.
123    pub scope: AttributeScope,
124
125    /// Only emit the constant when this attribute has an assigned value on the
126    /// scoped object (the message for [`AttributeScope::Message`] or the signal
127    /// for [`AttributeScope::Signal`]).
128    pub require: &'a str,
129
130    /// The struct fields, written in this order.
131    pub fields: &'a [AttributeField<'a>],
132
133    /// Restrict [`AttributeScope::NodeSignal`]/[`AttributeScope::NodeMessage`]
134    /// emission to this one node and remove the node name from the generated
135    /// const. Set to `None` to keep the default behavior (one constant per
136    /// matching node).
137    pub for_node: Option<&'a str>,
138}
139
140/// Whether an [`AttributeStruct`] is emitted once per message or once per signal.
141#[derive(Debug, Clone, Copy)]
142#[non_exhaustive]
143pub enum AttributeScope {
144    /// One const per message (`BO_` attribute).
145    Message,
146    /// One const per signal (`SG_` attribute).
147    Signal,
148    /// One const per node related to a message (`BU_BO_REL_` relation attribute).
149    NodeMessage,
150    /// One const per receiving node of a signal (`BU_SG_REL_` relation attribute).
151    NodeSignal,
152}
153
154/// One field of an [`AttributeStruct`] and where its value comes from.
155#[derive(Debug, Clone)]
156pub struct AttributeField<'a> {
157    /// The target struct field name.
158    pub name: &'a str,
159    /// Where the field's value comes from.
160    pub source: FieldSource<'a>,
161}
162
163/// Source of a generated [`AttributeField`] value.
164#[derive(Debug, Clone)]
165#[non_exhaustive]
166pub enum FieldSource<'a> {
167    /// Value of an attribute on the scoped object: a signal-level attribute in
168    /// [`AttributeScope::Signal`] or a message-level attribute in
169    /// [`AttributeScope::Message`].
170    Attr(&'a str),
171    /// Value of a message-level attribute.
172    MessageAttr(&'a str),
173    /// The signal's raw DBC start bit. [`AttributeScope::Signal`] only.
174    StartBit,
175    /// The signal's start byte. [`AttributeScope::Signal`] only.
176    StartByte,
177    /// The signal's width in bits. [`AttributeScope::Signal`] only.
178    BitWidth,
179    /// The message size in bytes.
180    MessageSize,
181    /// A literal integer.
182    Int(i64),
183    /// A literal string.
184    Str(&'a str),
185    /// The name of the related node.
186    /// [`AttributeScope::NodeSignal`] and [`AttributeScope::NodeMessage`] only.
187    NodeName,
188}
189
190/// Message/signal/node context used to resolve an [`AttributeStruct`]'s fields.
191struct AttrTarget<'a> {
192    msg: &'a Message,
193    dbc: &'a Dbc,
194    signal: Option<&'a Signal>,
195    node: Option<&'a str>,
196}
197
198impl Config<'_> {
199    /// Write Rust structs matching DBC input description to `out` buffer
200    fn codegen(&self, out: impl Write) -> Result<()> {
201        self.validate_attribute_structs()?;
202        let dbc = Dbc::try_from(self.dbc_content).map_err(|e| {
203            let msg = "Could not parse dbc file";
204            if self.debug_prints {
205                anyhow!("{msg}: {e:#?}")
206            } else {
207                anyhow!("{msg}")
208            }
209        })?;
210        if self.debug_prints {
211            eprintln!("{dbc:#?}");
212        }
213        let mut w = BufWriter::new(out);
214
215        writeln!(
216            w,
217            "/// The name of the DBC file this code was generated from"
218        )?;
219        writeln!(w, "#[allow(dead_code)]")?;
220        let dbc_name = self.dbc_name.to_token_stream();
221        writeln!(w, "pub const DBC_FILE_NAME: &str = {dbc_name};")?;
222        writeln!(
223            w,
224            "/// The version of the DBC file this code was generated from"
225        )?;
226        writeln!(w, "#[allow(dead_code)]")?;
227        let dbc_version = dbc.version.0.to_token_stream();
228        writeln!(w, "pub const DBC_FILE_VERSION: &str = {dbc_version};")?;
229
230        writeln!(w, "#[allow(unused_imports)]")?;
231        writeln!(w, "use core::ops::BitOr;")?;
232        writeln!(w, "#[allow(unused_imports)]")?;
233        writeln!(w, "use bitvec::prelude::*;")?;
234        writeln!(w, "#[allow(unused_imports)]")?;
235        writeln!(w, "use embedded_can::{{Id, StandardId, ExtendedId}};")?;
236
237        self.impl_arbitrary.fmt_cfg(&mut w, |w| {
238            writeln!(w, "use arbitrary::{{Arbitrary, Unstructured}};")
239        })?;
240
241        self.impl_serde.fmt_cfg(&mut w, |w| {
242            writeln!(w, "use serde::{{Serialize, Deserialize}};")
243        })?;
244
245        writeln!(w)?;
246        self.render_dbc(&mut w, &dbc)?;
247        writeln!(w)?;
248        self.render_error(&mut w)?;
249        self.render_arbitrary_helpers(&mut w)?;
250        writeln!(w)?;
251
252        Ok(())
253    }
254
255    fn render_dbc(&self, w: &mut impl Write, dbc: &Dbc) -> Result<()> {
256        self.render_root_enum(w, dbc)?;
257
258        for msg in get_relevant_messages(dbc) {
259            self.render_message(w, msg, dbc)
260                .with_context(|| format!("write message `{}`", msg.name))?;
261            writeln!(w)?;
262        }
263
264        Ok(())
265    }
266
267    fn render_root_enum(&self, w: &mut impl Write, dbc: &Dbc) -> Result<()> {
268        writeln!(w, "/// All messages")?;
269        writeln!(w, "{ALLOW_LINTS}")?;
270        self.write_allow_dead_code(w)?;
271        writeln!(w, "#[derive(Clone)]")?;
272        self.impl_debug.fmt_attr(w, "derive(Debug)")?;
273        self.impl_defmt.fmt_attr(w, "derive(defmt::Format)")?;
274        self.impl_serde.fmt_attr(w, "derive(Serialize)")?;
275        self.impl_serde.fmt_attr(w, "derive(Deserialize)")?;
276        writeln!(w, "pub enum Messages {{")?;
277        {
278            let mut w = PadAdapter::wrap(w);
279            for msg in get_relevant_messages(dbc) {
280                writeln!(w, "/// {}", msg.name)?;
281                writeln!(w, "{0}({0}),", msg.type_name())?;
282            }
283        }
284        writeln!(w, "}}")?;
285        writeln!(w)?;
286
287        writeln!(w, "{ALLOW_LINTS}")?;
288        self.write_allow_dead_code(w)?;
289        writeln!(w, "impl Messages {{")?;
290        {
291            let mut w = PadAdapter::wrap(w);
292            writeln!(w, "/// Read message from CAN frame")?;
293            writeln!(w, "#[inline(never)]")?;
294            writeln!(
295                w,
296                "pub fn from_can_message(id: Id, payload: &[u8]) -> Result<Self, CanError> {{",
297            )?;
298
299            {
300                let mut w = PadAdapter::wrap(&mut w);
301                let messages: Vec<_> = get_relevant_messages(dbc).collect();
302                if messages.is_empty() {
303                    writeln!(w, "Err(CanError::UnknownMessageId(id))")?;
304                } else {
305                    writeln!(w)?;
306                    writeln!(w, "let res = match id {{")?;
307                    {
308                        let mut w = PadAdapter::wrap(&mut w);
309                        for msg in messages {
310                            writeln!(
311                                w,
312                                "{0}::MESSAGE_ID => Messages::{0}({0}::try_from(payload)?),",
313                                msg.type_name(),
314                            )?;
315                        }
316                        writeln!(w, r"id => return Err(CanError::UnknownMessageId(id)),")?;
317                    }
318                    writeln!(w, "}};")?;
319                    writeln!(w, "Ok(res)")?;
320                }
321            }
322
323            writeln!(w, "}}")?;
324        }
325        writeln!(w, "}}")?;
326        writeln!(w)?;
327
328        Ok(())
329    }
330
331    fn render_message(&self, w: &mut impl Write, msg: &Message, dbc: &Dbc) -> Result<()> {
332        writeln!(w, "/// {}", msg.name)?;
333        writeln!(w, "///")?;
334        match msg.id {
335            MessageId::Standard(id) => writeln!(w, "/// - Standard ID: {id} (0x{id:x})"),
336            MessageId::Extended(id) => writeln!(w, "/// - Extended ID: {id} (0x{id:x})"),
337        }?;
338        writeln!(w, "/// - Size: {} bytes", msg.size)?;
339        if let Some(transmitter) = &msg.transmitter {
340            writeln!(w, "/// - Transmitter: {transmitter}")?;
341        }
342        if let Some(comment) = dbc.message_comment(msg.id) {
343            writeln!(w, "///")?;
344            for line in comment.trim().lines() {
345                writeln!(w, "/// {line}")?;
346            }
347        }
348        writeln!(w, "#[derive(Clone, Copy)]")?;
349        self.impl_serde.fmt_attr(w, "derive(Serialize)")?;
350        self.impl_serde.fmt_attr(w, "derive(Deserialize)")?;
351        writeln!(w, "pub struct {} {{", msg.type_name())?;
352        {
353            let mut w = PadAdapter::wrap(w);
354            self.impl_serde
355                .fmt_attr(&mut w, "serde(with = \"serde_bytes\")")?;
356            writeln!(w, "raw: [u8; {}],", msg.size)?;
357        }
358        writeln!(w, "}}")?;
359        writeln!(w)?;
360
361        writeln!(w, "{ALLOW_LINTS}")?;
362        self.write_allow_dead_code(w)?;
363        writeln!(w, "impl {} {{", msg.type_name())?;
364        {
365            let mut w = PadAdapter::wrap(w);
366
367            writeln!(
368                w,
369                "pub const MESSAGE_ID: embedded_can::Id = {};",
370                match msg.id {
371                    MessageId::Standard(id) =>
372                        format!("Id::Standard(StandardId::new({id:#x}).unwrap())"),
373                    MessageId::Extended(id) =>
374                        format!("Id::Extended(ExtendedId::new({id:#x}).unwrap())"),
375                }
376            )?;
377            writeln!(w)?;
378
379            writeln!(w, "pub const MESSAGE_SIZE: usize = {};", msg.size)?;
380            writeln!(w)?;
381
382            Self::render_cycle_time(&mut w, msg, dbc)?;
383
384            for signal in &msg.signals {
385                let typ = ValType::from_signal(signal);
386                if typ != ValType::Bool {
387                    let sig = signal.field_name().to_uppercase();
388                    let min = signal.min;
389                    let max = signal.max;
390                    writeln!(w, "pub const {sig}_MIN: {typ} = {min}_{typ};")?;
391                    writeln!(w, "pub const {sig}_MAX: {typ} = {max}_{typ};")?;
392                }
393            }
394            writeln!(w)?;
395
396            self.render_attribute_structs(&mut w, msg, dbc)?;
397
398            writeln!(
399                w,
400                "/// Constructs a new `{}` message from values.",
401                msg.name
402            )?;
403            let args = msg
404                .signals
405                .iter()
406                .filter_map(|signal| {
407                    if matches!(signal.multiplexer_indicator, Plain | Multiplexor) {
408                        let field = signal.field_name();
409                        let typ = signal_pub_type(dbc, msg, signal);
410                        Some(format!("{field}: {typ}"))
411                    } else {
412                        None
413                    }
414                })
415                .collect::<Vec<_>>()
416                .join(", ");
417            writeln!(w, "pub fn new({args}) -> Result<Self, CanError> {{")?;
418            {
419                let mut w = PadAdapter::wrap(&mut w);
420                let mutable = if msg.signals.is_empty() { "" } else { "mut " };
421                let padding_value = if self.padding_bit_value {
422                    "0xFF"
423                } else {
424                    "0x00"
425                };
426                let size = msg.size;
427                writeln!(
428                    w,
429                    "let {mutable}res = Self {{ raw: [{padding_value}; {size}] }};"
430                )?;
431                for signal in &msg.signals {
432                    if matches!(signal.multiplexer_indicator, Plain | Multiplexor) {
433                        writeln!(w, "res.set_{0}({0})?;", signal.field_name())?;
434                    }
435                }
436                writeln!(w, "Ok(res)")?;
437            }
438            writeln!(w, "}}")?;
439            writeln!(w)?;
440
441            writeln!(w, "/// Returns the raw `{}` message payload.", msg.name)?;
442            writeln!(w, "pub fn raw(&self) -> &[u8; {}] {{", msg.size)?;
443            {
444                let mut w = PadAdapter::wrap(&mut w);
445                writeln!(w, "&self.raw")?;
446            }
447            writeln!(w, "}}")?;
448            writeln!(w)?;
449
450            for signal in &msg.signals {
451                match signal.multiplexer_indicator {
452                    Plain => self
453                        .render_signal(&mut w, signal, dbc, msg)
454                        .with_context(|| format!("write signal impl `{}`", signal.name))?,
455                    Multiplexor => {
456                        self.render_multiplexor_signal(&mut w, signal, dbc, msg)?;
457                    }
458                    MultiplexedSignal(_) | MultiplexorAndMultiplexedSignal(_) => {}
459                }
460            }
461        }
462
463        writeln!(w, "}}")?;
464        writeln!(w)?;
465
466        let typ = msg.type_name();
467        writeln!(w, "impl core::convert::TryFrom<&[u8]> for {typ} {{")?;
468        {
469            let mut w = PadAdapter::wrap(w);
470            writeln!(w, "type Error = CanError;")?;
471            writeln!(w)?;
472            writeln!(w, "#[inline(always)]")?;
473            writeln!(
474                w,
475                "fn try_from(payload: &[u8]) -> Result<Self, Self::Error> {{",
476            )?;
477            {
478                let mut w = PadAdapter::wrap(&mut w);
479                writeln!(
480                    w,
481                    r"if payload.len() != {} {{ return Err(CanError::InvalidPayloadSize); }}",
482                    msg.size,
483                )?;
484                writeln!(w, "let mut raw = [0u8; {}];", msg.size)?;
485                writeln!(w, "raw.copy_from_slice(&payload[..{}]);", msg.size)?;
486                writeln!(w, "Ok(Self {{ raw }})")?;
487            }
488            writeln!(w, "}}")?;
489        }
490        writeln!(w, "}}")?;
491        writeln!(w)?;
492
493        self.render_embedded_can_frame(w, msg)?;
494
495        self.impl_debug
496            .fmt_cfg(&mut *w, |w| render_debug_impl(w, msg))?;
497        self.impl_defmt
498            .fmt_cfg(&mut *w, |w| render_defmt_impl(w, msg))?;
499        self.impl_arbitrary
500            .fmt_cfg(&mut *w, |w| self.render_arbitrary(w, dbc, msg))?;
501
502        let enums_for_this_message = dbc.value_descriptions.iter().filter_map(|x| {
503            if let ValueDescription::Signal {
504                message_id,
505                name,
506                value_descriptions,
507            } = x
508            {
509                if *message_id != msg.id {
510                    return None;
511                }
512                dbc.signal_by_name(*message_id, name)
513                    .map(|v| (v, value_descriptions))
514            } else {
515                None
516            }
517        });
518        for (signal, variants) in enums_for_this_message {
519            self.write_enum(w, signal, msg, variants.as_slice())?;
520        }
521
522        let multiplexor_signal = msg
523            .signals
524            .iter()
525            .find(|s| s.multiplexer_indicator == Multiplexor);
526
527        if let Some(multiplexor_signal) = multiplexor_signal {
528            self.render_multiplexor_enums(w, dbc, msg, multiplexor_signal)?;
529        }
530
531        Ok(())
532    }
533
534    /// Validate the [`AttributeStruct`] specs.
535    fn validate_attribute_structs(&self) -> Result<()> {
536        for spec in self.attribute_structs {
537            // Require SCREAMING_SNAKE_CASE to guarantee that the name never collides
538            // with a generated method (all `snake_case`).
539            ensure!(
540                is_screaming_snake_case(spec.const_name),
541                "attribute_structs: 'const_name' {:?} must be SCREAMING_SNAKE_CASE",
542                spec.const_name
543            );
544            ensure!(
545                is_valid_type_path(spec.type_path),
546                "attribute_structs: 'type_path' {:?} for {:?} is not a valid Rust type path",
547                spec.type_path,
548                spec.const_name
549            );
550            ensure!(
551                !spec.require.is_empty(),
552                "attribute_structs: 'require' must not be empty for {:?}",
553                spec.const_name
554            );
555            ensure!(
556                !spec.fields.is_empty(),
557                "attribute_structs: {:?} declares no fields",
558                spec.const_name
559            );
560
561            let has_signal = matches!(
562                spec.scope,
563                AttributeScope::Signal | AttributeScope::NodeSignal
564            );
565            let has_node = matches!(
566                spec.scope,
567                AttributeScope::NodeSignal | AttributeScope::NodeMessage
568            );
569            if let Some(for_node) = spec.for_node {
570                ensure!(
571                    !for_node.is_empty(),
572                    "attribute_structs: 'for_node' must not be empty for {:?}",
573                    spec.const_name
574                );
575                ensure!(
576                    has_node,
577                    "attribute_structs: {:?} sets 'for_node' but its scope has no associated \
578                     node (scope must be NodeSignal or NodeMessage)",
579                    spec.const_name
580                );
581            }
582            let mut seen = BTreeSet::new();
583            for field in spec.fields {
584                ensure!(
585                    is_valid_ident(field.name),
586                    "attribute_structs: field name {:?} in {:?} is not a valid Rust identifier",
587                    field.name,
588                    spec.const_name
589                );
590                ensure!(
591                    seen.insert(field.name),
592                    "attribute_structs: duplicate field {:?} in {:?}",
593                    field.name,
594                    spec.const_name
595                );
596                if !has_signal {
597                    ensure!(
598                        !matches!(
599                            field.source,
600                            FieldSource::StartBit | FieldSource::StartByte | FieldSource::BitWidth
601                        ),
602                        "attribute_structs: field {:?} of {:?} uses a signal-only source \
603                         (StartBit/StartByte/BitWidth) but the struct has no associated signal",
604                        field.name,
605                        spec.const_name
606                    );
607                }
608                if !has_node {
609                    ensure!(
610                        !matches!(field.source, FieldSource::NodeName),
611                        "attribute_structs: field {:?} of {:?} uses NodeName but the struct has \
612                         no associated node (scope must be NodeSignal or NodeMessage)",
613                        field.name,
614                        spec.const_name
615                    );
616                }
617            }
618        }
619        Ok(())
620    }
621
622    /// Emit the configured [`AttributeStruct`]s as associated constants.
623    fn render_attribute_structs(&self, w: &mut impl Write, msg: &Message, dbc: &Dbc) -> Result<()> {
624        if self.attribute_structs.is_empty() {
625            return Ok(());
626        }
627
628        // Track every associated item already emitted for this message type so
629        // that colliding const names are rejected here instead of producing code
630        // that fails to build. Using a BTree to ensure deterministic output.
631        let mut used: BTreeSet<String> = BTreeSet::new();
632        used.insert("MESSAGE_ID".to_string());
633        for signal in &msg.signals {
634            if ValType::from_signal(signal) != ValType::Bool {
635                let sig = signal.field_name().to_uppercase();
636                used.insert(format!("{sig}_MIN"));
637                used.insert(format!("{sig}_MAX"));
638            }
639        }
640
641        for spec in self.attribute_structs {
642            match spec.scope {
643                AttributeScope::Message => {
644                    if dbc.message_attribute(msg.id, spec.require).is_none() {
645                        continue;
646                    }
647                    Self::render_attribute_struct(
648                        w,
649                        spec,
650                        spec.const_name,
651                        &AttrTarget {
652                            msg,
653                            dbc,
654                            signal: None,
655                            node: None,
656                        },
657                        &mut used,
658                    )?;
659                }
660                AttributeScope::Signal => {
661                    for signal in &msg.signals {
662                        if dbc
663                            .signal_attribute(msg.id, &signal.name, spec.require)
664                            .is_none()
665                        {
666                            continue;
667                        }
668                        let name =
669                            format!("{}_{}", signal.field_name().to_uppercase(), spec.const_name);
670                        Self::render_attribute_struct(
671                            w,
672                            spec,
673                            &name,
674                            &AttrTarget {
675                                msg,
676                                dbc,
677                                signal: Some(signal),
678                                node: None,
679                            },
680                            &mut used,
681                        )?;
682                    }
683                }
684                AttributeScope::NodeSignal => {
685                    for signal in &msg.signals {
686                        for node in &signal.receivers {
687                            if spec.for_node.is_some_and(|target| node != target) {
688                                continue;
689                            }
690                            if node_relation_attribute(
691                                dbc,
692                                node,
693                                msg.id,
694                                Some(&signal.name),
695                                spec.require,
696                            )
697                            .is_none()
698                            {
699                                continue;
700                            }
701                            let name = if spec.for_node.is_some() {
702                                format!(
703                                    "{}_{}",
704                                    signal.field_name().to_uppercase(),
705                                    spec.const_name
706                                )
707                            } else {
708                                let node_ident = node_field_name(node).to_uppercase();
709                                format!(
710                                    "{}_{node_ident}_{}",
711                                    signal.field_name().to_uppercase(),
712                                    spec.const_name
713                                )
714                            };
715                            Self::render_attribute_struct(
716                                w,
717                                spec,
718                                &name,
719                                &AttrTarget {
720                                    msg,
721                                    dbc,
722                                    signal: Some(signal),
723                                    node: Some(node),
724                                },
725                                &mut used,
726                            )?;
727                        }
728                    }
729                }
730                AttributeScope::NodeMessage => {
731                    for node in &dbc.nodes {
732                        if spec.for_node.is_some_and(|target| node.0 != target) {
733                            continue;
734                        }
735                        if node_relation_attribute(dbc, &node.0, msg.id, None, spec.require)
736                            .is_none()
737                        {
738                            continue;
739                        }
740                        let name = if spec.for_node.is_some() {
741                            spec.const_name.to_string()
742                        } else {
743                            format!(
744                                "{}_{}",
745                                node_field_name(&node.0).to_uppercase(),
746                                spec.const_name
747                            )
748                        };
749                        Self::render_attribute_struct(
750                            w,
751                            spec,
752                            &name,
753                            &AttrTarget {
754                                msg,
755                                dbc,
756                                signal: None,
757                                node: Some(&node.0),
758                            },
759                            &mut used,
760                        )?;
761                    }
762                }
763            }
764        }
765        Ok(())
766    }
767
768    /// Emit a single struct constant.
769    fn render_attribute_struct(
770        w: &mut impl Write,
771        spec: &AttributeStruct<'_>,
772        const_name: &str,
773        target: &AttrTarget<'_>,
774        used: &mut BTreeSet<String>,
775    ) -> Result<()> {
776        ensure!(
777            used.insert(const_name.to_string()),
778            "attribute_structs: generated const '{const_name}' on message {:?} collides with \
779             another const. Use a distinct 'const_name'.",
780            target.msg.name
781        );
782
783        let mut fields = Vec::with_capacity(spec.fields.len());
784        for field in spec.fields {
785            let lit = resolve_field_source(&field.source, target).ok_or_else(|| {
786                anyhow!(
787                    "attribute_structs: const '{const_name}' field {:?} has no value in the DBC \
788                     and no default (source: {:?})",
789                    field.name,
790                    field.source
791                )
792            })?;
793            fields.push((field.name, lit));
794        }
795
796        let ty = spec.type_path;
797        writeln!(w, "/// Generated from DBC attributes `{}`", spec.require)?;
798        writeln!(w, "pub const {const_name}: {ty} = {ty} {{")?;
799        {
800            let mut w = PadAdapter::wrap(w);
801            for (name, lit) in fields {
802                writeln!(w, "{name}: {lit},")?;
803            }
804        }
805        writeln!(w, "}};")?;
806        writeln!(w)?;
807        Ok(())
808    }
809
810    /// Emit the cycle time as a constant for messages that have an assigned
811    /// `GenMsgCycleTime` (`BA_`) attribute.
812    fn render_cycle_time(w: &mut impl Write, msg: &Message, dbc: &Dbc) -> Result<()> {
813        let Some(ms) = message_cycle_time_ms(dbc, msg.id) else {
814            return Ok(());
815        };
816        writeln!(w, "pub const MESSAGE_CYCLE_TIME_MS: u32 = {ms};")?;
817        Ok(())
818    }
819
820    fn render_signal(
821        &self,
822        w: &mut impl Write,
823        signal: &Signal,
824        dbc: &Dbc,
825        msg: &Message,
826    ) -> Result<()> {
827        writeln!(w, "/// Returns the value of `{}`.", signal.name)?;
828        if let Some(comment) = dbc.signal_comment(msg.id, &signal.name) {
829            writeln!(w, "///")?;
830            for line in comment.trim().lines() {
831                writeln!(w, "/// {line}")?;
832            }
833        }
834        writeln!(w, "///")?;
835        writeln!(w, "/// - Min: {}", signal.min)?;
836        writeln!(w, "/// - Max: {}", signal.max)?;
837        if signal.unit.is_empty() {
838            writeln!(w, "/// - Unit: Not specified")?;
839        } else {
840            writeln!(w, "/// - Unit: {:?}", signal.unit)?;
841        }
842        if signal.receivers.is_empty() {
843            writeln!(w, "/// - Receivers: Vector__XXX")?;
844        } else {
845            writeln!(w, "/// - Receivers: {}", signal.receivers.join(", "))?;
846        }
847
848        let fn_name = signal.field_name();
849        let variants = dbc.value_descriptions_for_signal(msg.id, &signal.name);
850        if variants.is_none() {
851            writeln!(w, "/// - Factor: {}", signal.factor)?;
852            writeln!(w, "/// - Offset: {}", signal.offset)?;
853        }
854        writeln!(w, "#[inline(always)]")?;
855        if let Some(variants) = variants {
856            let type_name = enum_name(msg, signal);
857            let signal_ty = ValType::from_signal(signal);
858            let variant_infos = generate_variant_info(variants, signal_ty);
859
860            writeln!(w, "pub fn {fn_name}(&self) -> {type_name} {{")?;
861            {
862                let mut w = PadAdapter::wrap(w);
863
864                // Use signed type for loading when signal is signed and has negative values
865                let has_negative_values = variants.iter().any(|v| v.id < 0);
866                let load_type = if signal.value_type == Signed && has_negative_values {
867                    signal_ty
868                } else {
869                    ValType::from_signal_uint(signal)
870                };
871
872                let read = read_fn_with_type(signal, msg, load_type)?;
873                writeln!(w, r"let signal = {read};")?;
874                writeln!(w)?;
875                writeln!(w, "match signal {{")?;
876                {
877                    let mut w = PadAdapter::wrap(&mut w);
878                    for info in &variant_infos {
879                        let literal = info.value;
880                        let variant = &info.base_name;
881                        match info.dup_type {
882                            DuplicateType::Unique => {
883                                writeln!(w, "{literal} => {type_name}::{variant},")?;
884                            }
885                            DuplicateType::FirstDuplicate | DuplicateType::Duplicate => {
886                                writeln!(w, "{literal} => {type_name}::{variant}({literal}),")?;
887                            }
888                        }
889                    }
890                    writeln!(w, "_ => {type_name}::_Other(self.{fn_name}_phys_val()),")?;
891                }
892                writeln!(w, "}}")?;
893            }
894            writeln!(w, "}}")?;
895            writeln!(w)?;
896
897            // Private helper for the `_Other()` fallback above.
898            writeln!(w, "#[inline(always)]")?;
899            let typ = ValType::from_signal(signal);
900            writeln!(w, "fn {fn_name}_phys_val(&self) -> {typ} {{")?;
901            {
902                let mut w = PadAdapter::wrap(w);
903                signal_from_payload(&mut w, signal, msg).context("signal from payload")?;
904            }
905            writeln!(w, "}}")?;
906            writeln!(w)?;
907        } else {
908            let typ = ValType::from_signal(signal);
909            writeln!(w, "pub fn {fn_name}(&self) -> {typ} {{")?;
910            {
911                let mut w = PadAdapter::wrap(w);
912                signal_from_payload(&mut w, signal, msg).context("signal from payload")?;
913            }
914            writeln!(w, "}}")?;
915            writeln!(w)?;
916        }
917
918        render_raw_accessors(w, signal, msg)?;
919
920        self.render_set_signal(w, signal, dbc, msg)?;
921
922        Ok(())
923    }
924
925    fn render_set_signal(
926        &self,
927        w: &mut impl Write,
928        signal: &Signal,
929        dbc: &Dbc,
930        msg: &Message,
931    ) -> Result<()> {
932        // To avoid accidentally changing the multiplexor value without changing
933        // the signals accordingly this fn is kept private for multiplexors.
934        let visibility = if signal.multiplexer_indicator == Multiplexor {
935            ""
936        } else {
937            "pub "
938        };
939
940        let field = signal.field_name();
941        let typ = ValType::from_signal(signal);
942        let param_type = signal_pub_type(dbc, msg, signal);
943        let is_enum_backed = param_type != typ.to_string();
944
945        writeln!(w, "/// Sets the value of `{}`.", signal.name)?;
946        writeln!(w, "#[inline(always)]")?;
947        writeln!(
948            w,
949            "{visibility}fn set_{field}(&mut self, value: {param_type}) -> Result<(), CanError> {{",
950        )?;
951        {
952            let mut w = PadAdapter::wrap(w);
953            // Enum-backed signals accept the value-description enum; convert it to
954            // the raw primitive before range checks and packing.
955            if is_enum_backed {
956                writeln!(w, "let value = {typ}::from(value);")?;
957            }
958            self.render_set_signal_body(&mut w, signal, msg)?;
959        }
960        writeln!(w, "}}")?;
961        writeln!(w)?;
962
963        Ok(())
964    }
965
966    fn render_set_signal_body(
967        &self,
968        w: &mut impl Write,
969        signal: &Signal,
970        msg: &Message,
971    ) -> Result<()> {
972        if signal.size != 1 {
973            if let FeatureConfig::Gated(gate) = self.check_ranges {
974                writeln!(w, r"#[cfg(feature = {gate:?})]")?;
975            }
976
977            if let FeatureConfig::Gated(..) | FeatureConfig::Always = self.check_ranges {
978                let typ = ValType::from_signal(signal);
979                let min = signal.min;
980                let max = signal.max;
981                writeln!(w, r"if value < {min}_{typ} || {max}_{typ} < value {{")?;
982
983                {
984                    let mut w = PadAdapter::wrap(&mut *w);
985                    let typ = msg.type_name();
986                    writeln!(
987                        w,
988                        r"return Err(CanError::ParameterOutOfRange {{ message_id: {typ}::MESSAGE_ID }});",
989                    )?;
990                }
991
992                writeln!(w, r"}}")?;
993            }
994        }
995        signal_to_payload(&mut *w, signal, msg).context("signal to payload")?;
996
997        Ok(())
998    }
999
1000    fn render_multiplexor_signal(
1001        &self,
1002        w: &mut impl Write,
1003        signal: &Signal,
1004        dbc: &Dbc,
1005        msg: &Message,
1006    ) -> Result<()> {
1007        let signal_typ = ValType::from_signal(signal);
1008
1009        render_raw_accessors(w, signal, msg)?;
1010
1011        let field = signal.field_name();
1012        let typ = multiplex_enum_name(msg, signal)?;
1013        writeln!(
1014            w,
1015            "/// Selects the active multiplexed sub-message for `{}`.",
1016            signal.name
1017        )?;
1018        writeln!(
1019            w,
1020            "pub fn {field}_multiplexed(&mut self) -> Result<{typ}, CanError> {{"
1021        )?;
1022
1023        let multiplexer_indexes: BTreeSet<u64> = msg
1024            .signals
1025            .iter()
1026            .filter_map(|s| {
1027                if let MultiplexedSignal(index) = &s.multiplexer_indicator {
1028                    Some(index)
1029                } else {
1030                    None
1031                }
1032            })
1033            .copied()
1034            .collect();
1035
1036        {
1037            let mut w = PadAdapter::wrap(w);
1038            writeln!(w, "match self.{}_raw_val() {{", signal.field_name())?;
1039
1040            {
1041                let mut w = PadAdapter::wrap(&mut w);
1042                for idx in &multiplexer_indexes {
1043                    writeln!(
1044                        w,
1045                        "{idx} => Ok({enum_name}::{multiplexed_wrapper_name}({multiplexed_name}{{ raw: self.raw }})),",
1046                        enum_name = multiplex_enum_name(msg, signal)?,
1047                        multiplexed_wrapper_name = multiplexed_enum_variant_wrapper_name(*idx),
1048                        multiplexed_name = multiplexed_enum_variant_name(msg, signal, *idx)?,
1049                    )?;
1050                }
1051                let to_u16 = match signal_typ {
1052                    ValType::UnsignedInt(IntSize::Size8) => "multiplexor.into()",
1053                    ValType::UnsignedInt(IntSize::Size16) => "multiplexor",
1054                    _ => "u16::try_from(multiplexor).unwrap_or(u16::MAX)",
1055                };
1056                writeln!(
1057                    w,
1058                    "multiplexor => Err(CanError::InvalidMultiplexor {{ message_id: {}::MESSAGE_ID, multiplexor: {to_u16} }}),",
1059                    msg.type_name(),
1060                )?;
1061            }
1062
1063            writeln!(w, "}}")?;
1064        }
1065        writeln!(w, "}}")?;
1066
1067        self.render_set_signal(w, signal, dbc, msg)?;
1068
1069        for switch_index in multiplexer_indexes {
1070            render_set_signal_multiplexer(w, signal, msg, switch_index)?;
1071        }
1072
1073        Ok(())
1074    }
1075
1076    fn write_enum(
1077        &self,
1078        w: &mut impl Write,
1079        signal: &Signal,
1080        msg: &Message,
1081        variants: &[ValDescription],
1082    ) -> Result<()> {
1083        let type_name = enum_name(msg, signal);
1084        let signal_ty = ValType::from_signal(signal);
1085
1086        // Generate variant info to handle duplicates with tuple variants
1087        let variant_infos = generate_variant_info(variants, signal_ty);
1088
1089        writeln!(w, "/// Defined values for {}", signal.name)?;
1090        writeln!(w, "{ALLOW_LINTS}")?;
1091        self.write_allow_dead_code(w)?;
1092        writeln!(w, "#[derive(Clone, Copy, PartialEq)]")?;
1093        self.impl_debug.fmt_attr(w, "derive(Debug)")?;
1094        self.impl_defmt.fmt_attr(w, "derive(defmt::Format)")?;
1095        self.impl_serde.fmt_attr(w, "derive(Serialize)")?;
1096        self.impl_serde.fmt_attr(w, "derive(Deserialize)")?;
1097        writeln!(w, "pub enum {type_name} {{")?;
1098        {
1099            let mut w = PadAdapter::wrap(w);
1100            for info in &variant_infos {
1101                let variant = &info.base_name;
1102                match info.dup_type {
1103                    DuplicateType::Unique => writeln!(w, "{variant},")?,
1104                    DuplicateType::FirstDuplicate => {
1105                        writeln!(w, "{variant}({}),", info.value_type)?;
1106                    }
1107                    DuplicateType::Duplicate => {}
1108                }
1109            }
1110            writeln!(w, "_Other({signal_ty}),")?;
1111        }
1112        writeln!(w, "}}")?;
1113        writeln!(w)?;
1114
1115        writeln!(w, "impl From<{type_name}> for {signal_ty} {{")?;
1116        {
1117            let match_on_raw_type = match ValType::from_signal(signal) {
1118                ValType::Bool => |x: i64| format!("{}", x == 1),
1119                ValType::F32 => |x: i64| format!("{x}_f32"),
1120                _ => |x: i64| format!("{x}"),
1121            };
1122
1123            let mut w = PadAdapter::wrap(w);
1124            writeln!(w, "fn from(val: {type_name}) -> {signal_ty} {{")?;
1125            {
1126                let mut w = PadAdapter::wrap(&mut w);
1127                writeln!(w, "match val {{")?;
1128                {
1129                    let mut w = PadAdapter::wrap(&mut w);
1130                    for info in &variant_infos {
1131                        match info.dup_type {
1132                            DuplicateType::Unique => {
1133                                let literal = match_on_raw_type(info.value);
1134                                writeln!(w, "{type_name}::{} => {literal},", info.base_name)?;
1135                            }
1136                            DuplicateType::FirstDuplicate => {
1137                                writeln!(w, "{type_name}::{}(v) => v,", info.base_name)?;
1138                            }
1139                            DuplicateType::Duplicate => {}
1140                        }
1141                    }
1142                    writeln!(w, "{type_name}::_Other(x) => x,")?;
1143                }
1144                writeln!(w, "}}")?;
1145            }
1146            writeln!(w, "}}")?;
1147        }
1148        writeln!(w, "}}")?;
1149        writeln!(w)?;
1150        Ok(())
1151    }
1152}
1153
1154fn render_set_signal_multiplexer(
1155    w: &mut impl Write,
1156    multiplexor: &Signal,
1157    msg: &Message,
1158    switch_index: u64,
1159) -> Result<()> {
1160    writeln!(w, "/// Sets the value of `{}`.", multiplexor.name)?;
1161    writeln!(w, "#[inline(always)]")?;
1162    writeln!(
1163        w,
1164        "pub fn set_{enum_variant_wrapper}(&mut self, value: {enum_variant}) -> Result<(), CanError> {{",
1165        enum_variant_wrapper = multiplexed_enum_variant_wrapper_name(switch_index).to_snake_case(),
1166        enum_variant = multiplexed_enum_variant_name(msg, multiplexor, switch_index)?,
1167    )?;
1168
1169    {
1170        let mut w = PadAdapter::wrap(w);
1171
1172        writeln!(w, "let b0 = BitArray::<_, LocalBits>::new(self.raw);")?;
1173        writeln!(w, "let b1 = BitArray::<_, LocalBits>::new(value.raw);")?;
1174        writeln!(w, "self.raw = b0.bitor(b1).into_inner();")?;
1175        writeln!(w, "self.set_{}({switch_index})?;", multiplexor.field_name())?;
1176        writeln!(w, "Ok(())")?;
1177    }
1178
1179    writeln!(w, "}}")?;
1180    writeln!(w)?;
1181
1182    Ok(())
1183}
1184
1185fn be_start_end_bit(signal: &Signal, msg: &Message) -> Result<(u64, u64)> {
1186    let err = "calculating start bit";
1187
1188    let x = signal.start_bit.checked_div(8).context(err)?;
1189    let x = x.checked_mul(8).context(err)?;
1190
1191    let y = signal.start_bit.checked_rem(8).context(err)?;
1192    let y = 7u64.checked_sub(y).context(err)?;
1193
1194    let start_bit = x.checked_add(y).context(err)?;
1195    let end_bit = start_bit
1196        .checked_add(signal.size)
1197        .context("calculating last bit position")?;
1198
1199    let msg_bits = msg.size.checked_mul(8).context("message size overflow")?;
1200
1201    ensure!(
1202        start_bit <= msg_bits,
1203        "signal starts at {start_bit}, but message is only {msg_bits} bits",
1204    );
1205    ensure!(
1206        end_bit <= msg_bits,
1207        "signal ends at {end_bit}, but message is only {msg_bits} bits",
1208    );
1209    Ok((start_bit, end_bit))
1210}
1211
1212fn le_start_end_bit(signal: &Signal, msg: &Message) -> Result<(u64, u64)> {
1213    let msg_bits = msg.size.checked_mul(8).context("message size overflow")?;
1214    let start_bit = signal.start_bit;
1215    ensure!(
1216        start_bit <= msg_bits,
1217        "signal starts at {start_bit}, but message is only {msg_bits} bits",
1218    );
1219
1220    let end_bit = signal
1221        .start_bit
1222        .checked_add(signal.size)
1223        .context("overflow calculating last bit position")?;
1224    ensure!(
1225        end_bit <= msg_bits,
1226        "signal ends at {end_bit}, but message is only {msg_bits} bits",
1227    );
1228    Ok((start_bit, end_bit))
1229}
1230
1231/// Public type of a signal as seen by `new()` and `set_*`. This is the
1232/// enum when the signal has one (and isn't the multiplexor selector), otherwise
1233/// it is the raw primitive type.
1234fn signal_pub_type(dbc: &Dbc, msg: &Message, signal: &Signal) -> String {
1235    if signal.multiplexer_indicator != Multiplexor
1236        && dbc
1237            .value_descriptions_for_signal(msg.id, &signal.name)
1238            .is_some()
1239    {
1240        enum_name(msg, signal)
1241    } else {
1242        ValType::from_signal(signal).to_string()
1243    }
1244}
1245
1246/// Whether the signal has no scaling applied, i.e. its raw and physical values are identical.
1247/// The float comparison to exact values is intentional.
1248#[allow(clippy::float_cmp)]
1249fn is_unscaled(signal: &Signal) -> bool {
1250    signal.factor == 1.0 && signal.offset == 0.0
1251}
1252
1253fn signal_from_payload(w: &mut impl Write, signal: &Signal, msg: &Message) -> Result<()> {
1254    if is_unscaled(signal) {
1255        let field = signal.field_name();
1256        if signal.size == 1 {
1257            writeln!(w, "self.{field}_raw_val() == 1")?;
1258        } else {
1259            writeln!(w, "self.{field}_raw_val()")?;
1260        }
1261        return Ok(());
1262    }
1263
1264    writeln!(w, r"let signal = {};", read_fn(signal, msg)?)?;
1265    writeln!(w)?;
1266
1267    let typ = ValType::from_signal(signal);
1268    match typ {
1269        ValType::Bool => {
1270            writeln!(w, "signal == 1")?;
1271        }
1272        ValType::F32 => {
1273            // Scaling is always done on floats
1274            writeln!(w, "let factor = {}_f32;", signal.factor)?;
1275            writeln!(w, "let offset = {}_f32;", signal.offset)?;
1276            writeln!(w, "(signal as f32) * factor + offset")?;
1277        }
1278        _ => {
1279            writeln!(w, "let factor = {};", signal.factor)?;
1280            if Some(typ) == ValType::from_signal_uint(signal).unsigned_to_signed() {
1281                // Can't do iNN::from(uNN) if they both fit in the same integer type,
1282                // so cast first
1283                writeln!(w, "let signal = signal as {typ};")?;
1284            }
1285
1286            if signal.offset >= 0.0 {
1287                writeln!(
1288                    w,
1289                    "{typ}::from(signal).saturating_mul(factor).saturating_add({})",
1290                    signal.offset,
1291                )?;
1292            } else {
1293                writeln!(
1294                    w,
1295                    "{typ}::from(signal).saturating_mul(factor).saturating_sub({})",
1296                    signal.offset.abs(),
1297                )?;
1298            }
1299        }
1300    }
1301    Ok(())
1302}
1303
1304fn read_fn(signal: &Signal, msg: &Message) -> Result<String> {
1305    read_fn_with_type(signal, msg, ValType::from_signal_int(signal))
1306}
1307
1308fn read_fn_with_type(signal: &Signal, msg: &Message, typ: ValType) -> Result<String> {
1309    Ok(match signal.byte_order {
1310        LittleEndian => {
1311            let (start, end) = le_start_end_bit(signal, msg)?;
1312            format!("self.raw.view_bits::<Lsb0>()[{start}..{end}].load_le::<{typ}>()")
1313        }
1314        BigEndian => {
1315            let (start, end) = be_start_end_bit(signal, msg)?;
1316            format!("self.raw.view_bits::<Msb0>()[{start}..{end}].load_be::<{typ}>()")
1317        }
1318    })
1319}
1320
1321fn signal_to_payload(w: &mut impl Write, signal: &Signal, msg: &Message) -> Result<()> {
1322    let typ = ValType::from_signal(signal);
1323    match typ {
1324        ValType::Bool => {
1325            // Map boolean to byte so we can pack it
1326            writeln!(w, "let value = value as u8;")?;
1327        }
1328        ValType::F32 => {
1329            // Massage value into an int
1330            writeln!(w, "let factor = {}_f32;", signal.factor)?;
1331            writeln!(w, "let offset = {}_f32;", signal.offset)?;
1332            let typ = ValType::from_signal_int(signal);
1333            writeln!(w, "let value = ((value - offset) / factor) as {typ};")?;
1334            writeln!(w)?;
1335        }
1336        _ if is_unscaled(signal) => {
1337            // `value` is already the raw type.
1338        }
1339        _ => {
1340            writeln!(w, "let factor = {};", signal.factor)?;
1341            if signal.offset >= 0.0 {
1342                writeln!(w, "let value = value.checked_sub({})", signal.offset)?;
1343            } else {
1344                writeln!(w, "let value = value.checked_add({})", signal.offset.abs())?;
1345            }
1346            writeln!(
1347                w,
1348                "    .ok_or(CanError::ParameterOutOfRange {{ message_id: {}::MESSAGE_ID }})?;",
1349                msg.type_name(),
1350            )?;
1351            let typ = ValType::from_signal_int(signal);
1352            writeln!(w, "let value = (value / factor) as {typ};")?;
1353            writeln!(w)?;
1354        }
1355    }
1356
1357    pack_bits(w, signal, msg)?;
1358
1359    writeln!(w, "Ok(())")?;
1360    Ok(())
1361}
1362
1363/// Pack `value` into the message payload.
1364fn pack_bits(w: &mut impl Write, signal: &Signal, msg: &Message) -> Result<()> {
1365    if signal.value_type == Signed {
1366        let typ = ValType::from_signal_uint(signal);
1367        writeln!(w, "let value = {typ}::from_ne_bytes(value.to_ne_bytes());")?;
1368    }
1369
1370    match signal.byte_order {
1371        LittleEndian => {
1372            let (start, end) = le_start_end_bit(signal, msg)?;
1373            writeln!(
1374                w,
1375                r"self.raw.view_bits_mut::<Lsb0>()[{start}..{end}].store_le(value);",
1376            )?;
1377        }
1378        BigEndian => {
1379            let (start, end) = be_start_end_bit(signal, msg)?;
1380            writeln!(
1381                w,
1382                r"self.raw.view_bits_mut::<Msb0>()[{start}..{end}].store_be(value);",
1383            )?;
1384        }
1385    }
1386
1387    Ok(())
1388}
1389
1390/// Generate the raw getter and setter for a signal.
1391fn render_raw_accessors(w: &mut impl Write, signal: &Signal, msg: &Message) -> Result<()> {
1392    let field = signal.field_name();
1393    let typ = ValType::from_signal_int(signal);
1394
1395    writeln!(w, "/// Returns the raw value of `{}`.", signal.name)?;
1396    writeln!(w, "///")?;
1397    writeln!(w, "/// - Start bit: {}", signal.start_bit)?;
1398    writeln!(w, "/// - Signal size: {} bits", signal.size)?;
1399    writeln!(w, "/// - Byte order: {:?}", signal.byte_order)?;
1400    writeln!(w, "/// - Value type: {:?}", signal.value_type)?;
1401    writeln!(w, "#[inline(always)]")?;
1402    writeln!(w, "pub fn {field}_raw_val(&self) -> {typ} {{")?;
1403    {
1404        let mut w = PadAdapter::wrap(&mut *w);
1405        writeln!(w, "{}", read_fn(signal, msg)?)?;
1406    }
1407    writeln!(w, "}}")?;
1408    writeln!(w)?;
1409
1410    // To avoid accidentally changing the multiplexor value without changing
1411    // the signals accordingly this fn is kept private for multiplexors.
1412    let visibility = if signal.multiplexer_indicator == Multiplexor {
1413        ""
1414    } else {
1415        "pub "
1416    };
1417
1418    writeln!(w, "/// Sets the raw value of `{}`.", signal.name)?;
1419    if visibility.is_empty() {
1420        // Private multiplexor raw setters have no internal caller.
1421        writeln!(w, "#[allow(dead_code)]")?;
1422    }
1423    writeln!(w, "#[inline(always)]")?;
1424    writeln!(
1425        w,
1426        "{visibility}fn set_{field}_raw_val(&mut self, value: {typ}) {{",
1427    )?;
1428    {
1429        let mut w = PadAdapter::wrap(&mut *w);
1430        pack_bits(&mut w, signal, msg)?;
1431    }
1432    writeln!(w, "}}")?;
1433    writeln!(w)?;
1434
1435    Ok(())
1436}
1437
1438enum DuplicateType {
1439    Unique,
1440    FirstDuplicate,
1441    Duplicate,
1442}
1443
1444/// Variant info for enum generation
1445struct VariantInfo {
1446    base_name: String,
1447    value: i64,
1448    dup_type: DuplicateType,
1449    value_type: String,
1450}
1451
1452/// Generate variant info for enum generation.
1453/// For duplicates, uses tuple variants like `Reserved(u8)` instead of separate variants.
1454fn generate_variant_info(variants: &[ValDescription], signal_ty: ValType) -> Vec<VariantInfo> {
1455    // First pass: count occurrences of each base name
1456    let mut name_counts: HashMap<String, usize> = HashMap::new();
1457    for variant in variants {
1458        let base_name = enum_variant_name(&variant.description);
1459        name_counts
1460            .entry(base_name)
1461            .and_modify(|c| *c = c.saturating_add(1))
1462            .or_insert(1);
1463    }
1464
1465    // Second pass: generate variant info
1466    let mut variant_infos = Vec::new();
1467    let mut seen_names: HashMap<String, usize> = HashMap::new();
1468    for variant in variants {
1469        let base_name = enum_variant_name(&variant.description);
1470        let count = name_counts.get(&base_name).copied().unwrap_or(0);
1471        let seen_count = seen_names.entry(base_name.clone()).or_insert(0);
1472        *seen_count = (*seen_count).saturating_add(1);
1473
1474        let dup_type = if count == 1 {
1475            DuplicateType::Unique
1476        } else if *seen_count == 1 {
1477            DuplicateType::FirstDuplicate
1478        } else {
1479            DuplicateType::Duplicate
1480        };
1481
1482        variant_infos.push(VariantInfo {
1483            base_name,
1484            value: variant.id,
1485            dup_type,
1486            value_type: signal_ty.to_string(),
1487        });
1488    }
1489    variant_infos
1490}
1491
1492impl Config<'_> {
1493    fn render_embedded_can_frame(&self, w: &mut impl Write, msg: &Message) -> Result<()> {
1494        self.impl_embedded_can_frame.fmt_cfg(w, |w| {
1495            writeln!(
1496                w,
1497                "\
1498impl embedded_can::Frame for {0} {{
1499    fn new(id: impl Into<Id>, data: &[u8]) -> Option<Self> {{
1500        if id.into() != Self::MESSAGE_ID {{
1501            None
1502        }} else {{
1503            data.try_into().ok()
1504        }}
1505    }}
1506
1507    fn new_remote(_id: impl Into<Id>, _dlc: usize) -> Option<Self> {{
1508        unimplemented!()
1509    }}
1510
1511    fn is_extended(&self) -> bool {{
1512        match self.id() {{
1513            Id::Standard(_) => false,
1514            Id::Extended(_) => true,
1515        }}
1516    }}
1517
1518    fn is_remote_frame(&self) -> bool {{
1519        false
1520    }}
1521
1522    fn id(&self) -> Id {{
1523        Self::MESSAGE_ID
1524    }}
1525
1526    fn dlc(&self) -> usize {{
1527        self.raw.len()
1528    }}
1529
1530    fn data(&self) -> &[u8] {{
1531        &self.raw
1532    }}
1533}}",
1534                msg.type_name(),
1535            )
1536        })
1537    }
1538}
1539
1540fn render_debug_impl(w: &mut impl Write, msg: &Message) -> Result<()> {
1541    let typ = msg.type_name();
1542    writeln!(w, r"impl core::fmt::Debug for {typ} {{")?;
1543    {
1544        let mut w = PadAdapter::wrap(w);
1545        writeln!(
1546            w,
1547            "fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {{",
1548        )?;
1549        {
1550            let mut w = PadAdapter::wrap(&mut w);
1551            writeln!(w, r"if f.alternate() {{")?;
1552            {
1553                let mut w = PadAdapter::wrap(&mut w);
1554                writeln!(w, r#"f.debug_struct("{typ}")"#)?;
1555                {
1556                    let mut w = PadAdapter::wrap(&mut w);
1557                    for signal in &msg.signals {
1558                        if signal.multiplexer_indicator == Plain {
1559                            writeln!(w, r#".field("{0}", &self.{0}())"#, signal.field_name())?;
1560                        }
1561                    }
1562                }
1563                writeln!(w, r".finish()")?;
1564            }
1565            writeln!(w, r"}} else {{")?;
1566            {
1567                let mut w = PadAdapter::wrap(&mut w);
1568                writeln!(w, r#"f.debug_tuple("{typ}").field(&self.raw).finish()"#)?;
1569            }
1570            writeln!(w, "}}")?;
1571        }
1572        writeln!(w, "}}")?;
1573    }
1574    writeln!(w, "}}")?;
1575    writeln!(w)?;
1576    Ok(())
1577}
1578
1579fn render_defmt_impl(w: &mut impl Write, msg: &Message) -> Result<()> {
1580    let typ = msg.type_name();
1581    writeln!(w, r"impl defmt::Format for {typ} {{")?;
1582    {
1583        let mut w = PadAdapter::wrap(w);
1584        writeln!(w, "fn format(&self, f: defmt::Formatter) {{")?;
1585        {
1586            let mut w = PadAdapter::wrap(&mut w);
1587            writeln!(w, r"defmt::write!(f,")?;
1588            {
1589                let mut w = PadAdapter::wrap(&mut w);
1590                write!(w, r#""{typ} {{{{"#)?;
1591                {
1592                    for signal in &msg.signals {
1593                        if signal.multiplexer_indicator == Plain {
1594                            write!(w, r" {}={{:?}}", signal.name)?;
1595                        }
1596                    }
1597                }
1598                writeln!(w, r#" }}}}","#)?;
1599
1600                for signal in &msg.signals {
1601                    if signal.multiplexer_indicator == Plain {
1602                        writeln!(w, "self.{}(),", signal.field_name())?;
1603                    }
1604                }
1605                writeln!(w, r");")?;
1606            }
1607            writeln!(w, "}}")?;
1608        }
1609    }
1610    writeln!(w, "}}")?;
1611    writeln!(w)?;
1612    Ok(())
1613}
1614
1615impl Config<'_> {
1616    fn render_multiplexor_enums(
1617        &self,
1618        w: &mut impl Write,
1619        dbc: &Dbc,
1620        msg: &Message,
1621        multiplexor_signal: &Signal,
1622    ) -> Result<()> {
1623        ensure!(
1624            multiplexor_signal.multiplexer_indicator == Multiplexor,
1625            "signal {} is not the multiplexor",
1626            multiplexor_signal.name,
1627        );
1628
1629        let mut multiplexed_signals = BTreeMap::new();
1630        for signal in &msg.signals {
1631            if let MultiplexedSignal(switch_index) = signal.multiplexer_indicator {
1632                multiplexed_signals
1633                    .entry(switch_index)
1634                    .and_modify(|v: &mut Vec<&Signal>| v.push(signal))
1635                    .or_insert_with(|| vec![&signal]);
1636            }
1637        }
1638
1639        writeln!(w, "/// Defined values for multiplexed signal {}", msg.name)?;
1640        writeln!(w, "{ALLOW_LINTS}")?;
1641        self.write_allow_dead_code(w)?;
1642
1643        self.impl_debug.fmt_attr(w, "derive(Debug)")?;
1644        self.impl_defmt.fmt_attr(w, "derive(defmt::Format)")?;
1645        self.impl_serde.fmt_attr(w, "derive(Serialize)")?;
1646        self.impl_serde.fmt_attr(w, "derive(Deserialize)")?;
1647
1648        let enum_name = multiplex_enum_name(msg, multiplexor_signal)?;
1649        writeln!(w, "pub enum {enum_name} {{")?;
1650
1651        {
1652            let mut w = PadAdapter::wrap(w);
1653            for switch_index in multiplexed_signals.keys() {
1654                writeln!(
1655                    w,
1656                    "{multiplexed_wrapper_name}({multiplexed_name}),",
1657                    multiplexed_wrapper_name = multiplexed_enum_variant_wrapper_name(*switch_index),
1658                    multiplexed_name =
1659                        multiplexed_enum_variant_name(msg, multiplexor_signal, *switch_index)?,
1660                )?;
1661            }
1662        }
1663        writeln!(w, "}}")?;
1664        writeln!(w)?;
1665
1666        for (switch_index, multiplexed_signals) in &multiplexed_signals {
1667            let struct_name =
1668                multiplexed_enum_variant_name(msg, multiplexor_signal, *switch_index)?;
1669
1670            writeln!(w, "{ALLOW_LINTS}")?;
1671            self.write_allow_dead_code(w)?;
1672            self.impl_debug.fmt_attr(w, "derive(Debug)")?;
1673            self.impl_defmt.fmt_attr(w, "derive(defmt::Format)")?;
1674            self.impl_serde.fmt_attr(w, "derive(Serialize)")?;
1675            self.impl_serde.fmt_attr(w, "derive(Deserialize)")?;
1676            writeln!(w, r"#[derive(Default)]")?;
1677            writeln!(w, "pub struct {struct_name} {{ raw: [u8; {}] }}", msg.size)?;
1678            writeln!(w)?;
1679
1680            writeln!(w, "{ALLOW_LINTS}")?;
1681            self.write_allow_dead_code(w)?;
1682            writeln!(w, "impl {struct_name} {{")?;
1683
1684            writeln!(
1685                w,
1686                "pub fn new() -> Self {{ Self {{ raw: [0u8; {}] }} }}",
1687                msg.size
1688            )?;
1689
1690            for signal in multiplexed_signals {
1691                self.render_signal(w, signal, dbc, msg)?;
1692            }
1693
1694            writeln!(w, "}}")?;
1695            writeln!(w)?;
1696        }
1697
1698        Ok(())
1699    }
1700
1701    fn render_arbitrary(&self, w: &mut impl Write, dbc: &Dbc, msg: &Message) -> Result<()> {
1702        writeln!(w, "{ALLOW_LINTS}")?;
1703        self.write_allow_dead_code(w)?;
1704        let typ = msg.type_name();
1705        writeln!(w, "impl<'a> Arbitrary<'a> for {typ} {{")?;
1706        {
1707            let filtered_signals: Vec<&Signal> = msg
1708                .signals
1709                .iter()
1710                .filter(|v| matches!(v.multiplexer_indicator, Plain | Multiplexor))
1711                .collect();
1712            let mut w = PadAdapter::wrap(w);
1713            writeln!(
1714                w,
1715                "fn arbitrary({}u: &mut Unstructured<'a>) -> Result<Self, arbitrary::Error> {{",
1716                if filtered_signals.is_empty() { "_" } else { "" },
1717            )?;
1718            {
1719                let mut w = PadAdapter::wrap(&mut w);
1720
1721                for signal in &filtered_signals {
1722                    writeln!(
1723                        w,
1724                        "let {field_name} = {arbitrary_value};",
1725                        field_name = signal.field_name(),
1726                        arbitrary_value = signal_to_arbitrary(signal),
1727                    )?;
1728                }
1729
1730                let args: Vec<String> = filtered_signals
1731                    .iter()
1732                    .map(|signal| {
1733                        let field = signal.field_name();
1734                        let is_enum_backed = signal_pub_type(dbc, msg, signal)
1735                            != ValType::from_signal(signal).to_string();
1736                        if is_enum_backed {
1737                            format!("{}::_Other({field})", enum_name(msg, signal))
1738                        } else {
1739                            field
1740                        }
1741                    })
1742                    .collect();
1743
1744                writeln!(
1745                    w,
1746                    "{typ}::new({args}).map_err(|_| arbitrary::Error::IncorrectFormat)",
1747                    typ = msg.type_name(),
1748                    args = args.join(","),
1749                )?;
1750            }
1751            writeln!(w, "}}")?;
1752        }
1753        writeln!(w, "}}")?;
1754
1755        Ok(())
1756    }
1757
1758    fn render_error(&self, w: &mut impl Write) -> Result<()> {
1759        w.write_all(
1760            r#"
1761        #[allow(dead_code)]
1762#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1763pub enum CanError {
1764    UnknownMessageId(embedded_can::Id),
1765    /// Signal parameter is not within the range
1766    /// defined in the dbc
1767    ParameterOutOfRange {
1768        /// dbc message id
1769        message_id: embedded_can::Id,
1770    },
1771    InvalidPayloadSize,
1772    /// Multiplexor value not defined in the dbc
1773    InvalidMultiplexor {
1774        /// dbc message id
1775        message_id: embedded_can::Id,
1776        /// Multiplexor value not defined in the dbc
1777        multiplexor: u16,
1778    },
1779}
1780
1781impl core::fmt::Display for CanError {
1782    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1783        write!(f, "{self:?}")
1784    }
1785}
1786        "#
1787            .as_bytes(),
1788        )?;
1789
1790        self.impl_error.fmt_cfg(w, |w| {
1791            writeln!(w, "impl core::error::Error for CanError {{}}")
1792        })
1793    }
1794
1795    fn render_arbitrary_helpers(&self, w: &mut impl Write) -> Result<()> {
1796        self.impl_arbitrary.fmt_cfg(&mut *w, |w| {
1797            self.write_allow_dead_code(w)?;
1798            writeln!(w, "trait UnstructuredFloatExt {{")?;
1799            writeln!(w, "    fn float_in_range(&mut self, range: core::ops::RangeInclusive<f32>) -> arbitrary::Result<f32>;")?;
1800            writeln!(w, "}}")?;
1801            writeln!(w)?;
1802            Ok::<_, Error>(())
1803        })?;
1804
1805        self.impl_arbitrary.fmt_cfg(w, |w| {
1806            writeln!(w, "impl UnstructuredFloatExt for arbitrary::Unstructured<'_> {{")?;
1807            writeln!(w, "    fn float_in_range(&mut self, range: core::ops::RangeInclusive<f32>) -> arbitrary::Result<f32> {{")?;
1808            writeln!(w, "        let min = range.start();")?;
1809            writeln!(w, "        let max = range.end();")?;
1810            writeln!(w, "        let steps = u32::MAX;")?;
1811            writeln!(w, "        let factor = (max - min) / (steps as f32);")?;
1812            writeln!(w, "        let random_int: u32 = self.int_in_range(0..=steps)?;")?;
1813            writeln!(w, "        let random = min + factor * (random_int as f32);")?;
1814            writeln!(w, "        Ok(random)")?;
1815            writeln!(w, "    }}")?;
1816            writeln!(w, "}}")?;
1817            writeln!(w)?;
1818            Ok::<_, Error>(())
1819        })
1820    }
1821}
1822
1823fn signal_to_arbitrary(signal: &Signal) -> String {
1824    let typ = ValType::from_signal(signal);
1825    match typ {
1826        ValType::Bool => "u.int_in_range(0..=1)? == 1".to_string(),
1827        ValType::F32 => {
1828            let min = signal.min;
1829            let max = signal.max;
1830            format!("u.float_in_range({min}_f32..={max}_f32)?")
1831        }
1832        _ => {
1833            let min = signal.min;
1834            let max = signal.max;
1835            format!("u.int_in_range({min}..={max})?")
1836        }
1837    }
1838}
1839
1840fn get_relevant_messages(dbc: &Dbc) -> impl Iterator<Item = &Message> {
1841    dbc.messages.iter().filter(|v| !message_ignored(v))
1842}
1843
1844fn message_ignored(message: &Message) -> bool {
1845    // DBC internal message containing signals unassigned to any real message
1846    message.name == "VECTOR__INDEPENDENT_SIG_MSG"
1847}
1848
1849/// Resolve a [`FieldSource`] to a Rust literal.
1850fn resolve_field_source(source: &FieldSource<'_>, target: &AttrTarget<'_>) -> Option<String> {
1851    let AttrTarget {
1852        msg,
1853        dbc,
1854        signal,
1855        node,
1856    } = *target;
1857    match source {
1858        FieldSource::Attr(name) => match (signal, node) {
1859            (Some(s), None) => dbc.resolved_signal_attribute(msg.id, &s.name, name),
1860            (None, None) => dbc.resolved_message_attribute(msg.id, name),
1861            (Some(s), Some(n)) => {
1862                resolved_node_relation_attribute(dbc, n, msg.id, Some(&s.name), name)
1863            }
1864            (None, Some(n)) => resolved_node_relation_attribute(dbc, n, msg.id, None, name),
1865        }
1866        .map(attr_value_literal),
1867        FieldSource::MessageAttr(name) => dbc
1868            .resolved_message_attribute(msg.id, name)
1869            .map(attr_value_literal),
1870        FieldSource::StartBit => signal.map(|s| s.start_bit.to_string()),
1871        FieldSource::StartByte => signal.map(|s| signal_start_byte(s).to_string()),
1872        FieldSource::BitWidth => signal.map(|s| s.size.to_string()),
1873        FieldSource::MessageSize => Some(msg.size.to_string()),
1874        FieldSource::Int(v) => Some(v.to_string()),
1875        FieldSource::Str(v) => Some(format!("{v:?}")),
1876        FieldSource::NodeName => node.map(|n| format!("{n:?}")),
1877    }
1878}
1879
1880/// Lookup an assigned node relation attribute value. This can be either
1881/// node-to-signal (`BA_REL_ ... BU_SG_REL_`, `signal_name: Some`) or
1882/// node-to-message (`BA_REL_ ... BU_BO_REL_`, `signal_name: None`).
1883fn node_relation_attribute<'a>(
1884    dbc: &'a Dbc,
1885    node_name: &str,
1886    message_id: MessageId,
1887    signal_name: Option<&str>,
1888    name: &str,
1889) -> Option<&'a AttributeValue> {
1890    dbc.relation_attribute_values.iter().find_map(|rel| {
1891        if rel.name != name {
1892            return None;
1893        }
1894        match (&rel.details, signal_name) {
1895            (
1896                AttributeValueForRelationType::NodeToSignal {
1897                    node_name: n,
1898                    message_id: mid,
1899                    signal_name: sig,
1900                    value,
1901                },
1902                Some(want_sig),
1903            ) if n == node_name && *mid == message_id && sig == want_sig => Some(value),
1904            (
1905                AttributeValueForRelationType::NodeToMessage {
1906                    node_name: n,
1907                    message_id: mid,
1908                    value,
1909                },
1910                None,
1911            ) if n == node_name && *mid == message_id => Some(value),
1912            _ => None,
1913        }
1914    })
1915}
1916
1917/// Lookup a node relation attribute value.
1918/// Uses the default (`BA_DEF_DEF_REL_`) if a value is not assigned.
1919fn resolved_node_relation_attribute<'a>(
1920    dbc: &'a Dbc,
1921    node_name: &str,
1922    message_id: MessageId,
1923    signal_name: Option<&str>,
1924    name: &str,
1925) -> Option<&'a AttributeValue> {
1926    node_relation_attribute(dbc, node_name, message_id, signal_name, name).or_else(|| {
1927        dbc.relation_attribute_defaults
1928            .iter()
1929            .find(|d| d.name == name)
1930            .map(|d| &d.value)
1931    })
1932}
1933
1934/// Byte index of a signal's start bit.
1935fn signal_start_byte(signal: &Signal) -> u64 {
1936    signal.start_bit.checked_div(8).unwrap_or(0)
1937}
1938
1939/// Render an attribute value as an un-suffixed Rust literal.
1940fn attr_value_literal(value: &AttributeValue) -> String {
1941    match value {
1942        AttributeValue::Uint(v) => v.to_string(),
1943        AttributeValue::Int(v) => v.to_string(),
1944        AttributeValue::Double(v) => format!("{v:?}"),
1945        AttributeValue::String(v) => format!("{v:?}"),
1946    }
1947}
1948
1949/// Cycle time in milliseconds from an assigned `GenMsgCycleTime` (`BA_`)
1950/// attribute, if it is present and a non-negative integer that fits within a `u32`.
1951fn message_cycle_time_ms(dbc: &Dbc, id: MessageId) -> Option<u32> {
1952    match dbc.message_attribute(id, "GenMsgCycleTime")? {
1953        AttributeValue::Uint(v) => u32::try_from(*v).ok(),
1954        AttributeValue::Int(v) => u32::try_from(*v).ok(),
1955        AttributeValue::Double(_) | AttributeValue::String(_) => None,
1956    }
1957}
1958
1959impl Config<'_> {
1960    /// Generate Rust structs matching DBC input description and return as String
1961    pub fn generate(self) -> Result<String> {
1962        let mut out = Vec::new();
1963        self.codegen(&mut out)
1964            .context("could not generate Rust code")?;
1965        // Parse and re-format the generated code to allow for future
1966        // syn/quote codegen migration. Note that this is inefficient at the moment,
1967        // but this shouldn't be significantly noticeable.
1968        let out = std::str::from_utf8(&out).context("Failed to convert output to str")?;
1969        let file = syn::parse_file(out).context("Failed to parse generated Rust code")?;
1970        Ok(prettyplease::unparse(&file))
1971    }
1972
1973    /// Generate Rust structs matching DBC input description and write to `out`
1974    pub fn write(self, mut out: impl Write) -> Result<()> {
1975        out.write_all(self.generate()?.as_bytes())?;
1976        Ok(())
1977    }
1978
1979    /// Generate Rust structs matching DBC input description and write to file at `path`
1980    pub fn write_to_file<P: AsRef<Path>>(self, path: P) -> Result<()> {
1981        let file = OpenOptions::new()
1982            .write(true)
1983            .truncate(true)
1984            .create(true)
1985            .open(path.as_ref())?;
1986
1987        self.write(file)
1988    }
1989
1990    fn write_allow_dead_code(&self, w: &mut impl Write) -> Result<()> {
1991        if self.allow_dead_code {
1992            writeln!(w, "{ALLOW_DEADCODE}")?;
1993        }
1994        Ok(())
1995    }
1996}