openigtlink_rust/protocol/types/
capability.rs

1//! CAPABILITY message type implementation
2//!
3//! The CAPABILITY message type is used to notify the receiver about
4//! the types of messages supported by the sender.
5
6use crate::error::{IgtlError, Result};
7use crate::protocol::message::Message;
8use bytes::{Buf, BufMut};
9
10/// CAPABILITY message containing list of supported message types
11///
12/// # OpenIGTLink Specification
13/// - Message type: "CAPABILITY"
14/// - Body size: Variable (4 bytes + sum of type name lengths + null terminators)
15/// - Encoding:
16///   - Number of types: u32 (4 bytes, big-endian)
17///   - Type names: null-terminated strings
18#[derive(Debug, Clone, PartialEq)]
19pub struct CapabilityMessage {
20    /// List of supported message type names
21    pub types: Vec<String>,
22}
23
24impl CapabilityMessage {
25    /// Create a new CAPABILITY message with the given type list
26    pub fn new(types: Vec<String>) -> Self {
27        CapabilityMessage { types }
28    }
29
30    /// Create an empty CAPABILITY message
31    pub fn empty() -> Self {
32        CapabilityMessage { types: Vec::new() }
33    }
34}
35
36impl Message for CapabilityMessage {
37    fn message_type() -> &'static str {
38        "CAPABILITY"
39    }
40
41    fn encode_content(&self) -> Result<Vec<u8>> {
42        let mut buf = Vec::new();
43
44        // Encode number of types (4 bytes, big-endian)
45        buf.put_u32(self.types.len() as u32);
46
47        // Encode each type name (null-terminated)
48        for type_name in &self.types {
49            buf.extend_from_slice(type_name.as_bytes());
50            buf.put_u8(0); // null terminator
51        }
52
53        Ok(buf)
54    }
55
56    fn decode_content(data: &[u8]) -> Result<Self> {
57        if data.len() < 4 {
58            return Err(IgtlError::InvalidSize {
59                expected: 4,
60                actual: data.len(),
61            });
62        }
63
64        let mut cursor = std::io::Cursor::new(data);
65
66        // Decode number of types (4 bytes, big-endian)
67        let count = cursor.get_u32() as usize;
68
69        let mut types = Vec::with_capacity(count);
70        let remaining = &data[cursor.position() as usize..];
71        let mut pos = 0;
72
73        for _ in 0..count {
74            if pos >= remaining.len() {
75                return Err(IgtlError::InvalidHeader(
76                    "Unexpected end of data while decoding capability types".to_string(),
77                ));
78            }
79
80            // Find null terminator
81            let end = remaining[pos..]
82                .iter()
83                .position(|&b| b == 0)
84                .ok_or_else(|| {
85                    IgtlError::InvalidHeader(
86                        "Missing null terminator in capability type".to_string(),
87                    )
88                })?;
89
90            // Extract type name
91            let type_bytes = &remaining[pos..pos + end];
92            let type_name = String::from_utf8(type_bytes.to_vec())?;
93            types.push(type_name);
94
95            pos += end + 1; // +1 for null terminator
96        }
97
98        Ok(CapabilityMessage { types })
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn test_message_type() {
108        assert_eq!(CapabilityMessage::message_type(), "CAPABILITY");
109    }
110
111    #[test]
112    fn test_empty_capability() {
113        let capability = CapabilityMessage::empty();
114        assert_eq!(capability.types.len(), 0);
115    }
116
117    #[test]
118    fn test_new_capability() {
119        let types = vec!["TRANSFORM".to_string(), "IMAGE".to_string()];
120        let capability = CapabilityMessage::new(types.clone());
121        assert_eq!(capability.types, types);
122    }
123
124    #[test]
125    fn test_capability_roundtrip() {
126        let original = CapabilityMessage {
127            types: vec![
128                "TRANSFORM".to_string(),
129                "IMAGE".to_string(),
130                "STATUS".to_string(),
131            ],
132        };
133
134        let encoded = original.encode_content().unwrap();
135        let decoded = CapabilityMessage::decode_content(&encoded).unwrap();
136
137        assert_eq!(original.types, decoded.types);
138    }
139
140    #[test]
141    fn test_empty_list() {
142        let capability = CapabilityMessage { types: Vec::new() };
143
144        let encoded = capability.encode_content().unwrap();
145        assert_eq!(encoded.len(), 4); // Just the count field
146
147        let decoded = CapabilityMessage::decode_content(&encoded).unwrap();
148        assert_eq!(decoded.types.len(), 0);
149    }
150
151    #[test]
152    fn test_single_type() {
153        let capability = CapabilityMessage {
154            types: vec!["TRANSFORM".to_string()],
155        };
156
157        let encoded = capability.encode_content().unwrap();
158        let decoded = CapabilityMessage::decode_content(&encoded).unwrap();
159
160        assert_eq!(decoded.types.len(), 1);
161        assert_eq!(decoded.types[0], "TRANSFORM");
162    }
163
164    #[test]
165    fn test_multiple_types() {
166        let capability = CapabilityMessage {
167            types: vec![
168                "TRANSFORM".to_string(),
169                "IMAGE".to_string(),
170                "STATUS".to_string(),
171                "POSITION".to_string(),
172                "CAPABILITY".to_string(),
173            ],
174        };
175
176        let encoded = capability.encode_content().unwrap();
177        let decoded = CapabilityMessage::decode_content(&encoded).unwrap();
178
179        assert_eq!(decoded.types, capability.types);
180    }
181
182    #[test]
183    fn test_null_termination() {
184        let capability = CapabilityMessage {
185            types: vec!["TEST".to_string()],
186        };
187
188        let encoded = capability.encode_content().unwrap();
189
190        // Should have: 4 bytes (count) + 4 bytes ("TEST") + 1 byte (null) = 9 bytes
191        assert_eq!(encoded.len(), 9);
192
193        // Check null terminator
194        assert_eq!(encoded[8], 0);
195    }
196
197    #[test]
198    fn test_big_endian_count() {
199        let capability = CapabilityMessage {
200            types: vec!["A".to_string(), "B".to_string()],
201        };
202
203        let encoded = capability.encode_content().unwrap();
204
205        // Verify big-endian encoding of count (2)
206        assert_eq!(encoded[0], 0x00);
207        assert_eq!(encoded[1], 0x00);
208        assert_eq!(encoded[2], 0x00);
209        assert_eq!(encoded[3], 0x02);
210    }
211
212    #[test]
213    fn test_decode_invalid_size() {
214        let short_data = vec![0u8; 2];
215        let result = CapabilityMessage::decode_content(&short_data);
216        assert!(matches!(result, Err(IgtlError::InvalidSize { .. })));
217    }
218
219    #[test]
220    fn test_decode_missing_null_terminator() {
221        let mut data = Vec::new();
222        data.extend_from_slice(&1u32.to_be_bytes()); // count = 1
223        data.extend_from_slice(b"TEST"); // no null terminator
224
225        let result = CapabilityMessage::decode_content(&data);
226        assert!(matches!(result, Err(IgtlError::InvalidHeader(_))));
227    }
228
229    #[test]
230    fn test_decode_unexpected_end() {
231        let mut data = Vec::new();
232        data.extend_from_slice(&2u32.to_be_bytes()); // count = 2
233        data.extend_from_slice(b"TEST\0"); // only one type
234
235        let result = CapabilityMessage::decode_content(&data);
236        assert!(matches!(result, Err(IgtlError::InvalidHeader(_))));
237    }
238}