1#![forbid(unsafe_code)]
2#![deny(missing_docs, unreachable_pub, rust_2018_idioms)]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4#![cfg_attr(not(feature = "std"), no_std)]
5#![doc = r#"
6# osc-types10
7**⚠ Experimental / Not for production use**
8
9Defines message and bundle types for Open Sound Control 1.0.
10- Pre-release (`0.1.0-alpha`)
11- Breaking changes may occur frequently
12- `no_std` compatible (optional)
13
14## Example
15```rust
16use osc_types10::{Message, OscType};
17let msg = Message::new("/example", vec![OscType::String("abc")]);
18println!("{msg:?}");
19```
20"#]
21
22#[cfg(not(feature = "std"))]
23extern crate alloc;
24
25#[cfg(feature = "std")]
26use std::vec::Vec;
27
28#[cfg(not(feature = "std"))]
29use alloc::vec::Vec;
30
31#[derive(Debug, Clone, PartialEq)]
33pub enum OscType<'a> {
34 Int(i32),
36 Float(f32),
38 String(&'a str),
40 Blob(&'a [u8]),
42}
43
44#[derive(Debug, Clone, PartialEq)]
46pub struct Message<'a> {
47 pub address: &'a str,
49 pub args: Vec<OscType<'a>>,
51}
52
53impl<'a> Message<'a> {
54 pub fn new(address: &'a str, args: Vec<OscType<'a>>) -> Self {
56 Self { address, args }
57 }
58
59 pub fn with_strings(address: &'a str, string_args: Vec<&'a str>) -> Self {
61 let args = string_args.into_iter().map(OscType::String).collect();
62 Self::new(address, args)
63 }
64}
65
66#[derive(Debug, Clone, PartialEq)]
68pub struct Bundle<'a> {
69 pub timetag: u64,
71 pub messages: Vec<Message<'a>>,
73}
74
75impl<'a> Bundle<'a> {
76 pub fn new(timetag: u64, messages: Vec<Message<'a>>) -> Self {
78 Self { timetag, messages }
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::{Bundle, Message, OscType};
85
86 #[cfg(not(feature = "std"))]
87 use alloc::vec;
88
89 #[test]
90 fn message_new_sets_address_and_args() {
91 let msg = Message::with_strings("/test", vec!["one", "two"]);
92
93 assert_eq!(msg.address, "/test");
94 assert_eq!(
95 msg.args,
96 vec![OscType::String("one"), OscType::String("two")]
97 );
98 }
99
100 #[test]
101 fn message_equality_compares_contents() {
102 let lhs = Message::with_strings("/foo", vec!["a", "b"]);
103 let rhs = Message::with_strings("/foo", vec!["a", "b"]);
104 let different_address = Message::with_strings("/bar", vec!["a", "b"]);
105 let different_args = Message::with_strings("/foo", vec!["a"]);
106
107 assert_eq!(lhs, rhs);
108 assert_ne!(lhs, different_address);
109 assert_ne!(lhs, different_args);
110 }
111
112 #[test]
113 fn message_supports_mixed_types() {
114 let msg = Message::new(
115 "/mixed",
116 vec![
117 OscType::Int(42),
118 OscType::Float(3.14),
119 OscType::String("hello"),
120 OscType::Blob(&[0x01, 0x02, 0x03]),
121 ],
122 );
123
124 assert_eq!(msg.address, "/mixed");
125 assert_eq!(msg.args.len(), 4);
126 }
127
128 #[test]
129 fn bundle_new_sets_timetag_and_messages() {
130 let messages = vec![
131 Message::with_strings("/bundle/one", vec!["1"]),
132 Message::with_strings("/bundle/two", vec!["2"]),
133 ];
134 let bundle = Bundle::new(42, messages.clone());
135
136 assert_eq!(bundle.timetag, 42);
137 assert_eq!(bundle.messages, messages);
138 }
139
140 #[test]
141 fn bundle_equality_compares_contents() {
142 let messages = vec![
143 Message::with_strings("/bundle", vec!["a"]),
144 Message::with_strings("/bundle", vec!["b"]),
145 ];
146 let lhs = Bundle::new(1, messages.clone());
147 let rhs = Bundle::new(1, messages);
148 let different_timetag = Bundle::new(2, vec![Message::with_strings("/bundle", vec!["a"])]);
149 let different_messages = Bundle::new(1, vec![Message::with_strings("/bundle", vec!["c"])]);
150
151 assert_eq!(lhs, rhs);
152 assert_ne!(lhs, different_timetag);
153 assert_ne!(lhs, different_messages);
154 }
155}