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}