someip_rs/
message.rs

1//! SOME/IP message handling.
2
3use bytes::Bytes;
4
5use crate::error::{Result, SomeIpError};
6use crate::header::{ClientId, MethodId, ServiceId, SessionId, SomeIpHeader, HEADER_SIZE};
7use crate::types::{MessageType, ReturnCode};
8
9/// Maximum payload size (default: 1400 bytes for UDP compatibility).
10pub const DEFAULT_MAX_PAYLOAD_SIZE: usize = 1400;
11
12/// A complete SOME/IP message (header + payload).
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct SomeIpMessage {
15    /// Message header.
16    pub header: SomeIpHeader,
17    /// Message payload.
18    pub payload: Bytes,
19}
20
21impl SomeIpMessage {
22    /// Create a new message with the given header and payload.
23    pub fn new(mut header: SomeIpHeader, payload: impl Into<Bytes>) -> Self {
24        let payload = payload.into();
25        header.set_payload_length(payload.len() as u32);
26        Self { header, payload }
27    }
28
29    /// Create a new message with an empty payload.
30    pub fn with_header(header: SomeIpHeader) -> Self {
31        Self::new(header, Bytes::new())
32    }
33
34    /// Create a request message builder.
35    pub fn request(service_id: ServiceId, method_id: MethodId) -> MessageBuilder {
36        MessageBuilder::new(service_id, method_id, MessageType::Request)
37    }
38
39    /// Create a request-no-return message builder.
40    pub fn request_no_return(service_id: ServiceId, method_id: MethodId) -> MessageBuilder {
41        MessageBuilder::new(service_id, method_id, MessageType::RequestNoReturn)
42    }
43
44    /// Create a notification message builder.
45    pub fn notification(service_id: ServiceId, method_id: MethodId) -> MessageBuilder {
46        MessageBuilder::new(service_id, method_id, MessageType::Notification)
47    }
48
49    /// Create a response to this message.
50    pub fn create_response(&self) -> MessageBuilder {
51        let mut builder = MessageBuilder::new(
52            self.header.service_id,
53            self.header.method_id,
54            MessageType::Response,
55        );
56        builder.client_id = self.header.client_id;
57        builder.session_id = self.header.session_id;
58        builder.interface_version = self.header.interface_version;
59        builder
60    }
61
62    /// Create an error response to this message.
63    pub fn create_error_response(&self, return_code: ReturnCode) -> MessageBuilder {
64        let mut builder = MessageBuilder::new(
65            self.header.service_id,
66            self.header.method_id,
67            MessageType::Error,
68        );
69        builder.client_id = self.header.client_id;
70        builder.session_id = self.header.session_id;
71        builder.interface_version = self.header.interface_version;
72        builder.return_code = return_code;
73        builder
74    }
75
76    /// Parse a message from bytes.
77    pub fn from_bytes(data: &[u8]) -> Result<Self> {
78        if data.len() < HEADER_SIZE {
79            return Err(SomeIpError::MessageTooShort {
80                expected: HEADER_SIZE,
81                actual: data.len(),
82            });
83        }
84
85        let header = SomeIpHeader::from_bytes(data)?;
86        let expected_total = HEADER_SIZE + header.payload_length() as usize;
87
88        if data.len() < expected_total {
89            return Err(SomeIpError::LengthMismatch {
90                header_length: header.length,
91                actual_length: data.len() - 8,
92            });
93        }
94
95        let payload = Bytes::copy_from_slice(&data[HEADER_SIZE..expected_total]);
96
97        Ok(Self { header, payload })
98    }
99
100    /// Serialize the message to bytes.
101    pub fn to_bytes(&self) -> Vec<u8> {
102        let mut buf = Vec::with_capacity(HEADER_SIZE + self.payload.len());
103        buf.extend_from_slice(&self.header.to_bytes());
104        buf.extend_from_slice(&self.payload);
105        buf
106    }
107
108    /// Get the total message size (header + payload).
109    pub fn total_size(&self) -> usize {
110        HEADER_SIZE + self.payload.len()
111    }
112
113    /// Check if this message is a request.
114    pub fn is_request(&self) -> bool {
115        matches!(
116            self.header.message_type,
117            MessageType::Request | MessageType::TpRequest
118        )
119    }
120
121    /// Check if this message is a response.
122    pub fn is_response(&self) -> bool {
123        self.header.message_type.is_response()
124    }
125
126    /// Check if this message expects a response.
127    pub fn expects_response(&self) -> bool {
128        self.header.message_type.expects_response()
129    }
130
131    /// Get the service ID.
132    pub fn service_id(&self) -> ServiceId {
133        self.header.service_id
134    }
135
136    /// Get the method ID.
137    pub fn method_id(&self) -> MethodId {
138        self.header.method_id
139    }
140
141    /// Get the client ID.
142    pub fn client_id(&self) -> ClientId {
143        self.header.client_id
144    }
145
146    /// Get the session ID.
147    pub fn session_id(&self) -> SessionId {
148        self.header.session_id
149    }
150
151    /// Get the return code.
152    pub fn return_code(&self) -> ReturnCode {
153        self.header.return_code
154    }
155
156    /// Check if the return code indicates success.
157    pub fn is_ok(&self) -> bool {
158        self.header.return_code.is_ok()
159    }
160}
161
162/// Builder for constructing SOME/IP messages.
163#[derive(Debug, Clone)]
164pub struct MessageBuilder {
165    service_id: ServiceId,
166    method_id: MethodId,
167    client_id: ClientId,
168    session_id: SessionId,
169    interface_version: u8,
170    message_type: MessageType,
171    return_code: ReturnCode,
172    payload: Bytes,
173}
174
175impl MessageBuilder {
176    /// Create a new message builder.
177    pub fn new(service_id: ServiceId, method_id: MethodId, message_type: MessageType) -> Self {
178        Self {
179            service_id,
180            method_id,
181            client_id: ClientId::default(),
182            session_id: SessionId::default(),
183            interface_version: 1,
184            message_type,
185            return_code: ReturnCode::Ok,
186            payload: Bytes::new(),
187        }
188    }
189
190    /// Set the client ID.
191    pub fn client_id(mut self, client_id: ClientId) -> Self {
192        self.client_id = client_id;
193        self
194    }
195
196    /// Set the session ID.
197    pub fn session_id(mut self, session_id: SessionId) -> Self {
198        self.session_id = session_id;
199        self
200    }
201
202    /// Set the interface version.
203    pub fn interface_version(mut self, version: u8) -> Self {
204        self.interface_version = version;
205        self
206    }
207
208    /// Set the return code.
209    pub fn return_code(mut self, code: ReturnCode) -> Self {
210        self.return_code = code;
211        self
212    }
213
214    /// Set the payload from bytes.
215    pub fn payload(mut self, payload: impl Into<Bytes>) -> Self {
216        self.payload = payload.into();
217        self
218    }
219
220    /// Set the payload from a Vec<u8>.
221    pub fn payload_vec(mut self, payload: Vec<u8>) -> Self {
222        self.payload = Bytes::from(payload);
223        self
224    }
225
226    /// Build the message.
227    pub fn build(self) -> SomeIpMessage {
228        let header = SomeIpHeader {
229            service_id: self.service_id,
230            method_id: self.method_id,
231            length: 8 + self.payload.len() as u32,
232            client_id: self.client_id,
233            session_id: self.session_id,
234            protocol_version: crate::types::PROTOCOL_VERSION,
235            interface_version: self.interface_version,
236            message_type: self.message_type,
237            return_code: self.return_code,
238        };
239
240        SomeIpMessage {
241            header,
242            payload: self.payload,
243        }
244    }
245}
246
247#[cfg(test)]
248mod tests {
249    use super::*;
250
251    #[test]
252    fn test_message_builder() {
253        let msg = SomeIpMessage::request(ServiceId(0x1234), MethodId(0x0001))
254            .client_id(ClientId(0x0100))
255            .session_id(SessionId(0x0001))
256            .payload(b"hello".as_slice())
257            .build();
258
259        assert_eq!(msg.header.service_id, ServiceId(0x1234));
260        assert_eq!(msg.header.method_id, MethodId(0x0001));
261        assert_eq!(msg.header.client_id, ClientId(0x0100));
262        assert_eq!(msg.header.session_id, SessionId(0x0001));
263        assert_eq!(msg.header.message_type, MessageType::Request);
264        assert_eq!(msg.payload.as_ref(), b"hello");
265        assert_eq!(msg.header.length, 8 + 5); // 8 + payload length
266    }
267
268    #[test]
269    fn test_message_roundtrip() {
270        let original = SomeIpMessage::request(ServiceId(0x1234), MethodId(0x5678))
271            .client_id(ClientId(0xABCD))
272            .session_id(SessionId(0x0001))
273            .payload(vec![1, 2, 3, 4, 5])
274            .build();
275
276        let bytes = original.to_bytes();
277        let parsed = SomeIpMessage::from_bytes(&bytes).unwrap();
278
279        assert_eq!(original, parsed);
280    }
281
282    #[test]
283    fn test_create_response() {
284        let request = SomeIpMessage::request(ServiceId(0x1234), MethodId(0x0001))
285            .client_id(ClientId(0x0100))
286            .session_id(SessionId(0x0042))
287            .build();
288
289        let response = request
290            .create_response()
291            .payload(b"response data".as_slice())
292            .build();
293
294        assert_eq!(response.header.service_id, request.header.service_id);
295        assert_eq!(response.header.method_id, request.header.method_id);
296        assert_eq!(response.header.client_id, request.header.client_id);
297        assert_eq!(response.header.session_id, request.header.session_id);
298        assert_eq!(response.header.message_type, MessageType::Response);
299    }
300
301    #[test]
302    fn test_create_error_response() {
303        let request = SomeIpMessage::request(ServiceId(0x1234), MethodId(0x0001))
304            .client_id(ClientId(0x0100))
305            .session_id(SessionId(0x0042))
306            .build();
307
308        let error = request
309            .create_error_response(ReturnCode::UnknownMethod)
310            .build();
311
312        assert_eq!(error.header.message_type, MessageType::Error);
313        assert_eq!(error.header.return_code, ReturnCode::UnknownMethod);
314    }
315
316    #[test]
317    fn test_total_size() {
318        let msg = SomeIpMessage::request(ServiceId(0x1234), MethodId(0x0001))
319            .payload(vec![0u8; 100])
320            .build();
321
322        assert_eq!(msg.total_size(), HEADER_SIZE + 100);
323    }
324
325    #[test]
326    fn test_parse_too_short() {
327        let data = vec![0u8; 10];
328        let result = SomeIpMessage::from_bytes(&data);
329        assert!(matches!(result, Err(SomeIpError::MessageTooShort { .. })));
330    }
331}