Skip to main content

simple_someip/protocol/
message_type.rs

1use super::Error;
2
3/// Bit flag in `message_type` field indicating that the message is a SOME/IP TP message.
4pub const MESSAGE_TYPE_TP_FLAG: u8 = 0x20;
5
6///Message types of a SOME/IP message.
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8pub enum MessageType {
9    /// A request expecting a response.
10    Request,
11    /// A fire-and-forget request.
12    RequestNoReturn,
13    /// An event notification.
14    Notification,
15    /// A response to a request.
16    Response,
17    /// An error response.
18    Error,
19}
20
21impl MessageType {
22    const fn try_from(value: u8) -> Result<Self, Error> {
23        match value & !MESSAGE_TYPE_TP_FLAG {
24            0x00 => Ok(MessageType::Request),
25            0x01 => Ok(MessageType::RequestNoReturn),
26            0x02 => Ok(MessageType::Notification),
27            0x80 => Ok(MessageType::Response),
28            0x81 => Ok(MessageType::Error),
29            _ => Err(Error::InvalidMessageTypeField(value)),
30        }
31    }
32}
33
34impl TryFrom<u8> for MessageType {
35    type Error = Error;
36    fn try_from(value: u8) -> Result<Self, Error> {
37        MessageType::try_from(value)
38    }
39}
40
41/// Newtype for message type field
42/// The field encodes the message type and the TP flag.
43/// The TP flag indicates that the message is a SOME/IP TP message.
44#[derive(Clone, Copy, Debug, Eq, PartialEq)]
45pub struct MessageTypeField(u8);
46
47impl TryFrom<u8> for MessageTypeField {
48    type Error = Error;
49    fn try_from(value: u8) -> Result<Self, Self::Error> {
50        MessageType::try_from(value)?;
51        Ok(MessageTypeField(value))
52    }
53}
54
55impl From<MessageTypeField> for u8 {
56    fn from(message_type_field: MessageTypeField) -> u8 {
57        message_type_field.0
58    }
59}
60
61impl MessageTypeField {
62    /// Creates a new message type field from a [`MessageType`] and TP flag.
63    #[must_use]
64    pub const fn new(msg_type: MessageType, tp: bool) -> Self {
65        // Map to the SOME/IP *wire* encoding — NOT `msg_type as u8`, which
66        // is the enum discriminant and only coincides with the wire byte
67        // for Request/RequestNoReturn/Notification (0/1/2). Response and
68        // Error diverge (discriminant 3/4 vs wire 0x80/0x81).
69        let base = match msg_type {
70            MessageType::Request => 0x00,
71            MessageType::RequestNoReturn => 0x01,
72            MessageType::Notification => 0x02,
73            MessageType::Response => 0x80,
74            MessageType::Error => 0x81,
75        };
76        let message_type_byte = if tp {
77            base | MESSAGE_TYPE_TP_FLAG
78        } else {
79            base
80        };
81        MessageTypeField(message_type_byte)
82    }
83
84    /// Creates a message type field for SOME/IP-SD (Notification, no TP).
85    #[must_use]
86    pub const fn new_sd() -> Self {
87        Self::new(MessageType::Notification, false)
88    }
89
90    /// Returns the message type of the message
91    ///
92    /// # Panics
93    ///
94    /// Cannot panic — the inner byte is always a valid `MessageType`.
95    #[must_use]
96    pub const fn message_type(&self) -> MessageType {
97        // The inner byte is always valid because it is validated on construction.
98        match self.0 & !MESSAGE_TYPE_TP_FLAG {
99            0x00 => MessageType::Request,
100            0x01 => MessageType::RequestNoReturn,
101            0x02 => MessageType::Notification,
102            0x80 => MessageType::Response,
103            0x81 => MessageType::Error,
104            _ => unreachable!(),
105        }
106    }
107
108    /// Returns the raw byte value of the message type field.
109    #[must_use]
110    pub const fn as_u8(self) -> u8 {
111        self.0
112    }
113
114    /// Returns `true` if the TP (Transport Protocol) flag is set.
115    #[must_use]
116    pub const fn is_tp(&self) -> bool {
117        self.0 & MESSAGE_TYPE_TP_FLAG != 0
118    }
119}
120
121#[cfg(test)]
122mod tests {
123
124    use super::*;
125
126    // --- MessageType TryFrom<u8> ---
127
128    #[test]
129    fn message_type_trait_try_from() {
130        // Exercise the TryFrom<u8> trait impl (not the inherent const fn)
131        let mt: Result<MessageType, _> = 0x00u8.try_into();
132        assert_eq!(mt.unwrap(), MessageType::Request);
133    }
134
135    // --- MessageTypeField::new ---
136
137    #[test]
138    fn new_with_tp_true() {
139        let field = MessageTypeField::new(MessageType::Request, true);
140        assert_eq!(field.message_type(), MessageType::Request);
141        assert!(field.is_tp());
142        assert_eq!(u8::from(field), 0x20);
143    }
144
145    #[test]
146    fn new_with_tp_false() {
147        let field = MessageTypeField::new(MessageType::Request, false);
148        assert_eq!(field.message_type(), MessageType::Request);
149        assert!(!field.is_tp());
150        assert_eq!(u8::from(field), 0x00);
151    }
152
153    // --- MessageTypeField::new_sd ---
154
155    #[test]
156    fn new_sd_is_notification_no_tp() {
157        let field = MessageTypeField::new_sd();
158        assert_eq!(field.message_type(), MessageType::Notification);
159        assert!(!field.is_tp());
160    }
161
162    /// `new` must emit the SOME/IP *wire* byte for every variant — not the
163    /// enum discriminant. `Response`/`Error` are the variants where the two
164    /// diverge (discriminant 3/4 vs wire 0x80/0x81); the others coincide.
165    #[test]
166    fn new_emits_wire_byte_for_all_variants() {
167        for (mt, wire) in [
168            (MessageType::Request, 0x00u8),
169            (MessageType::RequestNoReturn, 0x01),
170            (MessageType::Notification, 0x02),
171            (MessageType::Response, 0x80),
172            (MessageType::Error, 0x81),
173        ] {
174            let field = MessageTypeField::new(mt, false);
175            assert_eq!(u8::from(field), wire, "wire byte for {mt:?}");
176            assert_eq!(field.message_type(), mt, "round-trips back to {mt:?}");
177            let tp = MessageTypeField::new(mt, true);
178            assert_eq!(
179                u8::from(tp),
180                wire | MESSAGE_TYPE_TP_FLAG,
181                "wire byte for {mt:?} with TP flag"
182            );
183            assert_eq!(tp.message_type(), mt, "TP variant round-trips for {mt:?}");
184        }
185    }
186
187    // --- exhaustive u8 ---
188
189    /// Check that we properly decode and encode hex bytes
190    #[test]
191    fn test_all_u8_values() {
192        let valid_inputs: [u8; 10] = [0x00, 0x01, 0x02, 0x80, 0x81, 0x20, 0x21, 0x22, 0xA0, 0xA1];
193        for i in 0..=255 {
194            let msg_type = MessageTypeField::try_from(i);
195            if valid_inputs.contains(&i) {
196                assert!(msg_type.is_ok());
197                let msg_type = msg_type.unwrap();
198                match i {
199                    0x00 => {
200                        assert_eq!(msg_type.message_type(), MessageType::Request);
201                        assert!(!msg_type.is_tp());
202                    }
203                    0x01 => {
204                        assert_eq!(msg_type.message_type(), MessageType::RequestNoReturn);
205                        assert!(!msg_type.is_tp());
206                    }
207                    0x02 => {
208                        assert_eq!(msg_type.message_type(), MessageType::Notification);
209                        assert!(!msg_type.is_tp());
210                    }
211                    0x80 => {
212                        assert_eq!(msg_type.message_type(), MessageType::Response);
213                        assert!(!msg_type.is_tp());
214                    }
215                    0x81 => {
216                        assert_eq!(msg_type.message_type(), MessageType::Error);
217                        assert!(!msg_type.is_tp());
218                    }
219                    0x20 => {
220                        assert_eq!(msg_type.message_type(), MessageType::Request);
221                        assert!(msg_type.is_tp());
222                    }
223                    0x21 => {
224                        assert_eq!(msg_type.message_type(), MessageType::RequestNoReturn);
225                        assert!(msg_type.is_tp());
226                    }
227                    0x22 => {
228                        assert_eq!(msg_type.message_type(), MessageType::Notification);
229                        assert!(msg_type.is_tp());
230                    }
231                    0xA0 => {
232                        assert_eq!(msg_type.message_type(), MessageType::Response);
233                        assert!(msg_type.is_tp());
234                    }
235                    0xA1 => {
236                        assert_eq!(msg_type.message_type(), MessageType::Error);
237                        assert!(msg_type.is_tp());
238                    }
239
240                    _ => unreachable!("Only valid inputs should have made it to this point"),
241                }
242            } else {
243                assert!(msg_type.is_err());
244            }
245        }
246    }
247}