dbc_rs/
lib.rs

1//! # dbc-rs
2//!
3//! A `no_std` compatible Rust library for parsing and working with DBC (CAN database) files.
4//!
5//! ## Features
6//!
7//! - **`no_std` compatible**: Works in embedded environments without the standard library
8//! - **Minimal dependencies**: Only `heapless` when using `heapless` feature (zero dependencies with `alloc`/`std`)
9//! - **Memory efficient**: Uses `Vec` (via `alloc`) for dynamic collections
10//! - **Type-safe**: Strong typing for all DBC elements
11//!
12//! ## Usage
13//!
14//! ```rust,no_run
15//! use dbc_rs::Dbc;
16//!
17//! let dbc_content = r#"VERSION "1.0"
18//!
19//! BU_: ECM TCM
20//!
21//! BO_ 256 EngineData : 8 ECM
22//!  SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm" TCM
23//! "#;
24//!
25//! let dbc = Dbc::parse(dbc_content)?;
26//! # Ok::<(), dbc_rs::Error>(())
27//! ```
28
29#![cfg_attr(not(feature = "std"), no_std)]
30#![deny(unused_must_use)]
31#![forbid(unsafe_code)]
32
33#[cfg(feature = "std")]
34extern crate std;
35
36#[cfg(all(feature = "alloc", not(feature = "heapless")))]
37extern crate alloc;
38
39mod byte_order;
40mod compat;
41mod dbc;
42mod error;
43mod message;
44mod nodes;
45mod parser;
46mod receivers;
47mod signal;
48#[cfg(feature = "std")]
49mod value_descriptions;
50mod version;
51
52pub use byte_order::ByteOrder;
53pub use dbc::Dbc;
54pub use error::{Error, Result};
55pub use message::{Message, Signals};
56pub use nodes::Nodes;
57pub use receivers::Receivers;
58pub use signal::Signal;
59#[cfg(feature = "std")]
60pub use value_descriptions::{ValueDescriptions, ValueDescriptionsBuilder};
61pub use version::Version;
62
63#[cfg(feature = "std")]
64pub use dbc::DbcBuilder;
65#[cfg(feature = "std")]
66pub use message::MessageBuilder;
67#[cfg(feature = "std")]
68pub use nodes::NodesBuilder;
69#[cfg(feature = "std")]
70pub use receivers::ReceiversBuilder;
71#[cfg(feature = "std")]
72pub use signal::SignalBuilder;
73#[cfg(feature = "std")]
74pub use version::VersionBuilder;
75
76pub(crate) use parser::Parser;
77
78/// Helper function to validate and convert a string to a name with MAX_NAME_SIZE limit.
79///
80/// This centralizes the common pattern of converting a string reference to
81/// `String<{ MAX_NAME_SIZE }>` with proper error handling.
82///
83/// # Errors
84///
85/// Returns `Error::Expected` with `MAX_NAME_SIZE_EXCEEDED` message if the name
86/// exceeds `MAX_NAME_SIZE` (32 characters by default, per DBC specification).
87#[inline]
88pub(crate) fn validate_name<S: AsRef<str>>(name: S) -> Result<compat::String<{ MAX_NAME_SIZE }>> {
89    let name_str: &str = name.as_ref();
90
91    // Explicitly check length before conversion to ensure MAX_NAME_SIZE enforcement
92    // This check works for both alloc and heapless features
93    if name_str.len() > MAX_NAME_SIZE {
94        return Err(Error::Expected(error::Error::MAX_NAME_SIZE_EXCEEDED));
95    }
96
97    // Convert to compat::String - this will also check the limit internally,
98    // but we've already checked above for clarity and early error reporting
99    compat::String::try_from(name_str)
100        .map_err(|_| Error::Expected(error::Error::MAX_NAME_SIZE_EXCEEDED))
101}
102
103/// Helper function to check if a length exceeds a maximum limit.
104///
105/// This centralizes the common pattern of checking collection lengths against MAX limits.
106/// Returns `Some(error)` if the limit is exceeded, `None` otherwise.
107///
108/// This is an internal helper function and not part of the public API.
109#[inline]
110pub(crate) fn check_max_limit<E>(len: usize, max: usize, error: E) -> Option<E> {
111    if len > max { Some(error) } else { None }
112}
113
114/// The version of this crate as specified in `Cargo.toml`.
115///
116/// This constant is only available when the `std` feature is enabled.
117#[cfg(feature = "std")]
118pub const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
119
120// Maximum limits for two-pass parsing (no alloc)
121// Note: All MAX_* constants are now defined in limits.rs (generated by build.rs)
122// and can be overridden at build time via environment variables:
123// - DBC_MAX_MESSAGES (default: 8192, must be power of 2 for heapless)
124// - DBC_MAX_SIGNALS_PER_MESSAGE (default: 64)
125// - DBC_MAX_NODES (default: 256)
126// - DBC_MAX_VALUE_DESCRIPTIONS (default: 64)
127// - DBC_MAX_RECEIVER_NODES (default: 64)
128// - DBC_MAX_NAME_SIZE (default: 32, per DBC specification)
129include!(concat!(env!("OUT_DIR"), "/limits.rs"));
130
131// DBC file format keywords
132pub(crate) const VERSION: &str = "VERSION";
133pub(crate) const CM_: &str = "CM_";
134pub(crate) const NS_: &str = "NS_";
135pub(crate) const BS_: &str = "BS_";
136pub(crate) const BU_: &str = "BU_";
137pub(crate) const BO_: &str = "BO_";
138pub(crate) const SG_: &str = "SG_";
139pub(crate) const VAL_TABLE_: &str = "VAL_TABLE_";
140pub(crate) const BA_DEF_: &str = "BA_DEF_";
141pub(crate) const BA_DEF_DEF_: &str = "BA_DEF_DEF_";
142pub(crate) const BA_: &str = "BA_";
143pub(crate) const VAL_: &str = "VAL_";
144pub(crate) const SIG_GROUP_: &str = "SIG_GROUP_";
145pub(crate) const SIG_VALTYPE_: &str = "SIG_VALTYPE_";
146pub(crate) const EV_: &str = "EV_";
147pub(crate) const BO_TX_BU_: &str = "BO_TX_BU_";
148
149// Additional DBC keywords
150#[allow(clippy::upper_case_acronyms)]
151pub(crate) const VECTOR__INDEPENDENT_SIG_MSG: &str = "VECTOR__INDEPENDENT_SIG_MSG";
152#[allow(clippy::upper_case_acronyms)]
153pub(crate) const VECTOR__XXX: &str = "VECTOR__XXX";
154pub(crate) const BA_DEF_DEF_REL_: &str = "BA_DEF_DEF_REL_";
155pub(crate) const BA_DEF_SGTYPE_: &str = "BA_DEF_SGTYPE_";
156pub(crate) const SIGTYPE_VALTYPE_: &str = "SIGTYPE_VALTYPE_";
157pub(crate) const ENVVAR_DATA_: &str = "ENVVAR_DATA_";
158pub(crate) const SIG_TYPE_REF_: &str = "SIG_TYPE_REF_";
159pub(crate) const NS_DESC_: &str = "NS_DESC_";
160pub(crate) const BA_DEF_REL_: &str = "BA_DEF_REL_";
161pub(crate) const BA_SGTYPE_: &str = "BA_SGTYPE_";
162pub(crate) const SGTYPE_VAL_: &str = "SGTYPE_VAL_";
163pub(crate) const BU_SG_REL_: &str = "BU_SG_REL_";
164pub(crate) const BU_EV_REL_: &str = "BU_EV_REL_";
165pub(crate) const BU_BO_REL_: &str = "BU_BO_REL_";
166pub(crate) const SG_MUL_VAL_: &str = "SG_MUL_VAL_";
167pub(crate) const BA_REL_: &str = "BA_REL_";
168pub(crate) const CAT_DEF_: &str = "CAT_DEF_";
169pub(crate) const EV_DATA_: &str = "EV_DATA_";
170pub(crate) const CAT_: &str = "CAT_";
171pub(crate) const FILTER: &str = "FILTER";
172
173#[cfg_attr(not(feature = "std"), allow(dead_code))]
174const DBC_KEYWORDS: &[&str] = &[
175    VECTOR__INDEPENDENT_SIG_MSG,
176    VECTOR__XXX,
177    BA_DEF_DEF_REL_,
178    BA_DEF_SGTYPE_,
179    SIGTYPE_VALTYPE_,
180    ENVVAR_DATA_,
181    SIG_TYPE_REF_,
182    NS_DESC_,
183    BA_DEF_REL_,
184    BA_SGTYPE_,
185    SGTYPE_VAL_,
186    VAL_TABLE_,
187    SIG_GROUP_,
188    SIG_VALTYPE_,
189    BO_TX_BU_,
190    BU_SG_REL_,
191    BU_EV_REL_,
192    BU_BO_REL_,
193    SG_MUL_VAL_,
194    BA_DEF_DEF_,
195    BA_DEF_,
196    BA_REL_,
197    CAT_DEF_,
198    EV_DATA_,
199    BA_,
200    VAL_,
201    CM_,
202    CAT_,
203    NS_,
204    BS_,
205    BU_,
206    BO_,
207    SG_,
208    EV_,
209    VERSION,
210    FILTER,
211];