Add option to generate typed constants from DBC BA_ attributes#175
Add option to generate typed constants from DBC BA_ attributes#175felixvanoost wants to merge 8 commits into
Conversation
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
are there any existing examples of this in https://github.com/cantools/cantools in their tests/files? |
|
@nyurik This PR allows users to extract any arbitrary
|
|
@nyurik I've added some additional test cases using the |
There was a problem hiding this comment.
Pull request overview
Adds a new codegen configuration option to emit typed associated constants on generated message types, where the values are sourced from DBC BA_ attributes (with optional fallback to BA_DEF_DEF_ defaults), derived signal layout fields, and/or literals. This enables exposing per-message/per-signal metadata (e.g., AUTOSAR E2E / SecOC parameters) in a structured way in the generated Rust API.
Changes:
- Introduce
Config::attribute_structsplus supporting public spec types (AttributeStruct,AttributeScope,AttributeField,FieldSource) and generation logic insrc/lib.rs. - Add an end-to-end example and README documentation demonstrating how users map struct fields to DBC attributes/layout/literals.
- Add extensive tests covering success cases (scopes, defaults, layouts) and error cases (invalid sources, collisions, missing values).
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| tests/attribute_structs.rs | New tests validating attribute-driven constant generation and error handling. |
| src/lib.rs | Implements attribute-struct specs, validation, attribute lookup/defaulting, and code emission into message impl blocks. |
| README.md | Documents the new attribute_structs feature and provides usage/example output. |
| examples/attribute_structs.rs | Adds a runnable example showing signal/message-scoped attribute structs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -0,0 +1,82 @@ | |||
| //! Demonstrates [`Config::attribute_structs`]. | |||
| //! Emits constants for AUTOSAR E2E / SecOC-style metdata from DBC 'BA_' attributes. | |||
| ensure!( | ||
| !spec.fields.is_empty(), | ||
| "attribute_structs: '{}' declares no fields", | ||
| spec.const_name | ||
| ); | ||
| if matches!(spec.scope, AttributeScope::Message) { | ||
| for field in spec.fields { | ||
| ensure!( | ||
| !matches!( | ||
| field.source, | ||
| FieldSource::StartBit | FieldSource::StartByte | FieldSource::BitWidth | ||
| ), | ||
| "attribute_structs: field '{}' of '{}' uses a signal-only source \ | ||
| (StartBit/StartByte/BitWidth) but the struct has message scope", | ||
| field.name, | ||
| spec.const_name | ||
| ); | ||
| } | ||
| } |
| // Track every constant name already emitted for this message type so that | ||
| // duplicates are rejected here instead of producing code that fails to | ||
| // compile. | ||
| let mut used: BTreeSet<String> = BTreeSet::new(); | ||
| used.insert("MESSAGE_ID".to_string()); | ||
| for signal in &msg.signals { | ||
| if ValType::from_signal(signal) != ValType::Bool { | ||
| let sig = signal.field_name().to_uppercase(); | ||
| used.insert(format!("{sig}_MIN")); | ||
| used.insert(format!("{sig}_MAX")); | ||
| } | ||
| } |
| fn signal_start_byte(signal: &Signal) -> u64 { | ||
| let start_bit = match signal.byte_order { | ||
| LittleEndian | BigEndian => signal.start_bit, | ||
| }; | ||
| start_bit.checked_div(8).unwrap_or(0) | ||
| } |
|
@felixvanoost I ran some more AI on this, with some additional thoughts of mine. I asked AI also to consider if any of this functionality should be moved up to can-dbc crate, and it did find a few ones. VerdictThe direction is sound and worth merging, with a few things to fix first. The feature is genuinely useful, opt-in (zero effect when unused), avoids hard-coding OEM attribute names into the generator, and the tests are thorough. But there are two API-evolvability problems, one real compile-breaking edge case, and three helper functions that belong in can-dbc. Why it's neededAutomotive DBCs (especially AUTOSAR-flavored ones) carry per-message/per-signal metadata in Could a user do this themselves with can-dbc in their own build.rs? Mostly yes — What should move to can-dbcThe three lookup helpers added at lib.rs:1544-1570 are pure DBC queries with zero codegen logic:
can-dbc already has exactly this pattern of methods on
I'd add these as A real bug: ENUM attributes can generate non-compiling codeVerified against the cantools fixture the PR itself uses: Internal duplication and smells
API-evolvability concerns worth settling before merge
Suggested pathBefore merging the code: (1) land |
|
@nyurik I've opened a PR to move the helper functions into |
Adds methods to lookup DBC attributes: `message_attribute`, `signal_attribute`, `attribute_default`, `resolved_message_attribute` and `resolved_signal_attribute`. These will be used in oxibus/dbc-codegen#175 and oxibus/dbc-codegen#176. --------- Co-authored-by: Yuri Astrakhan <YuriAstrakhan@gmail.com>
Adds an option to generate typed constants from DBC
BA_attributes and place them within the existing generated message types. This is useful for DBCs that define per-message or per-signal metadata, like AUTOSAR E2E protection or SecOC parameters.Users define a struct that their crate owns and specify where each field should come from by adding one or more
AttributeStructentries to the generatorConfig. I have created an example to show the syntax in the README.