Crate osc_address_derive [] [src]

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, OscMessages can be serialized through any Serde backend as well.

Functions

derive_osc_address

Create a impl OscMessage for T implementation for a given type.