openigtlink_rust/protocol/types/
lbmeta.rs1use crate::error::{IgtlError, Result};
7use crate::protocol::message::Message;
8use bytes::{Buf, BufMut};
9
10#[derive(Debug, Clone, PartialEq)]
12pub struct LabelMetaElement {
13 pub name: String,
15 pub id: String,
17 pub label: u8,
19 pub rgba: [u8; 4],
21 pub size: [u16; 3],
23 pub owner: String,
25}
26
27impl LabelMetaElement {
28 pub fn new(name: impl Into<String>, id: impl Into<String>, label: u8) -> Self {
30 LabelMetaElement {
31 name: name.into(),
32 id: id.into(),
33 label,
34 rgba: [255, 255, 255, 255], size: [0, 0, 0],
36 owner: String::new(),
37 }
38 }
39
40 pub fn with_rgba(mut self, rgba: [u8; 4]) -> Self {
42 self.rgba = rgba;
43 self
44 }
45
46 pub fn with_size(mut self, size: [u16; 3]) -> Self {
48 self.size = size;
49 self
50 }
51
52 pub fn with_owner(mut self, owner: impl Into<String>) -> Self {
54 self.owner = owner.into();
55 self
56 }
57}
58
59#[derive(Debug, Clone, PartialEq)]
66pub struct LbMetaMessage {
67 pub labels: Vec<LabelMetaElement>,
69}
70
71impl LbMetaMessage {
72 pub fn new(labels: Vec<LabelMetaElement>) -> Self {
74 LbMetaMessage { labels }
75 }
76
77 pub fn empty() -> Self {
79 LbMetaMessage { labels: Vec::new() }
80 }
81
82 pub fn add_label(&mut self, label: LabelMetaElement) {
84 self.labels.push(label);
85 }
86
87 pub fn len(&self) -> usize {
89 self.labels.len()
90 }
91
92 pub fn is_empty(&self) -> bool {
94 self.labels.is_empty()
95 }
96}
97
98impl Message for LbMetaMessage {
99 fn message_type() -> &'static str {
100 "LBMETA"
101 }
102
103 fn encode_content(&self) -> Result<Vec<u8>> {
104 let mut buf = Vec::with_capacity(self.labels.len() * 116);
105
106 for label in &self.labels {
107 let mut name_bytes = [0u8; 64];
109 let name_str = label.name.as_bytes();
110 let copy_len = name_str.len().min(63);
111 name_bytes[..copy_len].copy_from_slice(&name_str[..copy_len]);
112 buf.extend_from_slice(&name_bytes);
113
114 let mut id_bytes = [0u8; 20];
116 let id_str = label.id.as_bytes();
117 let copy_len = id_str.len().min(19);
118 id_bytes[..copy_len].copy_from_slice(&id_str[..copy_len]);
119 buf.extend_from_slice(&id_bytes);
120
121 buf.put_u8(label.label);
123
124 buf.put_u8(0);
126
127 buf.extend_from_slice(&label.rgba);
129
130 for &s in &label.size {
132 buf.put_u16(s);
133 }
134
135 let mut owner_bytes = [0u8; 20];
137 let owner_str = label.owner.as_bytes();
138 let copy_len = owner_str.len().min(19);
139 owner_bytes[..copy_len].copy_from_slice(&owner_str[..copy_len]);
140 buf.extend_from_slice(&owner_bytes);
141 }
142
143 Ok(buf)
144 }
145
146 fn decode_content(mut data: &[u8]) -> Result<Self> {
147 let mut labels = Vec::new();
148
149 while data.len() >= 116 {
150 let name_bytes = &data[..64];
152 data.advance(64);
153 let name_len = name_bytes.iter().position(|&b| b == 0).unwrap_or(64);
154 let name = String::from_utf8(name_bytes[..name_len].to_vec())?;
155
156 let id_bytes = &data[..20];
158 data.advance(20);
159 let id_len = id_bytes.iter().position(|&b| b == 0).unwrap_or(20);
160 let id = String::from_utf8(id_bytes[..id_len].to_vec())?;
161
162 let label = data.get_u8();
164
165 let _reserved = data.get_u8();
167
168 let rgba = [data.get_u8(), data.get_u8(), data.get_u8(), data.get_u8()];
170
171 let size = [data.get_u16(), data.get_u16(), data.get_u16()];
173
174 let owner_bytes = &data[..20];
176 data.advance(20);
177 let owner_len = owner_bytes.iter().position(|&b| b == 0).unwrap_or(20);
178 let owner = String::from_utf8(owner_bytes[..owner_len].to_vec())?;
179
180 labels.push(LabelMetaElement {
181 name,
182 id,
183 label,
184 rgba,
185 size,
186 owner,
187 });
188 }
189
190 if !data.is_empty() {
191 return Err(IgtlError::InvalidSize {
192 expected: 0,
193 actual: data.len(),
194 });
195 }
196
197 Ok(LbMetaMessage { labels })
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[test]
206 fn test_message_type() {
207 assert_eq!(LbMetaMessage::message_type(), "LBMETA");
208 }
209
210 #[test]
211 fn test_empty() {
212 let msg = LbMetaMessage::empty();
213 assert!(msg.is_empty());
214 assert_eq!(msg.len(), 0);
215 }
216
217 #[test]
218 fn test_new() {
219 let elem = LabelMetaElement::new("Liver", "LBL001", 1);
220 assert_eq!(elem.name, "Liver");
221 assert_eq!(elem.id, "LBL001");
222 assert_eq!(elem.label, 1);
223 assert_eq!(elem.rgba, [255, 255, 255, 255]);
224 }
225
226 #[test]
227 fn test_with_rgba() {
228 let elem = LabelMetaElement::new("Heart", "LBL002", 2).with_rgba([255, 0, 0, 255]);
229 assert_eq!(elem.rgba, [255, 0, 0, 255]);
230 }
231
232 #[test]
233 fn test_with_size() {
234 let elem = LabelMetaElement::new("Kidney", "LBL003", 3).with_size([256, 256, 100]);
235 assert_eq!(elem.size, [256, 256, 100]);
236 }
237
238 #[test]
239 fn test_add_label() {
240 let mut msg = LbMetaMessage::empty();
241 msg.add_label(LabelMetaElement::new("Liver", "LBL001", 1));
242 assert_eq!(msg.len(), 1);
243 }
244
245 #[test]
246 fn test_encode_single() {
247 let elem = LabelMetaElement::new("TestLabel", "TEST001", 1);
248 let msg = LbMetaMessage::new(vec![elem]);
249 let encoded = msg.encode_content().unwrap();
250
251 assert_eq!(encoded.len(), 116);
252 }
253
254 #[test]
255 fn test_roundtrip() {
256 let original = LbMetaMessage::new(vec![LabelMetaElement::new("Liver", "LBL001", 1)
257 .with_rgba([139, 69, 19, 255])
258 .with_size([512, 512, 200])
259 .with_owner("CT001")]);
260
261 let encoded = original.encode_content().unwrap();
262 let decoded = LbMetaMessage::decode_content(&encoded).unwrap();
263
264 assert_eq!(decoded.labels.len(), 1);
265 assert_eq!(decoded.labels[0].name, "Liver");
266 assert_eq!(decoded.labels[0].id, "LBL001");
267 assert_eq!(decoded.labels[0].label, 1);
268 assert_eq!(decoded.labels[0].rgba, [139, 69, 19, 255]);
269 assert_eq!(decoded.labels[0].size, [512, 512, 200]);
270 assert_eq!(decoded.labels[0].owner, "CT001");
271 }
272
273 #[test]
274 fn test_roundtrip_multiple() {
275 let original = LbMetaMessage::new(vec![
276 LabelMetaElement::new("Liver", "LBL001", 1).with_rgba([139, 69, 19, 255]),
277 LabelMetaElement::new("Heart", "LBL002", 2).with_rgba([255, 0, 0, 255]),
278 LabelMetaElement::new("Kidney", "LBL003", 3).with_rgba([0, 255, 0, 255]),
279 ]);
280
281 let encoded = original.encode_content().unwrap();
282 let decoded = LbMetaMessage::decode_content(&encoded).unwrap();
283
284 assert_eq!(decoded.labels.len(), 3);
285 assert_eq!(decoded.labels[0].name, "Liver");
286 assert_eq!(decoded.labels[1].name, "Heart");
287 assert_eq!(decoded.labels[2].name, "Kidney");
288 }
289
290 #[test]
291 fn test_empty_message() {
292 let msg = LbMetaMessage::empty();
293 let encoded = msg.encode_content().unwrap();
294 let decoded = LbMetaMessage::decode_content(&encoded).unwrap();
295
296 assert_eq!(decoded.labels.len(), 0);
297 }
298
299 #[test]
300 fn test_decode_invalid_size() {
301 let data = vec![0u8; 115]; let result = LbMetaMessage::decode_content(&data);
303 assert!(result.is_err());
304 }
305}