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::protocol::message::Message;
7use crate::error::{IgtlError, Result};
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("Missing null terminator in capability type".to_string())
86                })?;
87
88            // Extract type name
89            let type_bytes = &remaining[pos..pos + end];
90            let type_name = String::from_utf8(type_bytes.to_vec())?;
91            types.push(type_name);
92
93            pos += end + 1; // +1 for null terminator
94        }
95
96        Ok(CapabilityMessage { types })
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn test_message_type() {
106        assert_eq!(CapabilityMessage::message_type(), "CAPABILITY");
107    }
108
109    #[test]
110    fn test_empty_capability() {
111        let capability = CapabilityMessage::empty();
112        assert_eq!(capability.types.len(), 0);
113    }
114
115    #[test]
116    fn test_new_capability() {
117        let types = vec!["TRANSFORM".to_string(), "IMAGE".to_string()];
118        let capability = CapabilityMessage::new(types.clone());
119        assert_eq!(capability.types, types);
120    }
121
122    #[test]
123    fn test_capability_roundtrip() {
124        let original = CapabilityMessage {
125            types: vec![
126                "TRANSFORM".to_string(),
127                "IMAGE".to_string(),
128                "STATUS".to_string(),
129            ],
130        };
131
132        let encoded = original.encode_content().unwrap();
133        let decoded = CapabilityMessage::decode_content(&encoded).unwrap();
134
135        assert_eq!(original.types, decoded.types);
136    }
137
138    #[test]
139    fn test_empty_list() {
140        let capability = CapabilityMessage { types: Vec::new() };
141
142        let encoded = capability.encode_content().unwrap();
143        assert_eq!(encoded.len(), 4); // Just the count field
144
145        let decoded = CapabilityMessage::decode_content(&encoded).unwrap();
146        assert_eq!(decoded.types.len(), 0);
147    }
148
149    #[test]
150    fn test_single_type() {
151        let capability = CapabilityMessage {
152            types: vec!["TRANSFORM".to_string()],
153        };
154
155        let encoded = capability.encode_content().unwrap();
156        let decoded = CapabilityMessage::decode_content(&encoded).unwrap();
157
158        assert_eq!(decoded.types.len(), 1);
159        assert_eq!(decoded.types[0], "TRANSFORM");
160    }
161
162    #[test]
163    fn test_multiple_types() {
164        let capability = CapabilityMessage {
165            types: vec![
166                "TRANSFORM".to_string(),
167                "IMAGE".to_string(),
168                "STATUS".to_string(),
169                "POSITION".to_string(),
170                "CAPABILITY".to_string(),
171            ],
172        };
173
174        let encoded = capability.encode_content().unwrap();
175        let decoded = CapabilityMessage::decode_content(&encoded).unwrap();
176
177        assert_eq!(decoded.types, capability.types);
178    }
179
180    #[test]
181    fn test_null_termination() {
182        let capability = CapabilityMessage {
183            types: vec!["TEST".to_string()],
184        };
185
186        let encoded = capability.encode_content().unwrap();
187
188        // Should have: 4 bytes (count) + 4 bytes ("TEST") + 1 byte (null) = 9 bytes
189        assert_eq!(encoded.len(), 9);
190
191        // Check null terminator
192        assert_eq!(encoded[8], 0);
193    }
194
195    #[test]
196    fn test_big_endian_count() {
197        let capability = CapabilityMessage {
198            types: vec!["A".to_string(), "B".to_string()],
199        };
200
201        let encoded = capability.encode_content().unwrap();
202
203        // Verify big-endian encoding of count (2)
204        assert_eq!(encoded[0], 0x00);
205        assert_eq!(encoded[1], 0x00);
206        assert_eq!(encoded[2], 0x00);
207        assert_eq!(encoded[3], 0x02);
208    }
209
210    #[test]
211    fn test_decode_invalid_size() {
212        let short_data = vec![0u8; 2];
213        let result = CapabilityMessage::decode_content(&short_data);
214        assert!(matches!(result, Err(IgtlError::InvalidSize { .. })));
215    }
216
217    #[test]
218    fn test_decode_missing_null_terminator() {
219        let mut data = Vec::new();
220        data.extend_from_slice(&1u32.to_be_bytes()); // count = 1
221        data.extend_from_slice(b"TEST"); // no null terminator
222
223        let result = CapabilityMessage::decode_content(&data);
224        assert!(matches!(result, Err(IgtlError::InvalidHeader(_))));
225    }
226
227    #[test]
228    fn test_decode_unexpected_end() {
229        let mut data = Vec::new();
230        data.extend_from_slice(&2u32.to_be_bytes()); // count = 2
231        data.extend_from_slice(b"TEST\0"); // only one type
232
233        let result = CapabilityMessage::decode_content(&data);
234        assert!(matches!(result, Err(IgtlError::InvalidHeader(_))));
235    }
236}