openigtlink_rust/protocol/types/
bind.rs

1//! BIND message type implementation
2//!
3//! The BIND message is used to bind multiple OpenIGTLink messages into a single message.
4//! This allows grouping related messages together for synchronized transmission.
5
6use crate::protocol::message::Message;
7use crate::error::{IgtlError, Result};
8use bytes::Buf;
9
10/// Child message entry in BIND message
11#[derive(Debug, Clone, PartialEq)]
12pub struct BindEntry {
13    /// Message type (max 12 chars)
14    pub message_type: String,
15    /// Device name (max 20 chars)
16    pub device_name: String,
17}
18
19impl BindEntry {
20    /// Create a new bind entry
21    pub fn new(message_type: impl Into<String>, device_name: impl Into<String>) -> Self {
22        BindEntry {
23            message_type: message_type.into(),
24            device_name: device_name.into(),
25        }
26    }
27}
28
29/// BIND message for grouping multiple messages
30///
31/// # OpenIGTLink Specification
32/// - Message type: "BIND"
33/// - Format: (TYPE (char[12]) + NAME (char[20])) * n
34/// - Each entry: 32 bytes
35/// - Number of child messages determined by body size / 32
36#[derive(Debug, Clone, PartialEq)]
37pub struct BindMessage {
38    /// List of child message entries
39    pub entries: Vec<BindEntry>,
40}
41
42impl BindMessage {
43    /// Create a new BIND message
44    pub fn new(entries: Vec<BindEntry>) -> Self {
45        BindMessage { entries }
46    }
47
48    /// Create an empty BIND message
49    pub fn empty() -> Self {
50        BindMessage { entries: Vec::new() }
51    }
52
53    /// Add a child message entry
54    pub fn add_entry(&mut self, entry: BindEntry) {
55        self.entries.push(entry);
56    }
57
58    /// Add a child message by type and name
59    pub fn add(&mut self, message_type: impl Into<String>, device_name: impl Into<String>) {
60        self.entries.push(BindEntry::new(message_type, device_name));
61    }
62
63    /// Get number of child messages
64    pub fn len(&self) -> usize {
65        self.entries.len()
66    }
67
68    /// Check if message has no children
69    pub fn is_empty(&self) -> bool {
70        self.entries.is_empty()
71    }
72}
73
74impl Message for BindMessage {
75    fn message_type() -> &'static str {
76        "BIND"
77    }
78
79    fn encode_content(&self) -> Result<Vec<u8>> {
80        let mut buf = Vec::with_capacity(self.entries.len() * 32);
81
82        for entry in &self.entries {
83            // Encode TYPE (char[12])
84            let mut type_bytes = [0u8; 12];
85            let type_str = entry.message_type.as_bytes();
86            let copy_len = type_str.len().min(12);
87            type_bytes[..copy_len].copy_from_slice(&type_str[..copy_len]);
88            buf.extend_from_slice(&type_bytes);
89
90            // Encode NAME (char[20])
91            let mut name_bytes = [0u8; 20];
92            let name_str = entry.device_name.as_bytes();
93            let copy_len = name_str.len().min(20);
94            name_bytes[..copy_len].copy_from_slice(&name_str[..copy_len]);
95            buf.extend_from_slice(&name_bytes);
96        }
97
98        Ok(buf)
99    }
100
101    fn decode_content(mut data: &[u8]) -> Result<Self> {
102        let mut entries = Vec::new();
103
104        while data.len() >= 32 {
105            // Decode TYPE (char[12])
106            let type_bytes = &data[..12];
107            data.advance(12);
108            let type_len = type_bytes.iter().position(|&b| b == 0).unwrap_or(12);
109            let message_type = String::from_utf8(type_bytes[..type_len].to_vec())?;
110
111            // Decode NAME (char[20])
112            let name_bytes = &data[..20];
113            data.advance(20);
114            let name_len = name_bytes.iter().position(|&b| b == 0).unwrap_or(20);
115            let device_name = String::from_utf8(name_bytes[..name_len].to_vec())?;
116
117            entries.push(BindEntry {
118                message_type,
119                device_name,
120            });
121        }
122
123        if !data.is_empty() {
124            return Err(IgtlError::InvalidSize {
125                expected: 0,
126                actual: data.len(),
127            });
128        }
129
130        Ok(BindMessage { entries })
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    #[test]
139    fn test_message_type() {
140        assert_eq!(BindMessage::message_type(), "BIND");
141    }
142
143    #[test]
144    fn test_empty() {
145        let msg = BindMessage::empty();
146        assert!(msg.is_empty());
147        assert_eq!(msg.len(), 0);
148    }
149
150    #[test]
151    fn test_new_entry() {
152        let entry = BindEntry::new("TRANSFORM", "Device1");
153        assert_eq!(entry.message_type, "TRANSFORM");
154        assert_eq!(entry.device_name, "Device1");
155    }
156
157    #[test]
158    fn test_add_entry() {
159        let mut msg = BindMessage::empty();
160        msg.add_entry(BindEntry::new("STATUS", "Device2"));
161        assert_eq!(msg.len(), 1);
162    }
163
164    #[test]
165    fn test_add() {
166        let mut msg = BindMessage::empty();
167        msg.add("TRANSFORM", "Device1");
168        msg.add("STATUS", "Device2");
169        assert_eq!(msg.len(), 2);
170    }
171
172    #[test]
173    fn test_encode_single() {
174        let msg = BindMessage::new(vec![
175            BindEntry::new("TRANSFORM", "Device1"),
176        ]);
177        let encoded = msg.encode_content().unwrap();
178
179        // Each entry is 32 bytes
180        assert_eq!(encoded.len(), 32);
181    }
182
183    #[test]
184    fn test_encode_multiple() {
185        let msg = BindMessage::new(vec![
186            BindEntry::new("TRANSFORM", "Device1"),
187            BindEntry::new("STATUS", "Device2"),
188            BindEntry::new("POSITION", "Device3"),
189        ]);
190        let encoded = msg.encode_content().unwrap();
191
192        assert_eq!(encoded.len(), 96); // 3 * 32
193    }
194
195    #[test]
196    fn test_roundtrip_single() {
197        let original = BindMessage::new(vec![
198            BindEntry::new("TRANSFORM", "SurgicalTool"),
199        ]);
200
201        let encoded = original.encode_content().unwrap();
202        let decoded = BindMessage::decode_content(&encoded).unwrap();
203
204        assert_eq!(decoded.entries.len(), 1);
205        assert_eq!(decoded.entries[0].message_type, "TRANSFORM");
206        assert_eq!(decoded.entries[0].device_name, "SurgicalTool");
207    }
208
209    #[test]
210    fn test_roundtrip_multiple() {
211        let original = BindMessage::new(vec![
212            BindEntry::new("TRANSFORM", "Device1"),
213            BindEntry::new("STATUS", "Device2"),
214            BindEntry::new("POSITION", "Device3"),
215            BindEntry::new("SENSOR", "Device4"),
216        ]);
217
218        let encoded = original.encode_content().unwrap();
219        let decoded = BindMessage::decode_content(&encoded).unwrap();
220
221        assert_eq!(decoded.entries.len(), 4);
222        assert_eq!(decoded.entries[0].message_type, "TRANSFORM");
223        assert_eq!(decoded.entries[1].message_type, "STATUS");
224        assert_eq!(decoded.entries[2].message_type, "POSITION");
225        assert_eq!(decoded.entries[3].message_type, "SENSOR");
226    }
227
228    #[test]
229    fn test_empty_message() {
230        let msg = BindMessage::empty();
231        let encoded = msg.encode_content().unwrap();
232        let decoded = BindMessage::decode_content(&encoded).unwrap();
233
234        assert!(decoded.is_empty());
235    }
236
237    #[test]
238    fn test_decode_invalid_size() {
239        let data = vec![0u8; 31]; // One byte short
240        let result = BindMessage::decode_content(&data);
241        assert!(result.is_err());
242    }
243
244    #[test]
245    fn test_long_names_truncated() {
246        let msg = BindMessage::new(vec![
247            BindEntry::new("VERYLONGMESSAGETYPE", "VERYLONGDEVICENAMEOVER20CHARS"),
248        ]);
249        let encoded = msg.encode_content().unwrap();
250        let decoded = BindMessage::decode_content(&encoded).unwrap();
251
252        // Should be truncated to 12 and 20 chars respectively
253        assert!(decoded.entries[0].message_type.len() <= 12);
254        assert!(decoded.entries[0].device_name.len() <= 20);
255    }
256}