Skip to main content

simple_someip/protocol/
message.rs

1use crate::{
2    protocol::{Error, Header, MessageType, ReturnCode, header::HeaderView, sd::SdHeaderView},
3    traits::{PayloadWireFormat, WireFormat},
4};
5
6/// A SOME/IP message consisting of a [`Header`] and a payload.
7#[derive(Clone, Debug, Eq, PartialEq)]
8pub struct Message<PayloadDefinition> {
9    header: Header,
10    payload: PayloadDefinition,
11}
12
13impl<PayloadDefinition: PayloadWireFormat> Message<PayloadDefinition> {
14    /// Creates a new message from a header and payload.
15    pub const fn new(header: Header, payload: PayloadDefinition) -> Self {
16        Self { header, payload }
17    }
18
19    /// Creates a new SOME/IP-SD message from a request ID and SD header.
20    #[must_use]
21    pub fn new_sd(
22        request_id: u32,
23        sd_header: &<PayloadDefinition as PayloadWireFormat>::SdHeader,
24    ) -> Self {
25        let sd_header_size = sd_header.required_size();
26        Self::new(
27            Header::new_sd(request_id, sd_header_size),
28            PayloadDefinition::new_sd_payload(sd_header),
29        )
30    }
31
32    /// Returns a reference to the message header.
33    pub const fn header(&self) -> &Header {
34        &self.header
35    }
36
37    /// Returns `true` if this is a SOME/IP-SD message.
38    pub const fn is_sd(&self) -> bool {
39        self.header.is_sd()
40    }
41
42    /// Sets the request ID in the header.
43    pub const fn set_request_id(&mut self, request_id: u32) {
44        self.header.set_request_id(request_id);
45    }
46
47    /// Returns the SD header if this is an SD message, or `None` otherwise.
48    pub fn sd_header(&self) -> Option<&<PayloadDefinition as PayloadWireFormat>::SdHeader> {
49        if !self.header().message_id().is_sd() || self.header().message_type().is_tp() {
50            return None;
51        }
52        self.payload.as_sd_header()
53    }
54
55    /// Returns a reference to the payload.
56    pub const fn payload(&self) -> &PayloadDefinition {
57        &self.payload
58    }
59
60    /// Returns a mutable reference to the payload.
61    pub const fn payload_mut(&mut self) -> &mut PayloadDefinition {
62        &mut self.payload
63    }
64}
65
66/// Zero-copy view into a complete SOME/IP message (header + payload).
67#[derive(Clone, Copy, Debug)]
68pub struct MessageView<'a> {
69    header: HeaderView<'a>,
70    payload: &'a [u8],
71}
72
73impl<'a> MessageView<'a> {
74    /// Parse a complete SOME/IP message from `buf`.
75    ///
76    /// Validates the header, checks that the buffer contains enough data for
77    /// the declared payload, and for SD messages validates SD-specific constraints.
78    ///
79    /// # Errors
80    ///
81    /// Returns an error if the header is invalid, the buffer is too short for the
82    /// declared payload, or SD-specific validation fails.
83    pub fn parse(buf: &'a [u8]) -> Result<Self, Error> {
84        let (header, remaining) = HeaderView::parse(buf)?;
85        let payload_size = header.payload_size();
86
87        if remaining.len() < payload_size {
88            return Err(Error::UnexpectedEof);
89        }
90
91        // SD-specific validation
92        if header.is_sd() {
93            if payload_size < 12 {
94                return Err(
95                    crate::protocol::sd::Error::InvalidMessage("SD message too short").into(),
96                );
97            }
98            if header.interface_version() != 0x01 {
99                return Err(crate::protocol::sd::Error::InvalidMessage(
100                    "SD interface version mismatch",
101                )
102                .into());
103            }
104            if header.message_type().message_type() != MessageType::Notification {
105                return Err(
106                    crate::protocol::sd::Error::InvalidMessage("SD message type mismatch").into(),
107                );
108            }
109            if header.return_code() != ReturnCode::Ok {
110                return Err(
111                    crate::protocol::sd::Error::InvalidMessage("SD return code mismatch").into(),
112                );
113            }
114        }
115
116        let payload = &remaining[..payload_size];
117        Ok(Self { header, payload })
118    }
119
120    /// Returns the header view.
121    #[must_use]
122    pub fn header(&self) -> HeaderView<'a> {
123        self.header
124    }
125
126    /// Returns the raw payload bytes.
127    #[must_use]
128    pub fn payload_bytes(&self) -> &'a [u8] {
129        self.payload
130    }
131
132    /// Returns `true` if this is a SOME/IP-SD message.
133    #[must_use]
134    pub fn is_sd(&self) -> bool {
135        self.header.is_sd()
136    }
137
138    /// Parse the payload as an SD header.
139    /// The caller should check `is_sd()` first; this method returns an error
140    /// if the message is not an SD message (the SD validation in `parse` must
141    /// have already passed).
142    ///
143    /// # Errors
144    ///
145    /// Returns an error if this is not an SD message or the SD payload is malformed.
146    pub fn sd_header(&self) -> Result<SdHeaderView<'a>, Error> {
147        if !self.is_sd() {
148            return Err(crate::protocol::sd::Error::InvalidMessage("Not an SD message").into());
149        }
150        SdHeaderView::parse(self.payload)
151    }
152}
153
154impl<PayloadDefinition: PayloadWireFormat> WireFormat for Message<PayloadDefinition> {
155    fn required_size(&self) -> usize {
156        self.header.required_size() + self.payload.required_size()
157    }
158
159    fn encode<W: embedded_io::Write>(&self, writer: &mut W) -> Result<usize, Error> {
160        Ok(self.header.encode(writer)? + self.payload.encode(writer)?)
161    }
162}
163
164#[cfg(test)]
165mod tests {
166    use super::*;
167    use crate::protocol::sd::test_support::{TestPayload, TestSdHeader, empty_sd_header};
168    use crate::protocol::{MessageId, sd};
169
170    type Msg = Message<TestPayload>;
171
172    fn minimal_sd_header() -> TestSdHeader {
173        empty_sd_header()
174    }
175
176    fn make_sd_message() -> Msg {
177        Msg::new_sd(0x0000_0001, &minimal_sd_header())
178    }
179
180    // --- new ---
181
182    #[test]
183    fn new_stores_header_and_payload() {
184        let header = Header::new_sd(0x42, 12);
185        let payload = TestPayload::new_sd_payload(&minimal_sd_header());
186        let msg = Msg::new(header.clone(), payload.clone());
187        assert_eq!(*msg.header(), header);
188        assert_eq!(*msg.payload(), payload);
189    }
190
191    // --- new_sd ---
192
193    #[test]
194    fn new_sd_creates_valid_message() {
195        let msg = make_sd_message();
196        assert!(msg.is_sd());
197        assert_eq!(msg.header().message_id(), MessageId::SD);
198    }
199
200    // --- header / payload / payload_mut ---
201
202    #[test]
203    fn header_returns_reference() {
204        let msg = make_sd_message();
205        assert_eq!(msg.header().protocol_version(), 0x01);
206    }
207
208    #[test]
209    fn payload_returns_reference() {
210        let sd_hdr = minimal_sd_header();
211        let msg = make_sd_message();
212        assert_eq!(msg.payload().as_sd_header().unwrap(), &sd_hdr);
213    }
214
215    #[test]
216    fn payload_mut_allows_modification() {
217        let mut msg = make_sd_message();
218        let _p = msg.payload_mut();
219        // Just verify we get a mutable reference without panic
220    }
221
222    // --- is_sd ---
223
224    #[test]
225    fn is_sd_true_for_sd_message() {
226        assert!(make_sd_message().is_sd());
227    }
228
229    // --- set_request_id ---
230
231    #[test]
232    fn set_request_id_updates_header() {
233        let mut msg = make_sd_message();
234        msg.set_request_id(0xDEAD_BEEF);
235        assert_eq!(msg.header().request_id(), 0xDEAD_BEEF);
236    }
237
238    // --- get_sd_header ---
239
240    #[test]
241    fn get_sd_header_returns_some_for_sd() {
242        let sd_hdr = minimal_sd_header();
243        let msg = make_sd_message();
244        assert_eq!(msg.sd_header().unwrap(), &sd_hdr);
245    }
246
247    // --- WireFormat: required_size ---
248
249    #[test]
250    fn required_size_is_header_plus_payload() {
251        let msg = make_sd_message();
252        let expected = msg.header().required_size() + msg.payload().required_size();
253        assert_eq!(msg.required_size(), expected);
254    }
255
256    // --- WireFormat: encode / MessageView::parse round-trip ---
257
258    #[test]
259    fn encode_parse_round_trip() {
260        let msg = make_sd_message();
261        let mut buf = [0u8; 64];
262        let n = msg.encode(&mut buf.as_mut_slice()).unwrap();
263        assert_eq!(n, msg.required_size());
264        let view = MessageView::parse(&buf[..n]).unwrap();
265        assert!(view.is_sd());
266        assert_eq!(view.header().to_owned(), *msg.header());
267    }
268
269    #[test]
270    fn encode_parse_with_entries() {
271        let mut entries = heapless::Vec::<sd::Entry, 4>::new();
272        entries
273            .push(sd::Entry::FindService(sd::ServiceEntry::find(0xABCD)))
274            .unwrap();
275        let sd_hdr = TestSdHeader {
276            flags: sd::Flags::new_sd(true),
277            entries,
278            options: heapless::Vec::new(),
279        };
280        let msg = Msg::new_sd(0x42, &sd_hdr);
281        let mut buf = [0u8; 64];
282        let n = msg.encode(&mut buf.as_mut_slice()).unwrap();
283        let view = MessageView::parse(&buf[..n]).unwrap();
284        let sd_view = view.sd_header().unwrap();
285        assert_eq!(sd_view.entry_count(), 1);
286        let entry = sd_view.entries().next().unwrap();
287        assert_eq!(entry.service_id(), 0xABCD);
288    }
289
290    // --- parse with exactly-sized slice ---
291
292    #[test]
293    fn parse_exact_size_slice_succeeds() {
294        let msg = make_sd_message();
295        let mut buf = [0u8; 64];
296        let n = msg.encode(&mut buf.as_mut_slice()).unwrap();
297        // Pass exactly n bytes — no extra data beyond the message
298        let view = MessageView::parse(&buf[..n]).unwrap();
299        assert!(view.is_sd());
300        assert_eq!(view.header().to_owned(), *msg.header());
301    }
302
303    // --- parse error paths ---
304
305    #[test]
306    fn parse_truncated_returns_eof() {
307        let buf: [u8; 4] = [0; 4];
308        assert!(matches!(
309            MessageView::parse(&buf[..]),
310            Err(Error::UnexpectedEof)
311        ));
312    }
313
314    // --- parse SD validation errors ---
315
316    #[test]
317    fn parse_sd_payload_too_short_returns_error() {
318        let msg = make_sd_message();
319        let mut buf = [0u8; 64];
320        msg.encode(&mut buf.as_mut_slice()).unwrap();
321        // Overwrite the length field (bytes 4..8) to make payload_size < 12
322        // length = 8 + payload_size, so length=19 → payload_size=11
323        let bad_len: u32 = 19;
324        buf[4..8].copy_from_slice(&bad_len.to_be_bytes());
325        assert!(matches!(
326            MessageView::parse(&buf[..]),
327            Err(Error::Sd(crate::protocol::sd::Error::InvalidMessage(
328                "SD message too short"
329            )))
330        ));
331    }
332
333    #[test]
334    fn parse_sd_wrong_interface_version_returns_error() {
335        let msg = make_sd_message();
336        let mut buf = [0u8; 64];
337        let n = msg.encode(&mut buf.as_mut_slice()).unwrap();
338        buf[13] = 0x02; // interface_version at byte 13
339        assert!(matches!(
340            MessageView::parse(&buf[..n]),
341            Err(Error::Sd(crate::protocol::sd::Error::InvalidMessage(
342                "SD interface version mismatch"
343            )))
344        ));
345    }
346
347    #[test]
348    fn parse_sd_wrong_message_type_returns_error() {
349        let msg = make_sd_message();
350        let mut buf = [0u8; 64];
351        let n = msg.encode(&mut buf.as_mut_slice()).unwrap();
352        buf[14] = 0x00; // Request instead of Notification
353        assert!(matches!(
354            MessageView::parse(&buf[..n]),
355            Err(Error::Sd(crate::protocol::sd::Error::InvalidMessage(
356                "SD message type mismatch"
357            )))
358        ));
359    }
360
361    #[test]
362    fn parse_sd_wrong_return_code_returns_error() {
363        let msg = make_sd_message();
364        let mut buf = [0u8; 64];
365        let n = msg.encode(&mut buf.as_mut_slice()).unwrap();
366        buf[15] = 0x01; // NotOk instead of Ok
367        assert!(matches!(
368            MessageView::parse(&buf[..n]),
369            Err(Error::Sd(crate::protocol::sd::Error::InvalidMessage(
370                "SD return code mismatch"
371            )))
372        ));
373    }
374
375    // --- MessageView accessors ---
376
377    #[test]
378    fn message_view_payload_bytes() {
379        let msg = make_sd_message();
380        let mut buf = [0u8; 64];
381        let n = msg.encode(&mut buf.as_mut_slice()).unwrap();
382        let view = MessageView::parse(&buf[..n]).unwrap();
383        assert_eq!(view.payload_bytes().len(), msg.header().payload_size());
384    }
385
386    #[test]
387    fn message_view_sd_header_on_non_sd_returns_error() {
388        // Build a non-SD message
389        let header = Header::new(
390            MessageId::new_from_service_and_method(0x1234, 0x0001),
391            0x0001,
392            0x01,
393            0x01,
394            crate::protocol::MessageTypeField::try_from(0x00).unwrap(),
395            ReturnCode::Ok,
396            0,
397        );
398        let mut buf = [0u8; 16];
399        header.encode(&mut buf.as_mut_slice()).unwrap();
400        let view = MessageView::parse(&buf).unwrap();
401        assert!(matches!(
402            view.sd_header(),
403            Err(Error::Sd(crate::protocol::sd::Error::InvalidMessage(
404                "Not an SD message"
405            )))
406        ));
407    }
408}