Skip to main content

can_dbc/ast/
message_id.rs

1use can_dbc_pest::{Pair, Rule};
2
3use crate::parser::parse_uint;
4use crate::DbcError;
5
6/// CAN id in header of CAN frame.
7/// Must be unique in DBC file.
8#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub enum MessageId {
11    Standard(u16),
12    /// 29 bit extended identifier without the extended bit.
13    /// For the raw value of the message id including the bit for extended identifiers use the `raw()` method.
14    Extended(u32),
15}
16
17impl MessageId {
18    /// Raw value of the message id including the bit for extended identifiers
19    #[must_use]
20    pub fn raw(self) -> u32 {
21        match self {
22            Self::Standard(id) => u32::from(id),
23            Self::Extended(id) => id | 1 << 31,
24        }
25    }
26}
27
28impl TryFrom<u64> for MessageId {
29    type Error = DbcError;
30
31    fn try_from(value: u64) -> Result<Self, Self::Error> {
32        u32::try_from(value)
33            .map_err(|_| DbcError::MessageIdOutOfRange(value))?
34            .try_into()
35    }
36}
37
38impl TryFrom<u32> for MessageId {
39    type Error = DbcError;
40
41    /// Create `MessageId` from u32 value including the extended bit flag
42    ///
43    /// If bit 31 is set, creates an Extended `MessageId` with bits 0-28.
44    /// Otherwise, creates a Standard `MessageId`, erroring if the value is out of range for u16.
45    fn try_from(value: u32) -> Result<Self, Self::Error> {
46        const EXTENDED_ID_FLAG: u32 = 1 << 31;
47        Ok(if value & EXTENDED_ID_FLAG != 0 {
48            Self::Extended(value & 0x1FFF_FFFF)
49        } else {
50            // FIXME: this code seems more correct, but breaks existing tests
51            // let v = u16::try_from(value)
52            //     .map_err(|_| DbcError::MessageIdOutOfRange(u64::from(value)))?;
53            #[allow(clippy::cast_possible_truncation)]
54            let v = value as u16;
55
56            Self::Standard(v)
57        })
58    }
59}
60
61impl TryFrom<Pair<'_, Rule>> for MessageId {
62    type Error = DbcError;
63
64    fn try_from(value: Pair<'_, Rule>) -> Result<Self, Self::Error> {
65        Self::try_from(parse_uint(&value)?)
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use can_dbc_pest::Rule;
72
73    use crate::test_helpers::*;
74    use crate::MessageId;
75
76    #[test]
77    #[expect(clippy::unreadable_literal)]
78    fn extended_message_id_raw() {
79        let id = MessageId::Extended(2);
80        assert_eq!(id.raw(), 2 | 1 << 31);
81
82        // test with all 29 bits set
83        let id = MessageId::Extended(0x1FFF_FFFF);
84        assert_eq!(id.raw(), 0b10011111_11111111_11111111_11111111);
85    }
86
87    #[test]
88    fn standard_message_id_raw() {
89        let id = MessageId::Standard(2);
90        assert_eq!(id.raw(), 2);
91    }
92
93    #[test]
94    fn try_from_u32_standard() {
95        let id = MessageId::try_from(500u32).unwrap();
96        assert_eq!(id, MessageId::Standard(500));
97    }
98
99    #[test]
100    fn try_from_u32_extended() {
101        let id = MessageId::try_from(2u32 | (1 << 31)).unwrap();
102        assert_eq!(id, MessageId::Extended(2));
103    }
104
105    #[test]
106    fn try_from_u64_standard() {
107        let id = MessageId::try_from(500u64).unwrap();
108        assert_eq!(id, MessageId::Standard(500));
109    }
110
111    #[test]
112    fn try_from_u64_extended() {
113        let id = MessageId::try_from(2u64 | (1 << 31)).unwrap();
114        assert_eq!(id, MessageId::Extended(2));
115    }
116
117    #[test]
118    fn standard_message_id_test() {
119        let val = test_into::<MessageId>("2", Rule::message_id);
120        assert_eq!(val, MessageId::Standard(2));
121    }
122
123    #[test]
124    fn extended_low_message_id_test() {
125        let s = (2u32 | 1 << 31).to_string();
126        let val = test_into::<MessageId>(&s, Rule::message_id);
127        assert_eq!(val, MessageId::Extended(2));
128    }
129
130    #[test]
131    fn extended_message_id_test() {
132        let s = (0x1FFF_FFFF_u32 | 1 << 31).to_string();
133        let val = test_into::<MessageId>(&s, Rule::message_id);
134        assert_eq!(val, MessageId::Extended(0x1FFF_FFFF));
135    }
136
137    #[test]
138    fn extended_message_id_test_max_29bit() {
139        let s = u32::MAX.to_string();
140        let val = test_into::<MessageId>(&s, Rule::message_id);
141        assert_eq!(val, MessageId::Extended(0x1FFF_FFFF));
142    }
143}