async_osc/
message.rs

1use rosc::{OscBundle, OscMessage, OscPacket, OscType};
2
3/// Extension methods for the [`rosc::OscMessage`] type.
4pub trait OscMessageExt {
5    /// Create a new OscMessage from an address and args.
6    /// The args can either be specified as a `Vec<[OscType]`, or as a tuple of regular Rust types
7    /// that can be converted into [`OscType`].
8    fn new<T>(addr: impl ToString, args: T) -> Self
9    where
10        T: IntoOscArgs;
11
12    /// Returns `true` if the address starts with the given prefix.
13    ///
14    /// Returns `false` otherwise.
15    fn starts_with(&self, prefix: &str) -> bool;
16
17    /// Get a reference to the message in tuple form.
18    ///
19    /// This is useful for pattern matching. Example:
20    ///
21    /// ```no_run
22    /// # use async_osc::{*, prelude::*};
23    /// let message = OscMessage::new("/foo", vec![
24    ///     OscType::Float(1.0), OscType::String("bar".into())
25    /// ]);
26    ///
27    /// match message.as_tuple() {
28    ///     ("foo", &[OscType::Float(val), OscType::String(ref text)]) => {
29    ///         eprintln!("Got foo message with args: {}, {}", val, text);
30    ///     },
31    ///     _ => {}
32    /// }
33    /// ```
34    fn as_tuple(&self) -> (&str, &[OscType]);
35}
36
37impl OscMessageExt for OscMessage {
38    fn new<T>(addr: impl ToString, args: T) -> Self
39    where
40        T: IntoOscArgs,
41    {
42        let args = args.into_osc_args();
43        let addr = addr.to_string();
44        OscMessage { addr, args }
45    }
46
47    fn starts_with(&self, prefix: &str) -> bool {
48        self.addr.starts_with(prefix)
49    }
50
51    fn as_tuple(&self) -> (&str, &[OscType]) {
52        (self.addr.as_str(), &self.args[..])
53    }
54}
55
56/// Extension methods for the [`rosc::OscMessage`] type.
57pub trait OscPacketExt {
58    /// Return `Some(&message)` if the packet is 'OscPacket::Message`.
59    ///
60    /// Return None otherwise.
61    fn message(&self) -> Option<&OscMessage>;
62
63    /// Return `Some(message)` if the packet is 'OscPacket::Message`.
64    ///
65    /// Return None otherwise.
66    fn into_message(self) -> Option<OscMessage>;
67}
68
69impl OscPacketExt for OscPacket {
70    fn message(&self) -> Option<&OscMessage> {
71        match self {
72            OscPacket::Message(message) => Some(message),
73            _ => None,
74        }
75    }
76    fn into_message(self) -> Option<OscMessage> {
77        match self {
78            OscPacket::Message(message) => Some(message),
79            _ => None,
80        }
81    }
82}
83
84/// Helper trait to convert types into `Vec<[OscType]>`
85pub trait IntoOscArgs {
86    /// Convert self to OSC args.
87    fn into_osc_args(self) -> Vec<OscType>;
88}
89
90impl<T> IntoOscArgs for Vec<T>
91where
92    T: Into<OscType>,
93{
94    fn into_osc_args(self) -> Vec<OscType> {
95        let args: Vec<OscType> = self.into_iter().map(|a| a.into()).collect();
96        args
97    }
98}
99
100// We cannot implement IntoOscArgs for T because it conflicts
101// with the impl for Vec<T> above.
102// TODO: Find out if there is a solution.
103// impl<T> IntoOscArgs for T
104// where
105//     T: Into<OscType>,
106// {
107//     fn into_osc_args(self) -> Vec<OscType> {
108//         vec![self.into()]
109//     }
110// }
111
112impl<T1> IntoOscArgs for (T1,)
113where
114    T1: Into<OscType>,
115{
116    fn into_osc_args(self) -> Vec<OscType> {
117        vec![self.0.into()]
118    }
119}
120
121impl<T1, T2> IntoOscArgs for (T1, T2)
122where
123    T1: Into<OscType>,
124    T2: Into<OscType>,
125{
126    fn into_osc_args(self) -> Vec<OscType> {
127        vec![self.0.into(), self.1.into()]
128    }
129}
130
131impl<T1, T2, T3> IntoOscArgs for (T1, T2, T3)
132where
133    T1: Into<OscType>,
134    T2: Into<OscType>,
135    T3: Into<OscType>,
136{
137    fn into_osc_args(self) -> Vec<OscType> {
138        vec![self.0.into(), self.1.into(), self.2.into()]
139    }
140}
141
142impl IntoOscArgs for OscType {
143    fn into_osc_args(self) -> Vec<OscType> {
144        vec![self]
145    }
146}
147
148/// Helper trait to convert [`OscMessage`] and [`OscBundle`] into [`OscPacket`].
149pub trait IntoOscPacket {
150    /// Convert into [`OscPacket`].
151    fn into_osc_packet(self) -> OscPacket;
152}
153
154impl IntoOscPacket for OscMessage {
155    fn into_osc_packet(self) -> OscPacket {
156        OscPacket::Message(self)
157    }
158}
159
160impl IntoOscPacket for OscBundle {
161    fn into_osc_packet(self) -> OscPacket {
162        OscPacket::Bundle(self)
163    }
164}
165
166impl IntoOscPacket for OscPacket {
167    fn into_osc_packet(self) -> OscPacket {
168        self
169    }
170}
171
172impl<T> IntoOscPacket for T
173where
174    T: IntoOscMessage,
175{
176    fn into_osc_packet(self) -> OscPacket {
177        OscPacket::Message(self.into_osc_message())
178    }
179}
180
181/// Helper trait to convert a `(impl ToString, impl IntoOscArgs)` tuple into [`OscMessage`].
182pub trait IntoOscMessage {
183    /// Convert to [`OscMessage`].
184    fn into_osc_message(self) -> OscMessage;
185}
186
187impl<S, A> IntoOscMessage for (S, A)
188where
189    S: ToString,
190    A: IntoOscArgs,
191{
192    fn into_osc_message(self) -> OscMessage {
193        OscMessage::new(self.0, self.1)
194    }
195}