Expand description
This crate implements #[derive(OscMessage)]
for the osc_address crate.
For general usage of the OscMessage
trait, refer to the osc_address
documentation.
§Example
Here’s an example of how this crate might be used alongside osc_address
and serde_osc (for deserialization of OSC packets from [u8]
).
This example can be run by cargo run --example basic
#[macro_use]
extern crate osc_address_derive;
extern crate osc_address;
extern crate serde_osc;
#[derive(OscMessage)]
pub enum OscToplevel {
// Any message addressed to /routegraph[/...] will be deserialized into
// an OscRouteGraph type (defined below).
#[osc_address(address="routegraph")]
RouteGraph((), OscRouteGraph),
// Messages address to /renderer[/...] are deserialized into OscRenderer
// types.
#[osc_address(address="renderer")]
Renderer((), OscRenderer),
}
/// OSC message to /routegraph[/...]
#[derive(OscMessage)]
pub enum OscRouteGraph {
// Messages to /routegraph/add_node expect one i32 argument.
#[osc_address(address="add_node")]
AddNode((), (i32,)),
// Messages to /routegraph/add_node expect two i32 arguments.
#[osc_address(address="add_edge")]
AddEdge((), (i32, i32)),
}
/// OSC message to /renderer[/...]
#[derive(OscMessage)]
pub enum OscRenderer {
// Messages to /renderer/new expect one i32 argument ...
#[osc_address(address="new")]
New((), (i32,)),
#[osc_address(address="del")]
Del((), (i32,)),
// Match messages to /renderer/<u32>[/...]
ById(u32, OscRendererById),
}
/// OSC message to /renderer/<u32>[/...]
#[derive(OscMessage)]
pub enum OscRendererById {
// Messages to /renderer/<u32>/say expect one string argument
#[osc_address(address="say")]
Say((), (String,)),
}
fn main() {
// Example of a valid OSC packet that should match the /renderer/<u32>/say address.
let packet = b"\0\0\0\x28/renderer/42/say\0\0\0\0,s\0\0HELLO, WORLD!\0\0\0";
// Parse the packet into a OscToplevel enum.
let message: OscToplevel = serde_osc::from_slice(&packet[..]).unwrap();
// This should display "id 42 says: HELLO, WORLD!" to the console.
dispatch(message);
}
/// Dispatch any received OSC message to the appropriate handler.
fn dispatch(msg : OscToplevel) {
match msg {
// handle messages to /routegraph[/...]
OscToplevel::RouteGraph((), rg_msg) => match rg_msg {
OscRouteGraph::AddNode((), (node_id,)) => println!("Adding a node with id={}", node_id),
OscRouteGraph::AddEdge((), (n1, n2)) => println!("New edge from {}->{}", n1, n2),
},
// handle messages to /renderer[/...]
OscToplevel::Renderer((), rend_msg) => match rend_msg {
OscRenderer::ById(renderer_id, by_id_msg) => match by_id_msg {
// handle messages to /renderer/<renderer_id>/say
OscRendererById::Say((), (say,)) => println!("id {} says: {}", renderer_id, say),
},
// other cases omitted for brevity.
_ => {},
}
}
}
§Supported Struct/Enum Formats
The #[derive(OscMessage)]
directive may be applied to either structs or enums.
When applying to an enum, each enum variant must have the form
VariantName(PathArgument, MsgPayload)
PathArgument
may be any type that implements both std::fmt::Display
and
std::str::FromStr
, e.g. i32
, f64
, String
, etc.
For example, VariantName(u8, MsgPayload)
will match any OSC address beginning with
“/xxx[/…]” where “xxx” is a valid u8
. In the special case that PathArgument=()
, the
variant must be explicitly annotated with its address:
// This enum variant will match any address "/my_address[/...]".
#[osc_address(address="my_address")]
VariantName((), MsgPayload)
§MsgPayload
The MsgPayload
component of the enum variant captures all the OSC arguments
associated with a message. This can be a tuple containing the expected types,
or another type that implements OscMessage
.
In the case where the MsgPayload
is a type implementing OscMessage
, it
is deserialized recursively using the unmatched portion of the OSC address.
This strategy was used in the example up top.
Alternatively, #[derive(OscMessage)]
can be applied to any struct that
implements both serde::Serialize
and serde::Deserialize
. This
allows a struct to be used for the MsgPayload
instead of a tuple,
and allows for finer control of encoding via options exposed through Serde.
For example,
enum OscToplevel {
#[osc_address(address="control")]
Control((), ControlArgs),
}
#[derive(Serialize, Deserialize)]
#[derive(OscMessage)]
struct ControlArgs {
id: i32,
amplitude: f32,
length: i32,
}
is functionally equivalent to
enum OscToplevel {
#[osc_address(address="control")]
Control((), (i32, f32, i32),
}
except that the captured arguments are named fields instead of tuple arguments.
§Serialization
The above explanation of address matching and message decoding assumed deserializing
a message into OscMessage
types. Of course, OscMessage
s can be serialized through
any Serde backend as well.
Derive Macros§
- Create a
impl OscMessage for T
implementation for a given type.