openigtlink_rust/protocol/types/
bind.rs1use crate::error::{IgtlError, Result};
7use crate::protocol::message::Message;
8use bytes::Buf;
9
10#[derive(Debug, Clone, PartialEq)]
12pub struct BindEntry {
13 pub message_type: String,
15 pub device_name: String,
17}
18
19impl BindEntry {
20 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#[derive(Debug, Clone, PartialEq)]
37pub struct BindMessage {
38 pub entries: Vec<BindEntry>,
40}
41
42impl BindMessage {
43 pub fn new(entries: Vec<BindEntry>) -> Self {
45 BindMessage { entries }
46 }
47
48 pub fn empty() -> Self {
50 BindMessage {
51 entries: Vec::new(),
52 }
53 }
54
55 pub fn add_entry(&mut self, entry: BindEntry) {
57 self.entries.push(entry);
58 }
59
60 pub fn add(&mut self, message_type: impl Into<String>, device_name: impl Into<String>) {
62 self.entries.push(BindEntry::new(message_type, device_name));
63 }
64
65 pub fn len(&self) -> usize {
67 self.entries.len()
68 }
69
70 pub fn is_empty(&self) -> bool {
72 self.entries.is_empty()
73 }
74}
75
76impl Message for BindMessage {
77 fn message_type() -> &'static str {
78 "BIND"
79 }
80
81 fn encode_content(&self) -> Result<Vec<u8>> {
82 let mut buf = Vec::with_capacity(self.entries.len() * 32);
83
84 for entry in &self.entries {
85 let mut type_bytes = [0u8; 12];
87 let type_str = entry.message_type.as_bytes();
88 let copy_len = type_str.len().min(12);
89 type_bytes[..copy_len].copy_from_slice(&type_str[..copy_len]);
90 buf.extend_from_slice(&type_bytes);
91
92 let mut name_bytes = [0u8; 20];
94 let name_str = entry.device_name.as_bytes();
95 let copy_len = name_str.len().min(20);
96 name_bytes[..copy_len].copy_from_slice(&name_str[..copy_len]);
97 buf.extend_from_slice(&name_bytes);
98 }
99
100 Ok(buf)
101 }
102
103 fn decode_content(mut data: &[u8]) -> Result<Self> {
104 let mut entries = Vec::new();
105
106 while data.len() >= 32 {
107 let type_bytes = &data[..12];
109 data.advance(12);
110 let type_len = type_bytes.iter().position(|&b| b == 0).unwrap_or(12);
111 let message_type = String::from_utf8(type_bytes[..type_len].to_vec())?;
112
113 let name_bytes = &data[..20];
115 data.advance(20);
116 let name_len = name_bytes.iter().position(|&b| b == 0).unwrap_or(20);
117 let device_name = String::from_utf8(name_bytes[..name_len].to_vec())?;
118
119 entries.push(BindEntry {
120 message_type,
121 device_name,
122 });
123 }
124
125 if !data.is_empty() {
126 return Err(IgtlError::InvalidSize {
127 expected: 0,
128 actual: data.len(),
129 });
130 }
131
132 Ok(BindMessage { entries })
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn test_message_type() {
142 assert_eq!(BindMessage::message_type(), "BIND");
143 }
144
145 #[test]
146 fn test_empty() {
147 let msg = BindMessage::empty();
148 assert!(msg.is_empty());
149 assert_eq!(msg.len(), 0);
150 }
151
152 #[test]
153 fn test_new_entry() {
154 let entry = BindEntry::new("TRANSFORM", "Device1");
155 assert_eq!(entry.message_type, "TRANSFORM");
156 assert_eq!(entry.device_name, "Device1");
157 }
158
159 #[test]
160 fn test_add_entry() {
161 let mut msg = BindMessage::empty();
162 msg.add_entry(BindEntry::new("STATUS", "Device2"));
163 assert_eq!(msg.len(), 1);
164 }
165
166 #[test]
167 fn test_add() {
168 let mut msg = BindMessage::empty();
169 msg.add("TRANSFORM", "Device1");
170 msg.add("STATUS", "Device2");
171 assert_eq!(msg.len(), 2);
172 }
173
174 #[test]
175 fn test_encode_single() {
176 let msg = BindMessage::new(vec![BindEntry::new("TRANSFORM", "Device1")]);
177 let encoded = msg.encode_content().unwrap();
178
179 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); }
194
195 #[test]
196 fn test_roundtrip_single() {
197 let original = BindMessage::new(vec![BindEntry::new("TRANSFORM", "SurgicalTool")]);
198
199 let encoded = original.encode_content().unwrap();
200 let decoded = BindMessage::decode_content(&encoded).unwrap();
201
202 assert_eq!(decoded.entries.len(), 1);
203 assert_eq!(decoded.entries[0].message_type, "TRANSFORM");
204 assert_eq!(decoded.entries[0].device_name, "SurgicalTool");
205 }
206
207 #[test]
208 fn test_roundtrip_multiple() {
209 let original = BindMessage::new(vec![
210 BindEntry::new("TRANSFORM", "Device1"),
211 BindEntry::new("STATUS", "Device2"),
212 BindEntry::new("POSITION", "Device3"),
213 BindEntry::new("SENSOR", "Device4"),
214 ]);
215
216 let encoded = original.encode_content().unwrap();
217 let decoded = BindMessage::decode_content(&encoded).unwrap();
218
219 assert_eq!(decoded.entries.len(), 4);
220 assert_eq!(decoded.entries[0].message_type, "TRANSFORM");
221 assert_eq!(decoded.entries[1].message_type, "STATUS");
222 assert_eq!(decoded.entries[2].message_type, "POSITION");
223 assert_eq!(decoded.entries[3].message_type, "SENSOR");
224 }
225
226 #[test]
227 fn test_empty_message() {
228 let msg = BindMessage::empty();
229 let encoded = msg.encode_content().unwrap();
230 let decoded = BindMessage::decode_content(&encoded).unwrap();
231
232 assert!(decoded.is_empty());
233 }
234
235 #[test]
236 fn test_decode_invalid_size() {
237 let data = vec![0u8; 31]; let result = BindMessage::decode_content(&data);
239 assert!(result.is_err());
240 }
241
242 #[test]
243 fn test_long_names_truncated() {
244 let msg = BindMessage::new(vec![BindEntry::new(
245 "VERYLONGMESSAGETYPE",
246 "VERYLONGDEVICENAMEOVER20CHARS",
247 )]);
248 let encoded = msg.encode_content().unwrap();
249 let decoded = BindMessage::decode_content(&encoded).unwrap();
250
251 assert!(decoded.entries[0].message_type.len() <= 12);
253 assert!(decoded.entries[0].device_name.len() <= 20);
254 }
255}