midi2 0.11.1

Ergonomic, versatile, strong types wrapping MIDI 2.0 message data.
Documentation
use crate::{error, sysex7};

#[derive(Eq, PartialEq, Clone, midi2_proc::Debug)]
pub struct Packet(pub(crate) [u32; 2]);

impl crate::traits::BufferAccess<[u32; 2]> for Packet {
    fn buffer_access(&self) -> &[u32; 2] {
        &self.0
    }
    fn buffer_access_mut(&mut self) -> &mut [u32; 2]
    where
        [u32; 2]: crate::buffer::BufferMut,
    {
        &mut self.0
    }
}

impl<'a> core::convert::TryFrom<&'a [u32]> for Packet {
    type Error = error::InvalidData;
    fn try_from(data: &'a [u32]) -> Result<Self, Self::Error> {
        if data.len() < 2 {
            return Err(error::InvalidData(
                crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT,
            ));
        }

        use crate::detail::BitOps;
        if u8::from(data[0].nibble(0)) != sysex7::UMP_MESSAGE_TYPE {
            return Err(error::InvalidData(
                crate::detail::common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE,
            ));
        }

        if u8::from(data[0].nibble(3)) > 6 {
            return Err(error::InvalidData(sysex7::ERR_INVALID_PACKET_SIZE));
        }

        status_from_data(data)?;

        Ok(Packet({
            let mut buffer = [0x0; 2];
            let sz = 2.min(data.len());
            buffer[..sz].copy_from_slice(&data[..sz]);
            buffer
        }))
    }
}

impl core::ops::Deref for Packet {
    type Target = [u32];
    fn deref(&self) -> &Self::Target {
        &self.0[..]
    }
}

impl crate::Grouped<[u32; 2]> for Packet {
    fn group(&self) -> crate::ux::u4 {
        use crate::detail::BitOps;
        self.0[0].nibble(1)
    }
    fn set_group(&mut self, group: crate::ux::u4)
    where
        [u32; 2]: crate::buffer::BufferMut,
    {
        use crate::detail::BitOps;
        self.0[0].set_nibble(1, group);
    }
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Status {
    Complete,
    Start,
    Continue,
    End,
}

pub struct PayloadIterator<'a> {
    data: &'a [u32; 2],
    index: usize,
}

impl core::iter::Iterator for PayloadIterator<'_> {
    type Item = crate::ux::u7;
    fn next(&mut self) -> Option<Self::Item> {
        use crate::detail::BitOps;
        if self.index >= self.packet_size() {
            return None;
        }
        let v = self.data[(self.index + 2) / 4].septet((self.index + 2) % 4);
        self.index += 1;
        Some(v)
    }

    fn nth(&mut self, n: usize) -> Option<Self::Item> {
        use crate::detail::BitOps;
        if self.index + n >= self.packet_size() {
            self.index = (self.index + n).min(6);
            return None;
        }
        let v = self.data[(self.index + n + 2) / 4].septet((self.index + n + 2) % 4);
        self.index += n + 1;
        Some(v)
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let len = self.len();
        (len, Some(len))
    }

    fn count(self) -> usize {
        self.len()
    }
}

impl core::iter::FusedIterator for PayloadIterator<'_> {}

impl core::iter::ExactSizeIterator for PayloadIterator<'_> {
    fn len(&self) -> usize {
        self.packet_size() - self.index
    }
}

impl PayloadIterator<'_> {
    fn packet_size(&self) -> usize {
        use crate::detail::BitOps;
        let len = u8::from(self.data[0].nibble(3)) as usize;
        debug_assert!(len <= 6);
        len
    }
}

impl Packet {
    pub fn status(&self) -> Status {
        status_from_data(&self.0[..]).unwrap()
    }

    pub fn payload<'a>(&'a self) -> PayloadIterator<'a> {
        PayloadIterator {
            data: &self.0,
            index: 0,
        }
    }
}

fn status_from_data(data: &[u32]) -> Result<Status, error::InvalidData> {
    use crate::detail::BitOps;
    use Status::*;
    match u8::from(data[0].nibble(2)) {
        0x0 => Ok(Complete),
        0x1 => Ok(Start),
        0x2 => Ok(Continue),
        0x3 => Ok(End),
        _ => Err(error::InvalidData("Invalid SysEx7 status byte")),
    }
}

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

    #[test]
    fn construction() {
        assert_eq!(
            &*Packet::try_from(&[0x3000_0000, 0x0000_0000][..]).unwrap(),
            &[0x3000_0000, 0x0000_0000][..],
        );
    }

    #[test]
    fn construction_long_slice() {
        assert_eq!(
            &*Packet::try_from(&[0x3000_0000, 0x0, 0x0, 0x0][..]).unwrap(),
            &[0x3000_0000, 0x0000_0000][..],
        );
    }

    #[test]
    fn construction_short_slice() {
        assert_eq!(
            Packet::try_from(&[0x3000_0000][..]),
            Err(error::InvalidData(
                crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT
            )),
        );
    }

    #[test]
    fn construction_wrong_ump_type() {
        assert_eq!(
            Packet::try_from(&[0x0000_0000, 0x0000_0000][..]),
            Err(error::InvalidData(
                crate::detail::common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE
            )),
        );
    }

    #[test]
    fn construction_invalid_payload_size() {
        assert_eq!(
            Packet::try_from(&[0x3007_0000, 0x0, 0x0, 0x0][..]),
            Err(error::InvalidData(sysex7::ERR_INVALID_PACKET_SIZE)),
        );
    }

    #[test]
    fn construction_complete() {
        assert_eq!(
            Packet::try_from(&[0x3000_0000, 0x0000_0000][..])
                .unwrap()
                .status(),
            Status::Complete,
        );
    }

    #[test]
    fn construction_start() {
        assert_eq!(
            Packet::try_from(&[0x3010_0000, 0x0000_0000][..])
                .unwrap()
                .status(),
            Status::Start,
        );
    }

    #[test]
    fn construction_continue() {
        assert_eq!(
            Packet::try_from(&[0x3020_0000, 0x0000_0000][..])
                .unwrap()
                .status(),
            Status::Continue,
        );
    }

    #[test]
    fn construction_end() {
        assert_eq!(
            Packet::try_from(&[0x3030_0000, 0x0000_0000][..])
                .unwrap()
                .status(),
            Status::End,
        );
    }

    #[test]
    fn payload_empty() {
        assert_eq!(
            Packet::try_from(&[0x3000_0000, 0x0000_0000][..])
                .unwrap()
                .payload()
                .collect::<std::vec::Vec<ux::u7>>(),
            std::vec::Vec::<ux::u7>::new(),
        );
    }

    #[test]
    fn payload_one_item() {
        assert_eq!(
            Packet::try_from(&[0x3001_7F00, 0x0000_0000][..])
                .unwrap()
                .payload()
                .collect::<std::vec::Vec<ux::u7>>(),
            std::vec![ux::u7::new(0x7F),]
        );
    }

    #[test]
    fn payload_two_items() {
        assert_eq!(
            Packet::try_from(&[0x3002_0102, 0x0000_0000][..])
                .unwrap()
                .payload()
                .collect::<std::vec::Vec<ux::u7>>(),
            std::vec![ux::u7::new(0x01), ux::u7::new(0x02)],
        );
    }

    #[test]
    fn payload_full_payload() {
        assert_eq!(
            Packet::try_from(&[0x3006_0102, 0x0304_0506][..])
                .unwrap()
                .payload()
                .collect::<std::vec::Vec<ux::u7>>(),
            std::vec![
                ux::u7::new(0x01),
                ux::u7::new(0x02),
                ux::u7::new(0x03),
                ux::u7::new(0x04),
                ux::u7::new(0x05),
                ux::u7::new(0x06),
            ],
        );
    }

    #[test]
    #[allow(clippy::iter_nth_zero)]
    fn payload_nth_0() {
        assert_eq!(
            Packet::try_from(&[0x3006_0102, 0x0304_0506][..])
                .unwrap()
                .payload()
                .nth(0),
            Some(ux::u7::new(0x01)),
        );
    }

    #[test]
    fn payload_nth_1() {
        assert_eq!(
            Packet::try_from(&[0x3006_0102, 0x0304_0506][..])
                .unwrap()
                .payload()
                .nth(1),
            Some(ux::u7::new(0x02)),
        );
    }

    #[test]
    fn payload_nth_5() {
        assert_eq!(
            Packet::try_from(&[0x3006_0102, 0x0304_0506][..])
                .unwrap()
                .payload()
                .nth(5),
            Some(ux::u7::new(0x06)),
        );
    }

    #[test]
    fn payload_nth_6() {
        assert_eq!(
            Packet::try_from(&[0x3006_0102, 0x0304_0506][..])
                .unwrap()
                .payload()
                .nth(6),
            None,
        );
    }

    #[test]
    fn payload_nth_6_followed_by_next_should_return_none() {
        let buffer = [0x3006_0102, 0x0304_0506];
        let message = Packet::try_from(&buffer[..]).unwrap();
        let mut iter = message.payload();
        assert_eq!(iter.nth(6), None);
        assert_eq!(iter.next(), None);
    }

    #[test]
    fn payload_call_next_and_the_iterator_length_should_be_one_fewer() {
        let buffer = [0x3006_0102, 0x0304_0506];
        let message = Packet::try_from(&buffer[..]).unwrap();
        let mut iter = message.payload();
        assert_eq!(iter.len(), 6);
        iter.next();
        assert_eq!(iter.len(), 5);
    }

    #[test]
    fn payload_exhaustive_nth_should_leave_iter_length_0() {
        let buffer = [0x3006_0102, 0x0304_0506];
        let message = Packet::try_from(&buffer[..]).unwrap();
        let mut iter = message.payload();
        assert_eq!(iter.len(), 6);
        iter.nth(6);
        assert_eq!(iter.len(), 0);
    }

    #[test]
    fn payload_over_exhaustive_nth_should_leave_iter_length_0() {
        let buffer = [0x3006_0102, 0x0304_0506];
        let message = Packet::try_from(&buffer[..]).unwrap();
        let mut iter = message.payload();
        assert_eq!(iter.len(), 6);
        iter.nth(7);
        assert_eq!(iter.len(), 0);
    }

    #[test]
    fn payload_nth_should_leave_iter_with_n_fewer_length() {
        let buffer = [0x3006_0102, 0x0304_0506];
        let message = Packet::try_from(&buffer[..]).unwrap();
        let mut iter = message.payload();
        assert_eq!(iter.len(), 6);
        iter.nth(2);
        assert_eq!(iter.len(), 3);
    }
}