1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
//! The **trigger** module provides types for working with Open Sound Module
//! trigger messages.

use crate::osc::write_osc_string;
use byteorder::{BigEndian, ByteOrder};

use std::fmt;
use std::fmt::Display;
use std::io::{Error, ErrorKind, Read};
static TYPE_TAG: &'static str = ",i";
pub static ADDR_A: &'static str = "/osm/a/tr";
pub static ADDR_B: &'static str = "/osm/b/tr";

/// TriggerAddress represents all possible Open Sound Module
/// trigger addresses
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TriggerAddress {
    A,
    B,
}

impl Display for TriggerAddress {
    /// Display Trait for tr::TriggerAddress
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = match self {
            &TriggerAddress::A => ADDR_A.to_string(),
            &TriggerAddress::B => ADDR_B.to_string(),
        };

        write!(f, "{}", s)
    }
}

/// An Open Sound Module trigger message
/// ```rust
/// use std::io::Read;
/// use open_sound_module::{TriggerMessage,TriggerAddress};
///  
/// fn main() -> Result<(), failure::Error> {
///     let mut msg = TriggerMessage::new(TriggerAddress::B, 1);
///     let bytes = msg.to_vec();
///     let mut buf: [u8; 1024] = [0; 1024];
///     let n = msg.read(&mut buf)?;
///     assert!(n == 20);
///     Ok(())  
/// }
/// ```
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct TriggerMessage {
    addr: TriggerAddress,
    arg: i32,
}

impl TriggerMessage {
    /// Create a new TriggerMessage from the following args:
    /// addr (OSC address pattern)
    /// * TriggerAddress::A
    /// * TriggerAddress::B
    /// arg: (float32)
    /// * 0 for off / low
    /// * 1 for on / high
    pub fn new(addr: TriggerAddress, arg: i32) -> TriggerMessage {
        return TriggerMessage {
            addr: addr,
            arg: arg,
        };
    }

    /// Return a vector of u8 bytes represending the
    /// TriggerMessage in Open Sound Control protocol
    pub fn to_vec(&self) -> Vec<u8> {
        let mut buf: Vec<u8> = Vec::new();

        write_osc_string(&mut buf, self.addr.to_string());
        write_osc_string(&mut buf, TYPE_TAG.to_string());

        let mut be_bytes = [0; 4];
        BigEndian::write_i32(&mut be_bytes, self.arg);

        for byte in be_bytes.iter() {
            buf.push(*byte);
        }

        return buf;
    }
}

impl Display for TriggerMessage {
    /// Display trait for TriggerMessage
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "({}, {}))", self.addr, self.arg)
    }
}

impl Read for TriggerMessage {
    /// Read trait for TriggerMessage
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
        let v = self.to_vec();

        if buf.len() < v.len() {
            let e = Error::new(ErrorKind::Other, "supplied buffer too small");
            return Err(e);
        }

        buf[..v.len()].clone_from_slice(&v);
        Ok(v.len())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    const ADDR_START: usize = 0;
    const ADDR_END: usize = 9;
    const TAG_START: usize = 12;
    const TAG_END: usize = 14;
    const ARG_START: usize = 16;
    const ARG_END: usize = 20;
    const ARG_SIZE: usize = 4;
    const MSG_LEN: usize = 20;

    #[test]
    fn test_trigger_addr() -> Result<(), failure::Error> {
        let test_fixture_a = "/osm/a/tr";
        let test_fixture_b = "/osm/a/tr";

        let addr_a = TriggerAddress::A;
        assert!(addr_a.to_string() == test_fixture_a);

        let addr_b = TriggerAddress::B;
        assert!(addr_a.to_string() == test_fixture_b);

        assert!(addr_a != addr_b);

        let addr_a2 = addr_a.clone();
        assert!(addr_a == addr_a2);

        Ok(())
    }

    #[test]
    fn test_trigger_msg_new() -> Result<(), failure::Error> {
        let test_val = 1;
        let msg = TriggerMessage::new(TriggerAddress::A, test_val);
        println!("{}", msg);
        Ok(())
    }

    #[test]
    fn test_trigger_message_to_vec() -> Result<(), failure::Error> {
        let test_val = 1;
        let msg = TriggerMessage::new(TriggerAddress::A, test_val);
        let bytes = msg.to_vec();

        let comp_bytes = ADDR_A.as_bytes();
        let addr_bytes = &bytes[ADDR_START..ADDR_END];
        assert!(comp_bytes == addr_bytes);

        let comp_bytes = TYPE_TAG.as_bytes();
        let flag_bytes = &bytes[TAG_START..TAG_END];
        assert!(comp_bytes == flag_bytes);

        let mut comp_bytes = [0; ARG_SIZE];
        BigEndian::write_i32(&mut comp_bytes, test_val);
        let arg_bytes = &bytes[ARG_START..ARG_END];
        assert!(comp_bytes == arg_bytes);

        Ok(())
    }

    #[test]
    fn test_trigger_message_read() -> Result<(), failure::Error> {
        let test_val = 0;
        let mut bytes: [u8; 1024] = [0; 1024];
        let mut msg = TriggerMessage::new(TriggerAddress::B, test_val);
        let n = msg.read(&mut bytes).unwrap();
        assert!(bytes[..n].len() == MSG_LEN);

        let comp_bytes = ADDR_B.as_bytes();
        let addr_bytes = &bytes[ADDR_START..ADDR_END];
        assert!(comp_bytes == addr_bytes);

        let comp_bytes = TYPE_TAG.as_bytes();
        let flag_bytes = &bytes[TAG_START..TAG_END];
        assert!(comp_bytes == flag_bytes);

        let mut comp_bytes = [0; ARG_SIZE];
        BigEndian::write_i32(&mut comp_bytes, test_val);
        let arg_bytes = &bytes[ARG_START..ARG_END];
        assert!(comp_bytes == arg_bytes);

        Ok(())
    }
}