openigtlink_rust/protocol/
factory.rs

1//! Message factory for dynamic message type resolution
2//!
3//! This module provides a factory pattern for creating OpenIGTLink messages
4//! based on the message type name in the header, similar to the C++ implementation.
5
6use crate::error::{IgtlError, Result};
7use crate::protocol::any_message::AnyMessage;
8use crate::protocol::header::Header;
9use crate::protocol::message::IgtlMessage;
10use crate::protocol::types::*;
11
12/// Message factory for creating messages dynamically based on type name
13///
14/// This factory allows receiving and decoding messages without knowing the type
15/// at compile time. The message type is determined from the header's type_name field.
16///
17/// # Examples
18///
19/// ```no_run
20/// # use openigtlink_rust::protocol::factory::MessageFactory;
21/// # use openigtlink_rust::protocol::header::Header;
22/// # fn example(header: Header, body: &[u8]) -> Result<(), openigtlink_rust::error::IgtlError> {
23/// let factory = MessageFactory::new();
24/// let message = factory.decode_any(&header, body, true)?;
25/// println!("Received {} message", message.message_type());
26/// # Ok(())
27/// # }
28/// ```
29pub struct MessageFactory;
30
31impl MessageFactory {
32    /// Create a new message factory
33    pub fn new() -> Self {
34        MessageFactory
35    }
36
37    /// Decode a message from header and body bytes
38    ///
39    /// # Arguments
40    ///
41    /// * `header` - Parsed message header
42    /// * `body` - Raw body bytes (may include extended header, content, and metadata)
43    /// * `verify_crc` - Whether to verify CRC checksum
44    ///
45    /// # Returns
46    ///
47    /// `AnyMessage` containing the decoded message, or `AnyMessage::Unknown` if the
48    /// message type is not recognized.
49    ///
50    /// # Examples
51    ///
52    /// ```no_run
53    /// # use openigtlink_rust::protocol::factory::MessageFactory;
54    /// # use openigtlink_rust::protocol::header::Header;
55    /// # fn example(header: Header, body: &[u8]) -> Result<(), openigtlink_rust::error::IgtlError> {
56    /// let factory = MessageFactory::new();
57    /// let message = factory.decode_any(&header, body, true)?;
58    ///
59    /// match message {
60    ///     openigtlink_rust::protocol::AnyMessage::Transform(msg) => {
61    ///         println!("Transform from {}", msg.header.device_name.as_str()?);
62    ///     }
63    ///     openigtlink_rust::protocol::AnyMessage::Unknown { .. } => {
64    ///         println!("Unknown message type");
65    ///     }
66    ///     _ => {}
67    /// }
68    /// # Ok(())
69    /// # }
70    /// ```
71    pub fn decode_any(&self, header: &Header, body: &[u8], verify_crc: bool) -> Result<AnyMessage> {
72        use crate::protocol::crc::calculate_crc;
73
74        // Verify CRC if requested
75        if verify_crc {
76            let calculated_crc = calculate_crc(body);
77            if calculated_crc != header.crc {
78                return Err(IgtlError::CrcMismatch {
79                    expected: header.crc,
80                    actual: calculated_crc,
81                });
82            }
83        }
84
85        // Reconstruct full message bytes (header + body)
86        let mut full_msg = header.encode().to_vec();
87        full_msg.extend_from_slice(body);
88
89        // Get type name
90        let type_name = header.type_name.as_str()?;
91
92        // Decode based on type name
93        match type_name {
94            "TRANSFORM" => Ok(AnyMessage::Transform(
95                IgtlMessage::<TransformMessage>::decode_with_options(&full_msg, false)?,
96            )),
97            "STATUS" => Ok(AnyMessage::Status(
98                IgtlMessage::<StatusMessage>::decode_with_options(&full_msg, false)?,
99            )),
100            "CAPABILITY" => Ok(AnyMessage::Capability(
101                IgtlMessage::<CapabilityMessage>::decode_with_options(&full_msg, false)?,
102            )),
103            "IMAGE" => Ok(AnyMessage::Image(
104                IgtlMessage::<ImageMessage>::decode_with_options(&full_msg, false)?,
105            )),
106            "POSITION" => Ok(AnyMessage::Position(
107                IgtlMessage::<PositionMessage>::decode_with_options(&full_msg, false)?,
108            )),
109            "STRING" => Ok(AnyMessage::String(
110                IgtlMessage::<StringMessage>::decode_with_options(&full_msg, false)?,
111            )),
112            "QTDATA" => Ok(AnyMessage::QtData(
113                IgtlMessage::<QtDataMessage>::decode_with_options(&full_msg, false)?,
114            )),
115            "TDATA" => Ok(AnyMessage::TData(
116                IgtlMessage::<TDataMessage>::decode_with_options(&full_msg, false)?,
117            )),
118            "SENSOR" => Ok(AnyMessage::Sensor(
119                IgtlMessage::<SensorMessage>::decode_with_options(&full_msg, false)?,
120            )),
121            "POINT" => Ok(AnyMessage::Point(
122                IgtlMessage::<PointMessage>::decode_with_options(&full_msg, false)?,
123            )),
124            "TRAJECTORY" => Ok(AnyMessage::Trajectory(
125                IgtlMessage::<TrajectoryMessage>::decode_with_options(&full_msg, false)?,
126            )),
127            "NDARRAY" => Ok(AnyMessage::NdArray(
128                IgtlMessage::<NdArrayMessage>::decode_with_options(&full_msg, false)?,
129            )),
130            "BIND" => Ok(AnyMessage::Bind(
131                IgtlMessage::<BindMessage>::decode_with_options(&full_msg, false)?,
132            )),
133            "COLORTABLE" => Ok(AnyMessage::ColorTable(
134                IgtlMessage::<ColorTableMessage>::decode_with_options(&full_msg, false)?,
135            )),
136            "IMGMETA" => Ok(AnyMessage::ImgMeta(
137                IgtlMessage::<ImgMetaMessage>::decode_with_options(&full_msg, false)?,
138            )),
139            "LBMETA" => Ok(AnyMessage::LbMeta(
140                IgtlMessage::<LbMetaMessage>::decode_with_options(&full_msg, false)?,
141            )),
142            "POLYDATA" => Ok(AnyMessage::PolyData(
143                IgtlMessage::<PolyDataMessage>::decode_with_options(&full_msg, false)?,
144            )),
145            "VIDEO" => Ok(AnyMessage::Video(
146                IgtlMessage::<VideoMessage>::decode_with_options(&full_msg, false)?,
147            )),
148            "VIDEOMETA" => Ok(AnyMessage::VideoMeta(
149                IgtlMessage::<VideoMetaMessage>::decode_with_options(&full_msg, false)?,
150            )),
151            "COMMAND" => Ok(AnyMessage::Command(
152                IgtlMessage::<CommandMessage>::decode_with_options(&full_msg, false)?,
153            )),
154
155            // Query messages
156            "GET_TRANS" => Ok(AnyMessage::GetTransform(
157                IgtlMessage::<GetTransformMessage>::decode_with_options(&full_msg, false)?,
158            )),
159            "GET_STATUS" => Ok(AnyMessage::GetStatus(
160                IgtlMessage::<GetStatusMessage>::decode_with_options(&full_msg, false)?,
161            )),
162            "GET_CAPABIL" => Ok(AnyMessage::GetCapability(IgtlMessage::<
163                GetCapabilityMessage,
164            >::decode_with_options(
165                &full_msg, false
166            )?)),
167            "GET_IMAGE" => Ok(AnyMessage::GetImage(
168                IgtlMessage::<GetImageMessage>::decode_with_options(&full_msg, false)?,
169            )),
170            "GET_IMGMETA" => Ok(AnyMessage::GetImgMeta(
171                IgtlMessage::<GetImgMetaMessage>::decode_with_options(&full_msg, false)?,
172            )),
173            "GET_LBMETA" => Ok(AnyMessage::GetLbMeta(
174                IgtlMessage::<GetLbMetaMessage>::decode_with_options(&full_msg, false)?,
175            )),
176            "GET_POINT" => Ok(AnyMessage::GetPoint(
177                IgtlMessage::<GetPointMessage>::decode_with_options(&full_msg, false)?,
178            )),
179            "GET_TDATA" => Ok(AnyMessage::GetTData(
180                IgtlMessage::<GetTDataMessage>::decode_with_options(&full_msg, false)?,
181            )),
182
183            // Response messages
184            "RTS_TRANS" => Ok(AnyMessage::RtsTransform(
185                IgtlMessage::<RtsTransformMessage>::decode_with_options(&full_msg, false)?,
186            )),
187            "RTS_STATUS" => Ok(AnyMessage::RtsStatus(
188                IgtlMessage::<RtsStatusMessage>::decode_with_options(&full_msg, false)?,
189            )),
190            "RTS_CAPABIL" => Ok(AnyMessage::RtsCapability(IgtlMessage::<
191                RtsCapabilityMessage,
192            >::decode_with_options(
193                &full_msg, false
194            )?)),
195            "RTS_IMAGE" => Ok(AnyMessage::RtsImage(
196                IgtlMessage::<RtsImageMessage>::decode_with_options(&full_msg, false)?,
197            )),
198            "RTS_TDATA" => Ok(AnyMessage::RtsTData(
199                IgtlMessage::<RtsTDataMessage>::decode_with_options(&full_msg, false)?,
200            )),
201
202            // Streaming control messages
203            "STT_TDATA" => Ok(AnyMessage::StartTData(
204                IgtlMessage::<StartTDataMessage>::decode_with_options(&full_msg, false)?,
205            )),
206            "STP_TRANS" => Ok(AnyMessage::StopTransform(IgtlMessage::<
207                StopTransformMessage,
208            >::decode_with_options(
209                &full_msg, false
210            )?)),
211            "STP_POSITION" => Ok(AnyMessage::StopPosition(
212                IgtlMessage::<StopPositionMessage>::decode_with_options(&full_msg, false)?,
213            )),
214            "STP_QTDATA" => Ok(AnyMessage::StopQtData(
215                IgtlMessage::<StopQtDataMessage>::decode_with_options(&full_msg, false)?,
216            )),
217            "STP_TDATA" => Ok(AnyMessage::StopTData(
218                IgtlMessage::<StopTDataMessage>::decode_with_options(&full_msg, false)?,
219            )),
220            "STP_IMAGE" => Ok(AnyMessage::StopImage(
221                IgtlMessage::<StopImageMessage>::decode_with_options(&full_msg, false)?,
222            )),
223            "STP_NDARRAY" => Ok(AnyMessage::StopNdArray(
224                IgtlMessage::<StopNdArrayMessage>::decode_with_options(&full_msg, false)?,
225            )),
226
227            // Unknown message type - store header and body for manual processing
228            _ => Ok(AnyMessage::Unknown {
229                header: header.clone(),
230                body: body.to_vec(),
231            }),
232        }
233    }
234}
235
236impl Default for MessageFactory {
237    fn default() -> Self {
238        Self::new()
239    }
240}
241
242#[cfg(test)]
243mod tests {
244    use super::*;
245    use crate::protocol::types::TransformMessage;
246
247    #[test]
248    fn test_factory_decode_transform() {
249        let transform = TransformMessage::identity();
250        let msg = IgtlMessage::new(transform, "TestDevice").unwrap();
251        let encoded = msg.encode().unwrap();
252
253        // Parse header
254        let header = Header::decode(&encoded[..Header::SIZE]).unwrap();
255        let body = &encoded[Header::SIZE..];
256
257        // Decode with factory
258        let factory = MessageFactory::new();
259        let any_msg = factory.decode_any(&header, body, true).unwrap();
260
261        assert_eq!(any_msg.message_type(), "TRANSFORM");
262        assert!(any_msg.as_transform().is_some());
263        assert_eq!(any_msg.device_name().unwrap(), "TestDevice");
264    }
265
266    #[test]
267    fn test_factory_decode_status() {
268        let status = StatusMessage::ok("Test message");
269        let msg = IgtlMessage::new(status, "StatusDevice").unwrap();
270        let encoded = msg.encode().unwrap();
271
272        let header = Header::decode(&encoded[..Header::SIZE]).unwrap();
273        let body = &encoded[Header::SIZE..];
274
275        let factory = MessageFactory::new();
276        let any_msg = factory.decode_any(&header, body, true).unwrap();
277
278        assert_eq!(any_msg.message_type(), "STATUS");
279        assert!(any_msg.as_status().is_some());
280    }
281
282    #[test]
283    fn test_factory_unknown_type() {
284        // Create a header with unknown type
285        use crate::protocol::header::{DeviceName, Timestamp, TypeName};
286
287        let header = Header {
288            version: 2,
289            type_name: TypeName::new("CUSTOM").unwrap(),
290            device_name: DeviceName::new("Device").unwrap(),
291            timestamp: Timestamp::now(),
292            body_size: 4,
293            crc: 0,
294        };
295
296        let body = vec![1, 2, 3, 4];
297
298        let factory = MessageFactory::new();
299        let any_msg = factory.decode_any(&header, &body, false).unwrap();
300
301        assert!(any_msg.is_unknown());
302        assert_eq!(any_msg.message_type(), "CUSTOM");
303    }
304
305    #[test]
306    fn test_factory_crc_verification() {
307        let transform = TransformMessage::identity();
308        let msg = IgtlMessage::new(transform, "TestDevice").unwrap();
309        let mut encoded = msg.encode().unwrap();
310
311        let header = Header::decode(&encoded[..Header::SIZE]).unwrap();
312
313        // Corrupt the body
314        encoded[Header::SIZE] ^= 0xFF;
315        let body = &encoded[Header::SIZE..];
316
317        let factory = MessageFactory::new();
318
319        // Should fail with CRC verification
320        let result = factory.decode_any(&header, body, true);
321        assert!(result.is_err());
322
323        // Should succeed without CRC verification
324        let result = factory.decode_any(&header, body, false);
325        assert!(result.is_ok());
326    }
327}