midi2 0.11.1

Ergonomic, versatile, strong types wrapping MIDI 2.0 message data.
Documentation
use crate::{
    util::{Encode7Bit, Truncate},
    *,
};

pub use discovery::query::DiscoveryQuery;
pub use discovery::query::DiscoveryQueryBorrowed;
pub use discovery::query::DiscoveryQueryBorrowedBuilder;
pub use discovery::reply::DiscoveryReply;
pub use discovery::reply::DiscoveryReplyBorrowed;
pub use discovery::reply::DiscoveryReplyBorrowedBuilder;
pub use invalidate_muid::InvalidateMuid;
pub use invalidate_muid::InvalidateMuidBorrowed;
pub use invalidate_muid::InvalidateMuidBorrowedBuilder;
pub use nak::Nak;
pub use nak::NakBorrowed;
pub use nak::NakBorrowedBuilder;

mod discovery;
mod helpers;
mod invalidate_muid;
mod nak;

// todo: bump
const VERSION: u7 = u7::new(0x01);

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DeviceId {
    Channel(u4),
    Group,
    FunctionBlock,
}

impl core::default::Default for DeviceId {
    fn default() -> Self {
        Self::Channel(Default::default())
    }
}

impl DeviceId {
    pub(crate) fn from_u8(v: u8) -> Result<DeviceId> {
        if v == 0x7F {
            Ok(DeviceId::FunctionBlock)
        } else if v == 0x7E {
            Ok(DeviceId::Group)
        } else if v < 0x0F {
            Ok(DeviceId::Channel(v.try_into().unwrap()))
        } else {
            Err(Error::InvalidData)
        }
    }
    pub(crate) fn to_u8(self) -> u8 {
        match self {
            DeviceId::Group => 0x7E,
            DeviceId::FunctionBlock => 0x7F,
            DeviceId::Channel(c) => c.into(),
        }
    }
}

pub trait Ci: ByteData {
    fn device_id(&self) -> DeviceId {
        DeviceId::from_u8(self.byte_data()[2]).unwrap()
    }
    fn version(&self) -> u7 {
        self.byte_data()[5].truncate()
    }
    fn source(&self) -> u28 {
        u28::from_u7s(&self.byte_data()[6..10])
    }
    fn destination(&self) -> u28 {
        u28::from_u7s(&self.byte_data()[10..14])
    }
}

#[derive(Default)]
pub struct CiStandardData {
    pub device_id: DeviceId,
    pub source: Option<u28>,
    pub destination: Option<u28>,
    pub sysex_sub_id2: Option<u7>,
}

pub struct CiStandardDataIterator<'a>(&'a CiStandardData, [u7; 13], usize);

const UNIVERSAL_SYSEX: u7 = u7::new(0x7E);
const UNIVERSAL_SYSEX_SUB_ID_MIDI_CI: u7 = u7::new(0x0D);

impl CiStandardData {
    fn payload(&self) -> Result<CiStandardDataIterator> {
        let Some(source) = self.source else {
            return Err(Error::InvalidData);
        };
        let Some(destination) = self.destination else {
            return Err(Error::InvalidData);
        };
        let Some(sysex_sub_id2) = self.sysex_sub_id2 else {
            return Err(Error::InvalidData);
        };

        let mut data = [u7::default(); 13];
        data[0] = UNIVERSAL_SYSEX;
        data[1] = self.device_id.to_u8().truncate();
        data[2] = UNIVERSAL_SYSEX_SUB_ID_MIDI_CI;
        data[3] = sysex_sub_id2;
        data[4] = VERSION;
        source.to_u7s(&mut data[5..9]);
        destination.to_u7s(&mut data[9..13]);

        Ok(CiStandardDataIterator(self, data, 0))
    }
}

impl<'a> core::iter::Iterator for CiStandardDataIterator<'a> {
    type Item = u7;
    fn next(&mut self) -> Option<Self::Item> {
        if self.2 == self.1.len() {
            None
        } else {
            let ret = Some(self.1[self.2]);
            self.2 += 1;
            ret
        }
    }
}