dbc_rs/signal/
core.rs

1use super::Signal;
2use crate::{ByteOrder, Error, Receivers, Result};
3
4#[cfg(feature = "std")]
5use crate::MAX_NAME_SIZE;
6#[cfg(feature = "std")]
7use crate::compat::String;
8
9impl Signal {
10    pub(crate) fn validate(name: &str, length: u16, min: f64, max: f64) -> Result<()> {
11        if name.trim().is_empty() {
12            return Err(Error::Validation(Error::SIGNAL_NAME_EMPTY));
13        }
14
15        // Validate length: must be between 1 and 512 bits
16        // - Classic CAN (2.0A/2.0B): DLC up to 8 bytes (64 bits)
17        // - CAN FD: DLC up to 64 bytes (512 bits)
18        // Signal length is validated against message DLC in Message::validate
19        // Note: name is parsed before this validation, so we can include it in error messages
20        if length == 0 {
21            return Err(Error::Validation(Error::SIGNAL_LENGTH_TOO_SMALL));
22        }
23        if length > 512 {
24            return Err(Error::Validation(Error::SIGNAL_LENGTH_TOO_LARGE));
25        }
26
27        // Note: start_bit validation (boundary checks and overlap detection) is done in
28        // Message::validate, not here, because:
29        // 1. The actual message size depends on DLC (1-64 bytes for CAN FD)
30        // 2. Overlap detection requires comparing multiple signals
31        // 3. This allows signals to be created independently and validated when added to a message
32
33        // Validate min <= max
34        if min > max {
35            return Err(Error::Validation(Error::INVALID_RANGE));
36        }
37
38        Ok(())
39    }
40
41    #[cfg(feature = "std")]
42    #[allow(clippy::too_many_arguments)] // Internal method, builder pattern is the public API
43    pub(crate) fn new(
44        name: String<{ MAX_NAME_SIZE }>,
45        start_bit: u16,
46        length: u16,
47        byte_order: ByteOrder,
48        unsigned: bool,
49        factor: f64,
50        offset: f64,
51        min: f64,
52        max: f64,
53        unit: Option<String<{ MAX_NAME_SIZE }>>,
54        receivers: Receivers,
55    ) -> Self {
56        // Validation should have been done prior (by builder or parse)
57        Self {
58            name,
59            start_bit,
60            length,
61            byte_order,
62            unsigned,
63            factor,
64            offset,
65            min,
66            max,
67            unit,
68            receivers,
69        }
70    }
71
72    #[inline]
73    #[must_use = "return value should be checked"]
74    pub fn name(&self) -> &str {
75        self.name.as_ref()
76    }
77
78    #[inline]
79    #[must_use]
80    pub fn start_bit(&self) -> u16 {
81        self.start_bit
82    }
83
84    #[inline]
85    #[must_use]
86    pub fn length(&self) -> u16 {
87        self.length
88    }
89
90    #[inline]
91    #[must_use]
92    pub fn byte_order(&self) -> ByteOrder {
93        self.byte_order
94    }
95
96    #[inline]
97    #[must_use]
98    pub fn is_unsigned(&self) -> bool {
99        self.unsigned
100    }
101
102    #[inline]
103    #[must_use]
104    pub fn factor(&self) -> f64 {
105        self.factor
106    }
107
108    #[inline]
109    #[must_use]
110    pub fn offset(&self) -> f64 {
111        self.offset
112    }
113
114    #[inline]
115    #[must_use]
116    pub fn min(&self) -> f64 {
117        self.min
118    }
119
120    #[inline]
121    #[must_use]
122    pub fn max(&self) -> f64 {
123        self.max
124    }
125
126    #[inline]
127    #[must_use]
128    pub fn unit(&self) -> Option<&str> {
129        self.unit.as_ref().map(|u| u.as_ref())
130    }
131
132    #[inline]
133    #[must_use]
134    pub fn receivers(&self) -> &Receivers {
135        &self.receivers
136    }
137}
138
139// Custom Eq implementation that handles f64 (treats NaN as equal to NaN)
140impl Eq for Signal {}
141
142// Custom Hash implementation that handles f64 (treats NaN consistently)
143impl core::hash::Hash for Signal {
144    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
145        self.name.hash(state);
146        self.start_bit.hash(state);
147        self.length.hash(state);
148        self.byte_order.hash(state);
149        self.unsigned.hash(state);
150        // Handle f64: convert to bits for hashing (NaN will have consistent representation)
151        self.factor.to_bits().hash(state);
152        self.offset.to_bits().hash(state);
153        self.min.to_bits().hash(state);
154        self.max.to_bits().hash(state);
155        self.unit.hash(state);
156        self.receivers.hash(state);
157    }
158}