use crate::osc::write_osc_string;
use byteorder::{BigEndian, ByteOrder};
use std::fmt;
use std::fmt::Display;
use std::io::{Error, ErrorKind, Read};
use std::str::FromStr;
static ADDR_A: &'static str = "/osm/a/cv";
static ADDR_B: &'static str = "/osm/b/cv";
static TYPE_TAG: &'static str = ",f";
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CvAddress {
A,
B,
}
impl FromStr for CvAddress {
type Err = failure::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "/osm/a/cv" {
return Ok(CvAddress::A);
} else if s == "/osm/b/cv" {
return Ok(CvAddress::B);
}
return Err(failure::format_err!("{} is not a known address", s));
}
}
impl Display for CvAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
&CvAddress::A => ADDR_A.to_string(),
&CvAddress::B => ADDR_B.to_string(),
};
write!(f, "{}", s)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CvMessage {
pub addr: CvAddress,
pub arg: f32,
}
impl CvMessage {
pub fn new(addr: CvAddress, arg: f32) -> CvMessage {
return CvMessage {
addr: addr,
arg: arg,
};
}
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_f32(&mut be_bytes, self.arg);
for byte in be_bytes.iter() {
buf.push(*byte);
}
return buf;
}
}
impl Display for CvMessage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {}))", self.addr, self.arg)
}
}
impl Read for CvMessage {
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_cv_addr() -> Result<(), failure::Error> {
let test_fixture_a = "/osm/a/cv";
let test_fixture_b = "/osm/a/cv";
let addr_a = CvAddress::A;
assert!(addr_a.to_string() == test_fixture_a);
let addr_b = CvAddress::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_cv_msg_to_vec() -> Result<(), failure::Error> {
let test_val = 0.1;
let msg = CvMessage::new(CvAddress::A, test_val);
let bytes = msg.to_vec();
assert!(bytes.len() == MSG_LEN);
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_f32(&mut comp_bytes, test_val);
let arg_bytes = &bytes[ARG_START..ARG_END];
assert!(comp_bytes == arg_bytes);
Ok(())
}
#[test]
fn test_cv_msg_read() -> Result<(), failure::Error> {
let test_val = -0.5;
let mut bytes: [u8; 1024] = [0; 1024];
let mut msg = CvMessage::new(CvAddress::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_f32(&mut comp_bytes, test_val);
let arg_bytes = &bytes[ARG_START..ARG_END];
assert!(comp_bytes == arg_bytes);
Ok(())
}
}