veecle_os_data_support_can/
id.rs

1/// A standard CAN id.
2#[derive(Clone, Copy, PartialEq, Eq, serde::Serialize)]
3pub struct StandardId(u16);
4
5/// An extended CAN id.
6#[derive(Clone, Copy, PartialEq, Eq, serde::Serialize)]
7pub struct ExtendedId(u32);
8
9impl StandardId {
10    /// Creates a `StandardId`, returns `Some` if, and only if, `value < 0x800`.
11    pub const fn new(value: u16) -> Option<Self> {
12        if value < 0x800 {
13            Some(Self(value))
14        } else {
15            None
16        }
17    }
18
19    /// The equivalent of `StandardId::new(value).unwrap()`, but as a `const fn`, while `unwrap` is
20    /// not `const`-compatible.
21    pub const fn new_unwrap(value: u16) -> Self {
22        match Self::new(value) {
23            Some(value) => value,
24            None => panic!("out of range id"),
25        }
26    }
27
28    /// Returns the CAN Identifier as a 16-bit integer.
29    pub fn to_raw(self) -> u16 {
30        self.into()
31    }
32}
33
34impl TryFrom<u16> for StandardId {
35    type Error = ();
36
37    fn try_from(value: u16) -> Result<Self, Self::Error> {
38        Self::new(value).ok_or(())
39    }
40}
41
42impl TryFrom<u32> for StandardId {
43    type Error = ();
44
45    fn try_from(value: u32) -> Result<Self, Self::Error> {
46        u16::try_from(value).ok().and_then(Self::new).ok_or(())
47    }
48}
49
50impl From<StandardId> for u16 {
51    fn from(value: StandardId) -> Self {
52        value.0
53    }
54}
55
56impl core::fmt::Debug for StandardId {
57    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
58        write!(f, "{:#x}", self.0)
59    }
60}
61
62impl<'de> serde::Deserialize<'de> for StandardId {
63    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
64    where
65        D: serde::Deserializer<'de>,
66    {
67        let value = u16::deserialize(deserializer)?;
68        StandardId::new(value)
69            .ok_or_else(|| serde::de::Error::custom("standard CAN id must be < 0x800"))
70    }
71}
72
73impl ExtendedId {
74    /// Creates an `ExtendedId`, returns `Some` if, and only if, `value < 0x2000_0000`.
75    pub const fn new(value: u32) -> Option<Self> {
76        if value < 0x2000_0000 {
77            Some(Self(value))
78        } else {
79            None
80        }
81    }
82
83    /// The equivalent of `ExtendedId::new(value).unwrap()`, but as a `const fn`, while `unwrap` is
84    /// not `const`-compatible.
85    pub const fn new_unwrap(value: u32) -> Self {
86        match Self::new(value) {
87            Some(value) => value,
88            None => panic!("out of range id"),
89        }
90    }
91
92    /// Returns the CAN Identifier as a 32-bit integer.
93    pub fn to_raw(self) -> u32 {
94        self.into()
95    }
96}
97
98impl TryFrom<u32> for ExtendedId {
99    type Error = ();
100
101    fn try_from(value: u32) -> Result<Self, Self::Error> {
102        Self::new(value).ok_or(())
103    }
104}
105
106impl From<ExtendedId> for u32 {
107    fn from(value: ExtendedId) -> Self {
108        value.0
109    }
110}
111
112impl core::fmt::Debug for ExtendedId {
113    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
114        write!(f, "{:#x}", self.0)?;
115        Ok(())
116    }
117}
118
119impl<'de> serde::Deserialize<'de> for ExtendedId {
120    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
121    where
122        D: serde::Deserializer<'de>,
123    {
124        let value = u32::deserialize(deserializer)?;
125        ExtendedId::new(value)
126            .ok_or_else(|| serde::de::Error::custom("extended CAN id must be < 0x2000_0000"))
127    }
128}
129
130/// Either a standard or extended CAN id.
131#[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
132pub enum Id {
133    /// A standard CAN id.
134    Standard(StandardId),
135
136    /// An extended CAN id.
137    Extended(ExtendedId),
138}
139
140impl From<StandardId> for Id {
141    fn from(standard: StandardId) -> Self {
142        Self::Standard(standard)
143    }
144}
145
146impl From<ExtendedId> for Id {
147    fn from(extended: ExtendedId) -> Self {
148        Self::Extended(extended)
149    }
150}
151
152/// All `Id` values are <0x2000_0000 so we have the top three bits spare, this type packs the discriminant into the top
153/// bit and removes alignment to minimize the storage space required.
154#[derive(Clone, Copy, PartialEq, Eq, Debug)]
155#[repr(Rust, packed)]
156pub struct PackedId(u32);
157
158impl From<Id> for PackedId {
159    fn from(id: Id) -> Self {
160        match id {
161            Id::Standard(StandardId(value)) => PackedId(u32::from(value)),
162            Id::Extended(ExtendedId(value)) => PackedId(value | 0x8000_0000),
163        }
164    }
165}
166
167impl From<PackedId> for Id {
168    fn from(id: PackedId) -> Self {
169        let PackedId(value) = id;
170        if value & 0x8000_0000 == 0x8000_0000 {
171            Id::Extended(ExtendedId(value & !0x8000_0000))
172        } else {
173            Id::Standard(StandardId(value as u16))
174        }
175    }
176}
177
178// Manual serde implementations to serialize/deserialize via the `Id` enum instead of the packed representation.
179// This ensures the serialized format uses the logical representation and deserialization maintains invariants.
180impl serde::Serialize for PackedId {
181    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
182    where
183        S: serde::Serializer,
184    {
185        Id::from(*self).serialize(serializer)
186    }
187}
188
189impl<'de> serde::Deserialize<'de> for PackedId {
190    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
191    where
192        D: serde::Deserializer<'de>,
193    {
194        let id = Id::deserialize(deserializer)?;
195        Ok(PackedId::from(id))
196    }
197}
198
199#[cfg(test)]
200#[cfg_attr(coverage_nightly, coverage(off))]
201mod tests {
202    use std::format;
203    use std::string::ToString;
204
205    use crate::id::PackedId;
206    use crate::{ExtendedId, Id, StandardId};
207
208    const STANDARD_ID_VALIDS: [u16; 4] = [0, 1, 0x7FE, 0x7FF];
209    const STANDARD_ID_INVALIDS: [u16; 2] = [0x800, 0xFFFF];
210    const EXTENDED_ID_VALIDS: [u32; 4] = [0, 1, 0x1FFF_FFFE, 0x1FFF_FFFF];
211    const EXTENDED_ID_INVALIDS: [u32; 2] = [0x2000_0000, 0xFFFF_FFFF];
212
213    #[test]
214    fn pack_roundtrip() {
215        for id in STANDARD_ID_VALIDS {
216            let id = Id::Standard(StandardId::new(id).unwrap());
217            assert_eq!(id, Id::from(PackedId::from(id)));
218        }
219        for id in EXTENDED_ID_VALIDS {
220            let id = Id::Extended(ExtendedId::new(id).unwrap());
221            assert_eq!(id, Id::from(PackedId::from(id)));
222        }
223    }
224
225    #[test]
226    fn id_to_integer() {
227        for value in STANDARD_ID_VALIDS {
228            let standard_id = StandardId::new(value).unwrap();
229            assert_eq!(standard_id.to_raw(), u16::from(standard_id));
230            assert_eq!(value, standard_id.to_raw());
231        }
232        for value in EXTENDED_ID_VALIDS {
233            let extended_id = ExtendedId::new(value).unwrap();
234            assert_eq!(extended_id.to_raw(), u32::from(extended_id));
235            assert_eq!(value, extended_id.to_raw());
236        }
237    }
238
239    #[test]
240    fn test_deserialize_standard_id_valid() {
241        for value in STANDARD_ID_VALIDS {
242            let json = value.to_string();
243            let id: StandardId = serde_json::from_str(&json).unwrap();
244            assert_eq!(id, StandardId::new(value).unwrap());
245            assert_eq!(json, serde_json::to_string(&id).unwrap());
246        }
247    }
248
249    #[test]
250    fn test_deserialize_standard_id_invalid() {
251        for value in STANDARD_ID_INVALIDS {
252            let json = value.to_string();
253            assert!(serde_json::from_str::<StandardId>(&json).is_err());
254        }
255    }
256
257    #[test]
258    fn test_deserialize_extended_id_valid() {
259        for value in EXTENDED_ID_VALIDS {
260            let json = value.to_string();
261            let id: ExtendedId = serde_json::from_str(&json).unwrap();
262            assert_eq!(id, ExtendedId::new(value).unwrap());
263            assert_eq!(json, serde_json::to_string(&id).unwrap());
264        }
265    }
266
267    #[test]
268    fn test_deserialize_extended_id_invalid() {
269        for value in EXTENDED_ID_INVALIDS {
270            let json = value.to_string();
271            assert!(serde_json::from_str::<ExtendedId>(&json).is_err());
272        }
273    }
274
275    #[test]
276    fn test_deserialize_id_and_packed_id_valid() {
277        for value in STANDARD_ID_VALIDS {
278            let json = format!(r#"{{"Standard":{value}}}"#);
279            let id: Id = serde_json::from_str(&json).unwrap();
280            assert_eq!(id, Id::Standard(StandardId::new(value).unwrap()));
281            assert_eq!(json, serde_json::to_string(&id).unwrap());
282            let packed: PackedId = serde_json::from_str(&json).unwrap();
283            assert_eq!(packed, PackedId::from(id));
284            assert_eq!(json, serde_json::to_string(&packed).unwrap());
285        }
286        for value in EXTENDED_ID_VALIDS {
287            let json = format!(r#"{{"Extended":{value}}}"#);
288            let id: Id = serde_json::from_str(&json).unwrap();
289            assert_eq!(id, Id::Extended(ExtendedId::new(value).unwrap()));
290            assert_eq!(json, serde_json::to_string(&id).unwrap());
291            let packed: PackedId = serde_json::from_str(&json).unwrap();
292            assert_eq!(packed, PackedId::from(id));
293            assert_eq!(json, serde_json::to_string(&packed).unwrap());
294        }
295    }
296
297    #[test]
298    fn test_deserialize_id_and_packed_id_invalid() {
299        for value in STANDARD_ID_INVALIDS {
300            let json = format!(r#"{{"Standard":{value}}}"#);
301            assert!(serde_json::from_str::<Id>(&json).is_err());
302            assert!(serde_json::from_str::<PackedId>(&json).is_err());
303        }
304        for value in EXTENDED_ID_INVALIDS {
305            let json = format!(r#"{{"Extended":{value}}}"#);
306            assert!(serde_json::from_str::<Id>(&json).is_err());
307            assert!(serde_json::from_str::<PackedId>(&json).is_err());
308        }
309    }
310}