dbc_data/
lib.rs

1//! A derive-macro which produces code to access signals within CAN
2//! messages, as described by a `.dbc` file.  The generated code has
3//! very few dependencies: just core primitives and `[u8]` slices, and
4//! is `#[no_std]` compatible.
5//!
6//! # Changelog
7//! [CHANGELOG.md]
8//!
9//! # Example
10//! Given a `.dbc` file containing:
11//!
12//! ```text
13//! BO_ 1023 SomeMessage: 4 Ecu1
14//!  SG_ Unsigned16 : 16|16@0+ (1,0) [0|0] "" Vector__XXX
15//!  SG_ Unsigned8 : 8|8@1+ (1,0) [0|0] "" Vector__XXX
16//!  SG_ Signed8 : 0|8@1- (1,0) [0|0] "" Vector__XXX
17//! ```
18//! The following code can be written to access the fields of the
19//! message:
20//!
21//! ```
22//! pub use dbc_data::*;
23//!
24//! #[derive(DbcData, Default)]
25//! #[dbc_file = "tests/example.dbc"]
26//! struct TestData {
27//!     some_message: SomeMessage,
28//! }
29//!
30//! fn test() {
31//!     let mut t = TestData::default();
32//!
33//!     assert_eq!(SomeMessage::ID, 1023);
34//!     assert_eq!(SomeMessage::DLC, 4);
35//!     assert!(t.some_message.decode(&[0xFE, 0x34, 0x56, 0x78]));
36//!     assert_eq!(t.some_message.Signed8, -2);
37//!     assert_eq!(t.some_message.Unsigned8, 0x34);
38//!     assert_eq!(t.some_message.Unsigned16, 0x5678); // big-endian
39//! }
40//! ```
41//! See the test cases in this crate for examples of usage.
42//!
43//! # Code Generation
44//! This crate is aimed at embedded systems where typically some
45//! subset of the messages and signals defined in the `.dbc` file are
46//! of interest, and the rest can be ignored for a minimal footpint.
47//! If you need to decode the entire DBC into rich (possibly
48//! `std`-dependent) types to run on a host system, there are other
49//! crates for that such as `dbc_codegen`.
50//!
51//! ## Messages
52//! As `.dbc` files typically contain multiple messages, each of these
53//! can be brought into scope by referencing their name as a type
54//! (e.g. `SomeMessage` as shown above) and this determines what code
55//! is generated.  Messages not referenced will not generate any code.
56//!
57//! When a range of message IDs contain the same signals, such as a
58//! series of readings which do not fit into a single message, then
59//! declaring an array will allow that type to be used for all of
60//! them.
61//!
62//! # Signals
63//! For cases where only certain signals within a message are needed,
64//! the `#[dbc_signals]` attribute lets you specify which ones are
65//! used.
66//!
67//! ## Types
68//! Single-bit signals generate `bool` types, and signals with a scale
69//! factor generate `f32` types.  All other signals generate signed or
70//! unsigned native types which are large enough to fit the contained
71//! values, e.g.  13-bit signals will be stored in a `u16` and 17-bit
72//! signals will be stored in a `u32`.
73//!
74//! # Usage
75//! As DBC message names tend to follow different conventions from Rust
76//! code, it can be helpful to wrap them in newtype declarations.
77//! Additionally, it is often desirable to scope these identifiers away
78//! from application code by using a private module:
79//!
80//! ```ignore
81//! mod private {
82//!     use dbc_data::DbcData;
83//!     #[derive(DbcData)]
84//!     // (struct with DBC messages, e.g. some_Message_NAME)
85//! }
86//!
87//! pub type SomeMessageName = private::some_Message_NAME;
88//!
89//! ```
90//!
91//! The application uses this wrapped type without exposure to the
92//! DBC-centric naming.  The wrapped types can have their own `impl`
93//! block(s) to extend functionality, if desired.  Functions which
94//! perform operations on signals, define new constants, etc. can be
95//! added in such blocks.  The application can access signal fields
96//! directly from the underlying type and/or use the wrapped
97//! interfaces.
98//!
99//! # Functionality
100//! * Decode signals from PDU into native types
101//!     * const definitions for `ID: u32`, `DLC: u8`, `EXTENDED: bool`,
102//!       and `CYCLE_TIME: usize` when present
103//! * Encode signal into PDU (except unaligned BE)
104//!
105//! # TODO
106//! * Encode unaligned BE signals
107//! * Generate dispatcher for decoding based on ID (including ranges)
108//! * Enforce that arrays of messages contain the same signals
109//! * Support multiplexed signals
110//! * Emit `enum`s for value-tables, with optional type association
111//!
112//! # License
113//! [LICENSE-MIT]
114//!
115
116extern crate proc_macro;
117use can_dbc::{
118    AttributeValuedForObjectType, ByteOrder, MessageId, Signal, ValueType, DBC,
119};
120use proc_macro2::TokenStream;
121use quote::{quote, TokenStreamExt};
122use std::{collections::BTreeMap, fs::read};
123use syn::{
124    parse_macro_input, parse_quote, spanned::Spanned, Attribute, Data,
125    DeriveInput, Expr, Field, Fields, Ident, Lit, Meta, Result, Type,
126};
127
128struct DeriveData<'a> {
129    /// Name of the struct we are deriving for
130    #[allow(dead_code)]
131    name: &'a Ident,
132    /// The parsed DBC file
133    dbc: can_dbc::DBC,
134    /// All of the messages to derive
135    messages: BTreeMap<String, MessageInfo<'a>>,
136}
137
138struct MessageInfo<'a> {
139    id: u32,
140    extended: bool,
141    index: usize,
142    ident: &'a Ident,
143    attrs: &'a Vec<Attribute>,
144    cycle_time: Option<usize>,
145}
146
147/// Filter signals based on #[dbc_signals] list
148struct SignalFilter {
149    names: Vec<String>,
150}
151
152impl SignalFilter {
153    /// Create a signal filter from a message's attribute
154    fn new(message: &MessageInfo) -> Self {
155        let mut names: Vec<String> = vec![];
156        if let Some(attrs) = parse_attr(message.attrs, "dbc_signals") {
157            let list = attrs.split(",");
158            for name in list {
159                let name = name.trim();
160                names.push(name.to_string());
161            }
162        }
163        Self { names }
164    }
165
166    /// Return whether a signal should be used, i.e. whether it is
167    /// in the filter list or the list is empty
168    fn use_signal(&self, name: impl Into<String>) -> bool {
169        if self.names.is_empty() {
170            return true;
171        }
172        let name = name.into();
173        self.names.contains(&name)
174    }
175}
176
177/// Information about signal within message
178struct SignalInfo<'a> {
179    /// The DBC signal reference
180    signal: &'a Signal,
181    /// Our source identifier
182    ident: Ident,
183    /// The native type identifier
184    ntype: Ident,
185    /// The unsigned type used for encoding/decoding
186    utype: Ident,
187    /// The start bit of the signal within the PDU
188    start: usize,
189    /// The width (in bits) of the signal
190    width: usize,
191    /// The native width of the type containing the signal
192    nwidth: usize,
193    /// The scale-factor for the signal
194    scale: f32,
195    /// Indicates signed v.s. unsigned signal
196    signed: bool,
197}
198
199impl<'a> SignalInfo<'a> {
200    /// Create signal information
201    fn new(signal: &'a Signal, message: &MessageInfo) -> Self {
202        // TODO: sanitize and/or change name format
203        let name = signal.name();
204        let signed = matches!(signal.value_type(), ValueType::Signed);
205        let width = *signal.signal_size() as usize;
206        let scale = *signal.factor() as f32;
207
208        // get storage width of signal data
209        let nwidth = match width {
210            1 => 1,
211            2..=8 => 8,
212            9..=16 => 16,
213            17..=32 => 32,
214            _ => 64,
215        };
216
217        let utype = if width == 1 {
218            "bool"
219        } else {
220            &format!("{}{}", if signed { "i" } else { "u" }, nwidth)
221        };
222
223        // get native type for signal
224        let ntype = if scale == 1.0 { utype } else { "f32" };
225
226        Self {
227            signal,
228            ident: Ident::new(name, message.ident.span()),
229            ntype: Ident::new(ntype, message.ident.span()),
230            utype: Ident::new(utype, message.ident.span()),
231            start: *signal.start_bit() as usize,
232            scale,
233            signed,
234            width,
235            nwidth,
236        }
237    }
238
239    /// Produce an identifier for the DBC f64 value
240    fn const_ident(&self, v: f64) -> Expr {
241        if self.is_float() {
242            let v = v as f32;
243            parse_quote!(#v)
244        } else {
245            if self.width == 1 {
246                let b = v != 0.0;
247                parse_quote!(#b)
248            } else {
249                let v = v as usize;
250                let t = self.ntype.clone();
251                // TODO: make this less verbose and use type directly
252                parse_quote!(#v as #t)
253            }
254        }
255    }
256
257    /// Generate the code for extracting signal bits
258    fn extract_bits(&self) -> TokenStream {
259        if self.width == self.nwidth && (self.start % 8) == 0 {
260            self.extract_aligned()
261        } else {
262            self.extract_unaligned()
263        }
264    }
265
266    /// Code generation for aligned signal bits
267    fn extract_aligned(&self) -> TokenStream {
268        let low = self.start / 8;
269        let utype = &self.utype;
270        let le = self.signal.byte_order() == &ByteOrder::LittleEndian;
271        let mut ts = TokenStream::new();
272
273        let ext = if le {
274            Ident::new("from_le_bytes", utype.span())
275        } else {
276            Ident::new("from_be_bytes", utype.span())
277        };
278
279        let tokens = match self.width {
280            8 => quote! {
281                #utype::#ext([pdu[#low]])
282            },
283            16 => quote! {
284                #utype::#ext([pdu[#low],
285                              pdu[#low + 1]])
286            },
287            32 => quote! {
288                #utype::#ext([pdu[#low + 0],
289                              pdu[#low + 1],
290                              pdu[#low + 2],
291                              pdu[#low + 3]])
292            },
293            // NOTE: this compiles to very small code and does not
294            // involve actually fetching 8 separate bytes; e.g. on
295            // armv7 an `ldrd` to get both 32-bit values followed by
296            // two `rev` instructions to reverse the bytes.
297            64 => quote! {
298                #utype::#ext([pdu[#low + 0],
299                              pdu[#low + 1],
300                              pdu[#low + 2],
301                              pdu[#low + 3],
302                              pdu[#low + 4],
303                              pdu[#low + 5],
304                              pdu[#low + 6],
305                              pdu[#low + 7],
306                ])
307            },
308            _ => unimplemented!(),
309        };
310        ts.append_all(tokens);
311        quote! { { #ts } }
312    }
313
314    /// Code generation for unaligned signals
315    fn extract_unaligned(&self) -> TokenStream {
316        let low = self.start / 8;
317        let left = self.start % 8;
318        let high = (self.start + self.width - 1) / 8;
319        let right = (self.start + self.width) % 8;
320        let utype = &self.utype;
321        let le = self.signal.byte_order() == &ByteOrder::LittleEndian;
322
323        let mut ts = TokenStream::new();
324        if le {
325            let count = high - low;
326            for o in 0..=count {
327                let byte = low + o;
328                if o == 0 {
329                    // first byte
330                    ts.append_all(quote! {
331                        let v = pdu[#byte] as #utype;
332                    });
333                    if left != 0 {
334                        if count == 0 {
335                            let width = self.width;
336                            ts.append_all(quote! {
337                                let v = (v >> #left) & ((1 << #width) - 1);
338                            });
339                        } else {
340                            ts.append_all(quote! {
341                                let v = v >> #left;
342                            });
343                        }
344                    } else {
345                        let rem = self.width;
346                        ts.append_all(quote! {
347                            let v = v & ((1 << #rem) -1);
348                        });
349                    }
350                } else {
351                    let shift = (o * 8) - left;
352                    if o == count && right != 0 {
353                        ts.append_all(quote! {
354                            let v = v | (((pdu[#byte]
355                                           & ((1 << #right) - 1))
356                                          as #utype) << #shift);
357                        });
358                    } else {
359                        ts.append_all(quote! {
360                            let v = v | ((pdu[#byte] as #utype) << #shift);
361                        });
362                    }
363                }
364            }
365        } else {
366            // big-endian
367            let mut rem = self.width;
368            let mut byte = low;
369            while rem > 0 {
370                if byte == low {
371                    // first byte
372                    ts.append_all(quote! {
373                        let v = pdu[#byte] as #utype;
374                    });
375                    if rem < 8 {
376                        // single byte
377                        let mask = rem - 1;
378                        let shift = left + 1 - rem;
379                        ts.append_all(quote! {
380                            let mask: #utype = (1 << #mask)
381                                | ((1 << #mask) - 1);
382                            let v = (v >> #shift) & mask;
383                        });
384                        rem = 0;
385                    } else {
386                        // first of multiple bytes
387                        let mask = left;
388                        let shift = rem - left - 1;
389                        if mask < 7 {
390                            ts.append_all(quote! {
391                                let mask: #utype = (1 << #mask)
392                                    | ((1 << #mask) - 1);
393                                let v = (v & mask) << #shift;
394                            });
395                        } else {
396                            ts.append_all(quote! {
397                                let v = v << #shift;
398                            });
399                        }
400                        rem -= left + 1;
401                    }
402                    byte += 1;
403                } else {
404                    if rem < 8 {
405                        // last byte: take top bits
406                        let shift = 8 - rem;
407                        ts.append_all(quote! {
408                            let v = v |
409                            ((pdu[#byte] as #utype) >> #shift);
410                        });
411                        rem = 0;
412                    } else {
413                        rem -= 8;
414                        ts.append_all(quote! {
415                            let v = v |
416                            ((pdu[#byte] as #utype) << #rem);
417                        });
418                        byte += 1;
419                    }
420                };
421            }
422        }
423        // perform sign-extension for values with fewer bits than
424        // the storage type
425        if self.signed && self.width < self.nwidth {
426            let mask = self.width - 1;
427            ts.append_all(quote! {
428                let mask: #utype = (1 << #mask);
429                let v = if (v & mask) != 0 {
430                    let mask = mask | (mask - 1);
431                    v | !mask
432                } else {
433                    v
434                };
435            });
436        }
437        ts.append_all(quote! { v });
438
439        quote! { { #ts } }
440    }
441
442    /// Generate a signal's decoder
443    fn gen_decoder(&self) -> TokenStream {
444        let name = &self.ident;
445        if self.width == 1 {
446            // boolean
447            let byte = self.start / 8;
448            let bit = self.start % 8;
449            quote! {
450                self.#name = (pdu[#byte] & (1 << #bit)) != 0;
451            }
452        } else {
453            let value = self.extract_bits();
454            let ntype = &self.ntype;
455            if !self.is_float() {
456                quote! {
457                    self.#name = #value as #ntype;
458                }
459            } else {
460                let scale = self.scale;
461                let offset = *self.signal.offset() as f32;
462                quote! {
463                    self.#name = ((#value as f32) * #scale) + #offset;
464                }
465            }
466        }
467    }
468
469    /// Generate code for encoding a signal value
470    fn gen_encoder(&self) -> TokenStream {
471        let name = &self.ident;
472        let low = self.start / 8;
473        let mut byte = low;
474        let bit = self.start % 8;
475        if self.width == 1 {
476            // boolean
477            quote! {
478                let mask: u8 = (1 << #bit);
479                if self.#name {
480                    pdu[#byte] |= mask;
481                } else {
482                    pdu[#byte] &= !mask;
483                }
484            }
485        } else {
486            let utype = &self.utype;
487            let left = self.start % 8;
488            // let right = (self.start + self.width) % 8;
489            let le = self.signal.byte_order() == &ByteOrder::LittleEndian;
490
491            let mut ts = TokenStream::new();
492            if self.is_float() {
493                let scale = self.scale;
494                let offset = self.signal.offset as f32;
495                ts.append_all(quote! {
496                    let v = ((self.#name - #offset) / #scale) as #utype;
497                });
498            } else {
499                ts.append_all(quote! {
500                    let v = self.#name;
501                });
502            }
503            if le {
504                if self.width == self.nwidth && left == 0 {
505                    // aligned little-endian
506                    let mut bits = self.nwidth;
507                    let mut shift = 0;
508                    while bits >= 8 {
509                        ts.append_all(quote! {
510                            pdu[#byte] = ((v >> #shift) as u8) & 0xff;
511                        });
512                        bits -= 8;
513                        byte += 1;
514                        shift += 8;
515                    }
516                } else {
517                    // unaligned little-endian
518                    let mut rem = self.width;
519                    let mut lshift = left;
520                    let mut rshift = 0;
521                    while rem > 0 {
522                        if rem < 8 {
523                            let mask: u8 = (1 << rem) - 1;
524                            let mask = mask << lshift;
525                            ts.append_all(quote! {
526                                pdu[#byte] = (pdu[#byte] & !#mask) |
527                                ((((v >> #rshift) << (#lshift)) as u8) & #mask);
528                            });
529                            break;
530                        }
531
532                        if lshift != 0 {
533                            let mask: u8 = (1 << (8 - left)) - 1;
534                            let mask = mask << lshift;
535                            ts.append_all(quote! {
536                                pdu[#byte] = (pdu[#byte] & !#mask) |
537                                ((((v >> #rshift) << (#lshift)) as u8) & #mask);
538                            });
539                        } else {
540                            ts.append_all(quote! {
541                                pdu[#byte] = ((v >> #rshift) & 0xff) as u8;
542                            });
543                        }
544
545                        if byte == low {
546                            rem -= 8 - left;
547                            rshift += 8 - left;
548                        } else {
549                            rem -= 8;
550                            rshift += 8;
551                        }
552                        byte += 1;
553                        lshift = 0;
554                    }
555                }
556            } else {
557                if self.width == self.nwidth && left == 7 {
558                    // aligned big-endian
559                    let mut bits = self.nwidth;
560                    let mut shift = bits - 8;
561                    let mut byte = (self.start - 7) / 8;
562                    while bits >= 8 {
563                        ts.append_all(quote! {
564                            pdu[#byte] = ((v >> #shift) as u8) & 0xff;
565                        });
566                        bits -= 8;
567                        byte += 1;
568                        if shift >= 8 {
569                            shift -= 8;
570                        }
571                    }
572                } else {
573                    // unaligned big-endian
574                    //                    todo!();
575                }
576            }
577            ts
578        }
579    }
580
581    /// We consider any signal with a scale to be a floating-point
582    /// value
583    fn is_float(&self) -> bool {
584        self.scale != 1.0
585    }
586}
587
588impl<'a> MessageInfo<'a> {
589    fn new(dbc: &DBC, field: &'a Field) -> Option<Self> {
590        let stype = match &field.ty {
591            Type::Path(v) => v,
592            Type::Array(a) => match *a.elem {
593                // TODO: validate that all signals match in ID range
594                Type::Path(ref v) => v,
595                _ => unimplemented!(),
596            },
597            _ => unimplemented!(),
598        };
599        let ident = &stype.path.segments[0].ident;
600        let name = ident.to_string();
601
602        for (index, message) in dbc.messages().iter().enumerate() {
603            if message.message_name() == &name {
604                let id = message.message_id();
605                let (id32, extended) = match *id {
606                    MessageId::Standard(id) => (id as u32, false),
607                    MessageId::Extended(id) => (id, true),
608                };
609                let mut cycle_time: Option<usize> = None;
610                for attr in dbc.attribute_values().iter() {
611                    let value = attr.attribute_value();
612                    use AttributeValuedForObjectType as AV;
613                    match value {
614                        AV::MessageDefinitionAttributeValue(aid, Some(av)) => {
615                            if aid == id
616                                && attr.attribute_name() == "GenMsgCycleTime"
617                            {
618                                cycle_time = Some(Self::attr_value(av));
619                            }
620                        }
621                        _ => {}
622                    }
623                }
624
625                return Some(Self {
626                    id: id32,
627                    extended,
628                    index,
629                    ident,
630                    cycle_time,
631                    attrs: &field.attrs,
632                });
633            }
634        }
635        None
636    }
637
638    // TODO: revisit this to handle type conversion better; we
639    // expect that the value fits in a usize for e.g. GenMsgCycleTime
640    fn attr_value(v: &can_dbc::AttributeValue) -> usize {
641        use can_dbc::AttributeValue as AV;
642        match v {
643            AV::AttributeValueU64(x) => *x as usize,
644            AV::AttributeValueI64(x) => *x as usize,
645            AV::AttributeValueF64(x) => *x as usize,
646            AV::AttributeValueCharString(_) => 0usize, // TODO: parse as int?
647        }
648    }
649}
650
651impl<'a> DeriveData<'a> {
652    fn from(input: &'a DeriveInput) -> Result<Self> {
653        // load the DBC file
654        let dbc_file = parse_attr(&input.attrs, "dbc_file")
655            .expect("No DBC file specified");
656        let contents = read(&dbc_file).expect("Could not read DBC");
657        let dbc = match DBC::from_slice(&contents) {
658            Ok(dbc) => dbc,
659            Err(can_dbc::Error::Incomplete(dbc, _)) => {
660                // TODO: emit an actual compiler warning
661                eprintln!(
662                    "Warning: DBC load incomplete; some data may be missing"
663                );
664                dbc
665            }
666            Err(_) => {
667                panic!("Unable to parse {dbc_file}");
668            }
669        };
670
671        // gather all of the messages and associated attributes
672        let mut messages: BTreeMap<String, MessageInfo<'_>> =
673            Default::default();
674        match &input.data {
675            Data::Struct(data) => match &data.fields {
676                Fields::Named(fields) => {
677                    for field in &fields.named {
678                        if let Some(info) = MessageInfo::new(&dbc, field) {
679                            messages.insert(info.ident.to_string(), info);
680                        } else {
681                            return Err(syn::Error::new(
682                                field.span(),
683                                "Unknown message",
684                            ));
685                        }
686                    }
687                }
688                Fields::Unnamed(_) | Fields::Unit => unimplemented!(),
689            },
690            _ => unimplemented!(),
691        }
692
693        Ok(Self {
694            name: &input.ident,
695            dbc,
696            messages,
697        })
698    }
699
700    fn build(self) -> TokenStream {
701        let mut out = TokenStream::new();
702
703        for (name, message) in self.messages.iter() {
704            let m = self
705                .dbc
706                .messages()
707                .get(message.index)
708                .unwrap_or_else(|| panic!("Unknown message {name}"));
709
710            let filter = SignalFilter::new(message);
711
712            let mut signals: Vec<Ident> = vec![];
713            let mut types: Vec<Ident> = vec![];
714            let mut docs: Vec<String> = vec![];
715            let mut infos: Vec<SignalInfo> = vec![];
716            let mut values = TokenStream::new();
717            for s in m.signals().iter() {
718                if !filter.use_signal(s.name()) {
719                    continue;
720                }
721
722                let signal = SignalInfo::new(s, message);
723                signals.push(signal.ident.clone());
724                types.push(signal.ntype.clone());
725
726                // documentation text
727                let endian_string =
728                    if s.byte_order() == &ByteOrder::LittleEndian {
729                        "little-endian"
730                    } else {
731                        "big-endian"
732                    };
733                let scale_string = if signal.is_float() {
734                    &format!(", scale factor {}", s.factor())
735                } else {
736                    ""
737                };
738                let mut doc = format!(
739                    "Wire format: {} bit{} starting at bit {}{} ({})\n",
740                    s.signal_size(),
741                    if s.signal_size() != &1 { "s" } else { "" },
742                    s.start_bit(),
743                    scale_string,
744                    endian_string,
745                );
746
747                // value-table constants
748                if let Some(descs) = self
749                    .dbc
750                    .value_descriptions_for_signal(*m.message_id(), s.name())
751                {
752                    for desc in descs.iter() {
753                        let santized: String =
754                            format!("{}_{}", s.name(), desc.b())
755                                .to_uppercase()
756                                .chars()
757                                .filter(|c| c.is_alphanumeric() || c == &'_')
758                                .collect();
759                        let c = Ident::new(&santized, signal.ident.span());
760                        let i = signal.const_ident(*desc.a());
761                        let v = quote! {#i};
762                        let t = signal.ntype.clone();
763                        values.extend(quote! {
764                            pub const #c: #t = #v;
765                        });
766                        doc += &format!("\n{c} = {v}\n");
767                    }
768                }
769
770                infos.push(signal);
771                docs.push(doc);
772            }
773
774            let id = message.id;
775            let extended = message.extended;
776
777            let dlc = *m.message_size() as usize;
778            let dlc8 = dlc as u8;
779            let ident = message.ident;
780
781            // build signal decoders and encoders
782            let mut decoders = TokenStream::new();
783            let mut encoders = TokenStream::new();
784            for info in infos.iter() {
785                decoders.append_all(info.gen_decoder());
786                encoders.append_all(info.gen_encoder());
787            }
788            let cycle_time = if let Some(c) = message.cycle_time {
789                quote! {
790                    pub const CYCLE_TIME: usize = #c;
791                }
792            } else {
793                quote! {}
794            };
795
796            let cycle_time_doc = if let Some(c) = message.cycle_time {
797                &format!(", cycle time {}ms", c)
798            } else {
799                ""
800            };
801            let doc = format!(
802                "{} ID {} (0x{:X}){}",
803                if extended { "Extended" } else { "Standard" },
804                id,
805                id,
806                cycle_time_doc,
807            );
808
809            out.append_all(quote! {
810                #[automatically_derived]
811                #[allow(non_snake_case)]
812                #[allow(non_camel_case_types)]
813                #[derive(Default)]
814                #[doc = #doc]
815                pub struct #ident {
816                    #(
817                        #[doc = #docs]
818                        pub #signals: #types
819                    ),*
820                }
821
822                impl #ident {
823                    pub const ID: u32 = #id;
824                    pub const DLC: u8 = #dlc8;
825                    pub const EXTENDED: bool = #extended;
826                    #cycle_time
827                    #values
828
829                    pub fn decode(&mut self, pdu: &[u8])
830                                  -> bool {
831                        if pdu.len() != #dlc {
832                            return false
833                        }
834                        #decoders
835                        true
836                    }
837
838                    pub fn encode(&mut self, pdu: &mut [u8])
839                                  -> bool {
840                        if pdu.len() != #dlc {
841                            return false
842                        }
843                        #encoders
844                        true
845                    }
846                }
847
848                impl TryFrom<&[u8]> for #ident {
849                    type Error = ();
850                    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
851                        let mut pdu = Self::default(); // TODO: elide
852                        if pdu.decode(data) {
853                            Ok(pdu)
854                        } else {
855                            Err(())
856                        }
857                    }
858                }
859            });
860        }
861        out
862    }
863}
864
865/// See the crate documentation for details.
866///
867/// The `#[dbc_file]` attribute specifies the name of the .dbc file
868/// to use, and is required.
869///
870/// Individual messages may specify a `#[dbc_signals]` attribute
871/// naming the individual signals of interest; otherwise, all
872/// signals within the message are generated.
873#[proc_macro_derive(DbcData, attributes(dbc_file, dbc_signals))]
874pub fn dbc_data_derive(
875    input: proc_macro::TokenStream,
876) -> proc_macro::TokenStream {
877    derive_data(&parse_macro_input!(input as DeriveInput))
878        .unwrap_or_else(|err| err.to_compile_error())
879        .into()
880}
881
882fn derive_data(input: &DeriveInput) -> Result<TokenStream> {
883    Ok(DeriveData::from(input)?.build())
884}
885
886fn parse_attr(attrs: &[Attribute], name: &str) -> Option<String> {
887    let attr = attrs
888        .iter()
889        .filter(|a| {
890            a.path().segments.len() == 1 && a.path().segments[0].ident == name
891        })
892        .nth(0)?;
893
894    let expr = match &attr.meta {
895        Meta::NameValue(n) => Some(&n.value),
896        _ => None,
897    };
898
899    match &expr {
900        Some(Expr::Lit(e)) => match &e.lit {
901            Lit::Str(s) => Some(s.value()),
902            _ => None,
903        },
904        _ => None,
905    }
906}