veecle_os_data_support_can/
id.rs

1/// A standard CAN id.
2#[derive(Clone, Copy, PartialEq, Eq)]
3pub struct StandardId(u16);
4
5/// An extended CAN id.
6#[derive(Clone, Copy, PartialEq, Eq)]
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 ExtendedId {
63    /// Creates an `ExtendedId`, returns `Some` if, and only if, `value < 0x2000_0000`.
64    pub const fn new(value: u32) -> Option<Self> {
65        if value < 0x2000_0000 {
66            Some(Self(value))
67        } else {
68            None
69        }
70    }
71
72    /// The equivalent of `ExtendedId::new(value).unwrap()`, but as a `const fn`, while `unwrap` is
73    /// not `const`-compatible.
74    pub const fn new_unwrap(value: u32) -> Self {
75        match Self::new(value) {
76            Some(value) => value,
77            None => panic!("out of range id"),
78        }
79    }
80
81    /// Returns the CAN Identifier as a 32-bit integer.
82    pub fn to_raw(self) -> u32 {
83        self.into()
84    }
85}
86
87impl TryFrom<u32> for ExtendedId {
88    type Error = ();
89
90    fn try_from(value: u32) -> Result<Self, Self::Error> {
91        Self::new(value).ok_or(())
92    }
93}
94
95impl From<ExtendedId> for u32 {
96    fn from(value: ExtendedId) -> Self {
97        value.0
98    }
99}
100
101impl core::fmt::Debug for ExtendedId {
102    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
103        write!(f, "{:#x}", self.0)?;
104        Ok(())
105    }
106}
107
108/// Either a standard or extended CAN id.
109#[derive(Clone, Copy, PartialEq, Eq, Debug)]
110pub enum Id {
111    /// A standard CAN id.
112    Standard(StandardId),
113
114    /// An extended CAN id.
115    Extended(ExtendedId),
116}
117
118impl From<StandardId> for Id {
119    fn from(standard: StandardId) -> Self {
120        Self::Standard(standard)
121    }
122}
123
124impl From<ExtendedId> for Id {
125    fn from(extended: ExtendedId) -> Self {
126        Self::Extended(extended)
127    }
128}
129
130/// All `Id` values are <0x2000_0000 so we have the top three bits spare, this type packs the discriminant into the top
131/// bit and removes alignment to minimize the storage space required.
132#[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize)]
133#[repr(Rust, packed)]
134pub struct PackedId(u32);
135
136impl From<Id> for PackedId {
137    fn from(id: Id) -> Self {
138        match id {
139            Id::Standard(StandardId(value)) => PackedId(u32::from(value)),
140            Id::Extended(ExtendedId(value)) => PackedId(value | 0x8000_0000),
141        }
142    }
143}
144
145impl From<PackedId> for Id {
146    fn from(id: PackedId) -> Self {
147        let PackedId(value) = id;
148        if value & 0x8000_0000 == 0x8000_0000 {
149            Id::Extended(ExtendedId(value & !0x8000_0000))
150        } else {
151            Id::Standard(StandardId(value as u16))
152        }
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use crate::id::PackedId;
159    use crate::{ExtendedId, Id, StandardId};
160
161    #[test]
162    fn pack_roundtrip() {
163        for id in [0, 1, 0x7FE, 0x7FF] {
164            let id = Id::Standard(StandardId::new(id).unwrap());
165            assert_eq!(id, Id::from(PackedId::from(id)));
166        }
167        for id in [0, 1, 0x1FFF_FFFE, 0x1FFF_FFFF] {
168            let id = Id::Extended(ExtendedId::new(id).unwrap());
169            assert_eq!(id, Id::from(PackedId::from(id)));
170        }
171    }
172
173    #[test]
174    fn id_to_integer() {
175        for id in [0, 1, 0x7FE, 0x7FF] {
176            let standard_id = StandardId::new(id).unwrap();
177            assert_eq!(standard_id.to_raw(), u16::from(standard_id));
178            assert_eq!(id, standard_id.to_raw());
179        }
180        for id in [0, 1, 0x1FFF_FFFE, 0x1FFF_FFFF] {
181            let extended_id = ExtendedId::new(id).unwrap();
182            assert_eq!(extended_id.to_raw(), u32::from(extended_id));
183            assert_eq!(id, extended_id.to_raw());
184        }
185    }
186}