1use crate::argument::OscArgument;
2use crate::helpers;
3use crate::helpers::OscParseError;
4
5pub struct OscMessage {
7 pub address: String,
8 pub arguments: Vec<OscArgument>
9}
10
11impl OscMessage {
12
13 pub fn new(address: impl Into<String>) -> Self {
15 Self {
16 address: address.into(),
17 arguments: Vec::new()
18 }
19 }
20
21 pub fn with_arg(mut self, arg: OscArgument) -> Self {
23 self.arguments.push(arg);
24 self
25 }
26
27 pub fn from_bytes(bytes: &[u8]) -> Result<Self, OscParseError> {
29
30 if bytes.is_empty() {
31 return Err(OscParseError::NotEnoughData);
32 }
33
34 let address_pattern_null_pos = bytes.iter().position(|&b| b == 0).ok_or(OscParseError::InvalidFormat)?;
36 let address_pattern_bytes = &bytes[0..address_pattern_null_pos];
37 let address_pattern = String::from_utf8(address_pattern_bytes.to_vec()).map_err(|_| OscParseError::InvalidString)?;
38
39 if ! address_pattern.starts_with("/") {
41 return Err(OscParseError::InvalidString);
42 }
43
44 let comma_pos = address_pattern_null_pos + bytes[address_pattern_null_pos..].iter().position(|&b| b == 0x2c).ok_or(OscParseError::InvalidFormat)?;
46 let type_tag_null_pos = comma_pos + bytes[comma_pos..].iter().position(|&b| b == 0).ok_or(OscParseError::InvalidFormat)?;
47 let type_tag_bytes = &bytes[comma_pos..type_tag_null_pos];
48 let type_tag_string = String::from_utf8(type_tag_bytes.to_vec()).map_err(|_| OscParseError::InvalidString)?;
49
50 let mut arguments = Vec::<OscArgument>::new();
51
52 let mut arguments_index: usize = (type_tag_null_pos + 1 + 3) & !3;
54
55 for tag in type_tag_string[1..].chars() {
56 let argument = OscArgument::from_bytes(bytes, &mut arguments_index, tag).map_err(|_| OscParseError::CouldNotParseArguments)?;
57 arguments.push (argument);
58 }
59
60 Ok(OscMessage { address: address_pattern, arguments })
61 }
62
63 pub fn to_string(&self) -> String {
65 let mut result = String::new();
66 result.push_str(&self.address);
67 result.push_str(" ");
68
69 for (i, arg) in self.arguments.iter().enumerate() {
70 result.push_str(&arg.to_string());
71 result.push_str( if i < self.arguments.len() - 1 { ", " } else { "" } );
72 }
73 result
74 }
75
76 pub fn to_bytes(&self) -> Vec<u8> {
78 [
79 self.address_pattern_in_bytes(),
80 self.type_tag_string_in_bytes(),
81 self.arguments_in_bytes()
82 ].concat()
83 }
84
85 fn address_pattern_in_bytes(&self) -> Vec<u8> {
86 let mut bytes: Vec<u8> = self.address.as_bytes().to_vec();
87 bytes.push(b'\0');
88 helpers::pad_to_multiple_of_4_bytes(&mut bytes);
89 bytes
90 }
91
92 fn type_tag_string_in_bytes(&self) -> Vec<u8> {
93 let mut bytes: Vec<u8> = vec![b','];
94 bytes.extend(self.arguments.iter().map(|a| a.type_tag() as u8));
95 bytes.push(b'\0');
96 helpers::pad_to_multiple_of_4_bytes(&mut bytes);
97 bytes
98 }
99
100 fn arguments_in_bytes(&self) -> Vec<u8> {
101 self.arguments.iter().flat_map(|a| a.to_bytes()).collect()
102 }
103}
104
105#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
113 fn test_construct_message1() {
114 let m1 = OscMessage {
115 address: String::from("/new/message"),
116 arguments: vec![
117 OscArgument::Int32(3),
118 OscArgument::Float32(0.4)
119 ]
120 };
121
122 assert_eq!(m1.address, "/new/message");
123 assert_eq!(m1.arguments.len(), 2);
124 assert_eq!(m1.arguments.get(0), Some(&OscArgument::Int32(3)));
125 assert_eq!(m1.arguments.get(1), Some(&OscArgument::Float32(0.4)));
126 assert_eq!(m1.arguments.get(2), None);
127 }
128
129 #[test]
131 fn test_construct_message2() {
132 let m2 = OscMessage::new("/nice");
133
134 assert_eq!(m2.address, "/nice");
135 assert_eq!(m2.arguments.len(), 0);
136 }
137
138 #[test]
140 fn test_convert_message_to_bytes1() {
141 let m1 = OscMessage {
142 address: String::from("/oscillator/4/frequency"),
143 arguments: vec![
144 OscArgument::Float32(440.0)
145 ]
146 };
147
148 let bytes = m1.to_bytes();
149
150 let expected_bytes: Vec<u8> = vec![
151 0x2f, 0x6f, 0x73, 0x63,
152 0x69, 0x6c, 0x6c, 0x61,
153 0x74, 0x6f, 0x72, 0x2f,
154 0x34, 0x2f, 0x66, 0x72,
155 0x65, 0x71, 0x75, 0x65,
156 0x6e, 0x63, 0x79, 0x00,
157 0x2c, 0x66, 0x00, 0x00,
158 0x43, 0xdc, 0x00, 0x00,
159 ];
160
161 assert_eq!(bytes, expected_bytes);
162
163 let result = OscMessage::from_bytes(&bytes);
164 assert!(result.is_ok(), "Expected OscMessage ok, got: {:?}", result.err());
165
166 let m2 = result.unwrap();
167 assert_eq!(m2.address, "/oscillator/4/frequency");
168 assert_eq!(m2.arguments.len(), 1);
169 assert_eq!(m2.arguments.get(0), Some(&OscArgument::Float32(440.0)));
170 }
171
172 #[test]
174 fn test_convert_message_to_bytes2() {
175 let m1 = OscMessage {
176 address: String::from("/foo"),
177 arguments: vec![
178 OscArgument::Int32(1000),
179 OscArgument::Int32(-1),
180 OscArgument::String("hello".to_string()),
181 OscArgument::Float32(1.234),
182 OscArgument::Float32(5.678)
183 ]
184 };
185
186 let bytes = m1.to_bytes();
187
188 let expected_bytes: Vec<u8> = vec![
189 0x2f, 0x66, 0x6f, 0x6f,
190 0x00, 0x00, 0x00, 0x00,
191 0x2c, 0x69, 0x69, 0x73,
192 0x66, 0x66, 0x00, 0x00,
193 0x00, 0x00, 0x03, 0xe8,
194 0xff, 0xff, 0xff, 0xff,
195 0x68, 0x65, 0x6c, 0x6c,
196 0x6f, 0x00, 0x00, 0x00,
197 0x3f, 0x9d, 0xf3, 0xb6,
198 0x40, 0xb5, 0xb2, 0x2d,
199 ];
200
201 assert_eq!(bytes, expected_bytes);
202
203 let result = OscMessage::from_bytes(&bytes);
204 assert!(result.is_ok(), "Expected OscMessage ok, got: {:?}", result.err());
205
206 let m2 = result.unwrap();
207 assert_eq!(m2.address, "/foo");
208 assert_eq!(m2.arguments.len(), 5);
209 assert_eq!(m2.arguments.get(0), Some(&OscArgument::Int32(1000)));
210 assert_eq!(m2.arguments.get(1), Some(&OscArgument::Int32(-1)));
211 assert_eq!(m2.arguments.get(2), Some(&OscArgument::String("hello".to_string())));
212 assert_eq!(m2.arguments.get(3), Some(&OscArgument::Float32(1.234)));
213 assert_eq!(m2.arguments.get(4), Some(&OscArgument::Float32(5.678)));
214 }
215
216 #[test]
217 fn test_empty_arguments() {
218 let msg = OscMessage::new("/test");
219 let bytes = msg.to_bytes();
220 let decoded = OscMessage::from_bytes(&bytes).unwrap();
221 assert_eq!(decoded.address, "/test");
222 assert_eq!(decoded.arguments.len(), 0);
223 }
224
225 #[test]
226 fn test_invalid_address_no_slash() {
227 let bytes = b"invalid\0\0\0\0,\0\0\0";
228 assert!(matches!(
229 OscMessage::from_bytes(bytes),
230 Err(OscParseError::InvalidString)
231 ));
232 }
233
234 #[test]
235 fn test_truncated_message() {
236 let bytes = b"/test\0\0\0,i"; assert!(OscMessage::from_bytes(bytes).is_err());
238 }
239
240 #[test]
241 fn test_to_string() {
242 let msg = OscMessage {
243 address: "/example".to_string(),
244 arguments: vec![
245 OscArgument::Int32(42),
246 OscArgument::Float32(3.14),
247 OscArgument::String("hello".to_string()),
248 OscArgument::Blob(vec![0x01, 0x02, 0x03])
249 ],
250 };
251 let msg_str = msg.to_string();
252 assert_eq!(msg_str, "/example 42, 3.14, \"hello\", Blob(3 bytes)");
253 }
254}