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        let message_type_byte = if tp {
66            msg_type as u8 | MESSAGE_TYPE_TP_FLAG
67        } else {
68            msg_type as u8
69        };
70        MessageTypeField(message_type_byte)
71    }
72
73    /// Creates a message type field for SOME/IP-SD (Notification, no TP).
74    #[must_use]
75    pub const fn new_sd() -> Self {
76        Self::new(MessageType::Notification, false)
77    }
78
79    /// Returns the message type of the message
80    ///
81    /// # Panics
82    ///
83    /// Cannot panic — the inner byte is always a valid `MessageType`.
84    #[must_use]
85    pub const fn message_type(&self) -> MessageType {
86        // The inner byte is always valid because it is validated on construction.
87        match self.0 & !MESSAGE_TYPE_TP_FLAG {
88            0x00 => MessageType::Request,
89            0x01 => MessageType::RequestNoReturn,
90            0x02 => MessageType::Notification,
91            0x80 => MessageType::Response,
92            0x81 => MessageType::Error,
93            _ => unreachable!(),
94        }
95    }
96
97    /// Returns the raw byte value of the message type field.
98    #[must_use]
99    pub const fn as_u8(self) -> u8 {
100        self.0
101    }
102
103    /// Returns `true` if the TP (Transport Protocol) flag is set.
104    #[must_use]
105    pub const fn is_tp(&self) -> bool {
106        self.0 & MESSAGE_TYPE_TP_FLAG != 0
107    }
108}
109
110#[cfg(test)]
111mod tests {
112
113    use super::*;
114
115    // --- MessageType TryFrom<u8> ---
116
117    #[test]
118    fn message_type_trait_try_from() {
119        // Exercise the TryFrom<u8> trait impl (not the inherent const fn)
120        let mt: Result<MessageType, _> = 0x00u8.try_into();
121        assert_eq!(mt.unwrap(), MessageType::Request);
122    }
123
124    // --- MessageTypeField::new ---
125
126    #[test]
127    fn new_with_tp_true() {
128        let field = MessageTypeField::new(MessageType::Request, true);
129        assert_eq!(field.message_type(), MessageType::Request);
130        assert!(field.is_tp());
131        assert_eq!(u8::from(field), 0x20);
132    }
133
134    #[test]
135    fn new_with_tp_false() {
136        let field = MessageTypeField::new(MessageType::Request, false);
137        assert_eq!(field.message_type(), MessageType::Request);
138        assert!(!field.is_tp());
139        assert_eq!(u8::from(field), 0x00);
140    }
141
142    // --- MessageTypeField::new_sd ---
143
144    #[test]
145    fn new_sd_is_notification_no_tp() {
146        let field = MessageTypeField::new_sd();
147        assert_eq!(field.message_type(), MessageType::Notification);
148        assert!(!field.is_tp());
149    }
150
151    // --- exhaustive u8 ---
152
153    /// Check that we properly decode and encode hex bytes
154    #[test]
155    fn test_all_u8_values() {
156        let valid_inputs: [u8; 10] = [0x00, 0x01, 0x02, 0x80, 0x81, 0x20, 0x21, 0x22, 0xA0, 0xA1];
157        for i in 0..=255 {
158            let msg_type = MessageTypeField::try_from(i);
159            if valid_inputs.contains(&i) {
160                assert!(msg_type.is_ok());
161                let msg_type = msg_type.unwrap();
162                match i {
163                    0x00 => {
164                        assert_eq!(msg_type.message_type(), MessageType::Request);
165                        assert!(!msg_type.is_tp());
166                    }
167                    0x01 => {
168                        assert_eq!(msg_type.message_type(), MessageType::RequestNoReturn);
169                        assert!(!msg_type.is_tp());
170                    }
171                    0x02 => {
172                        assert_eq!(msg_type.message_type(), MessageType::Notification);
173                        assert!(!msg_type.is_tp());
174                    }
175                    0x80 => {
176                        assert_eq!(msg_type.message_type(), MessageType::Response);
177                        assert!(!msg_type.is_tp());
178                    }
179                    0x81 => {
180                        assert_eq!(msg_type.message_type(), MessageType::Error);
181                        assert!(!msg_type.is_tp());
182                    }
183                    0x20 => {
184                        assert_eq!(msg_type.message_type(), MessageType::Request);
185                        assert!(msg_type.is_tp());
186                    }
187                    0x21 => {
188                        assert_eq!(msg_type.message_type(), MessageType::RequestNoReturn);
189                        assert!(msg_type.is_tp());
190                    }
191                    0x22 => {
192                        assert_eq!(msg_type.message_type(), MessageType::Notification);
193                        assert!(msg_type.is_tp());
194                    }
195                    0xA0 => {
196                        assert_eq!(msg_type.message_type(), MessageType::Response);
197                        assert!(msg_type.is_tp());
198                    }
199                    0xA1 => {
200                        assert_eq!(msg_type.message_type(), MessageType::Error);
201                        assert!(msg_type.is_tp());
202                    }
203
204                    _ => unreachable!("Only valid inputs should have made it to this point"),
205                }
206            } else {
207                assert!(msg_type.is_err());
208            }
209        }
210    }
211}