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)]
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    fn extended_message_id_raw() {
78        let id = MessageId::Extended(2);
79        assert_eq!(id.raw(), 2 | 1 << 31);
80        let id = MessageId::Extended(2 ^ 29);
81        assert_eq!(id.raw(), 2 ^ 29 | 1 << 31);
82    }
83
84    #[test]
85    fn standard_message_id_raw() {
86        let id = MessageId::Standard(2);
87        assert_eq!(id.raw(), 2);
88    }
89
90    #[test]
91    fn try_from_u32_standard() {
92        let id = MessageId::try_from(500u32).unwrap();
93        assert_eq!(id, MessageId::Standard(500));
94    }
95
96    #[test]
97    fn try_from_u32_extended() {
98        let id = MessageId::try_from(2u32 | (1 << 31)).unwrap();
99        assert_eq!(id, MessageId::Extended(2));
100    }
101
102    #[test]
103    fn try_from_u64_standard() {
104        let id = MessageId::try_from(500u64).unwrap();
105        assert_eq!(id, MessageId::Standard(500));
106    }
107
108    #[test]
109    fn try_from_u64_extended() {
110        let id = MessageId::try_from(2u64 | (1 << 31)).unwrap();
111        assert_eq!(id, MessageId::Extended(2));
112    }
113
114    #[test]
115    fn standard_message_id_test() {
116        let val = test_into::<MessageId>("2", Rule::message_id);
117        assert_eq!(val, MessageId::Standard(2));
118    }
119
120    #[test]
121    fn extended_low_message_id_test() {
122        let s = (2u32 | 1 << 31).to_string();
123        let val = test_into::<MessageId>(&s, Rule::message_id);
124        assert_eq!(val, MessageId::Extended(2));
125    }
126
127    #[test]
128    fn extended_message_id_test() {
129        let s = (0x1FFF_FFFF_u32 | 1 << 31).to_string();
130        let val = test_into::<MessageId>(&s, Rule::message_id);
131        assert_eq!(val, MessageId::Extended(0x1FFF_FFFF));
132    }
133
134    #[test]
135    fn extended_message_id_test_max_29bit() {
136        let s = u32::MAX.to_string();
137        let val = test_into::<MessageId>(&s, Rule::message_id);
138        assert_eq!(val, MessageId::Extended(0x1FFF_FFFF));
139    }
140}