Skip to content

Commit

Permalink
feat!: packet types
Browse files Browse the repository at this point in the history
Packet types for each ump message type.
Messages are formed of packets.
The Packets trait has Item = Packet.
  • Loading branch information
Ben Leadbetter authored and BenLeadbetter committed Oct 2, 2024
1 parent 61ac321 commit 972f639
Show file tree
Hide file tree
Showing 32 changed files with 1,311 additions and 87 deletions.
17 changes: 13 additions & 4 deletions midi2_proc/src/derives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,16 +375,25 @@ pub fn debug(item: TokenStream1) -> TokenStream1 {
_ => panic!("Only enums and structs supported"),
};
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let buffer_id = common::buffer_generic(generics)
.expect("Expected buffer generic")
.ident();

let buffer_id = if ident == "Packet" {
// special handling
// always a u32 slice
quote! {crate::buffer::UNIT_ID_U32}
} else {
let buffer_ident = common::buffer_generic(generics)
.expect("Expected buffer generic")
.ident();
quote! {<<#buffer_ident as crate::buffer::Buffer>::Unit as crate::buffer::UnitPrivate>::UNIT_ID}
};

quote! {
impl #impl_generics core::fmt::Debug for #ident #ty_generics #where_clause {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
use crate::BufferAccess as BufferAccessDeriveDebug;

fmt.write_fmt(format_args!("{}([", stringify!(#ident)))?;
match <<#buffer_id as crate::buffer::Buffer>::Unit as crate::buffer::UnitPrivate>::UNIT_ID {
match #buffer_id {
crate::buffer::UNIT_ID_U8 => {
use crate::buffer::SpecialiseU8 as SpecialiseU8DeriveDebug;

Expand Down
2 changes: 1 addition & 1 deletion midi2_proc/src/generate_ci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ fn try_from_slice_impl(root_ident: &syn::Ident, properties: &[Property]) -> Toke
type Error = crate::error::InvalidData;
fn try_from(buffer: &'a [u8]) -> core::result::Result<Self, Self::Error> {
if buffer.len() < <Self as crate::traits::MinSize<&[u8]>>::MIN_SIZE {
return Err(crate::error::InvalidData("Slice is too short"));
return Err(crate::error::InvalidData(crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT));
}
if buffer[5] != VERSION {
return Err(crate::error::InvalidData("Incorrect CI version"));
Expand Down
8 changes: 6 additions & 2 deletions src/channel_voice1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod control_change;
mod key_pressure;
mod note_off;
mod note_on;
mod packet;
mod pitch_bend;
mod program_change;

Expand All @@ -15,6 +16,7 @@ pub use control_change::*;
pub use key_pressure::*;
pub use note_off::*;
pub use note_on::*;
pub use packet::Packet;
pub use pitch_bend::*;
pub use program_change::*;

Expand Down Expand Up @@ -52,7 +54,9 @@ impl<'a, U: crate::buffer::Unit> core::convert::TryFrom<&'a [U]> for ChannelVoic
type Error = crate::error::InvalidData;
fn try_from(buffer: &'a [U]) -> Result<Self, Self::Error> {
if buffer.is_empty() {
return Err(crate::error::InvalidData("Slice is too short"));
return Err(crate::error::InvalidData(
crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT,
));
};
Ok(match status(buffer) {
channel_pressure::STATUS => ChannelPressure::try_from(buffer)?.into(),
Expand Down Expand Up @@ -183,7 +187,7 @@ mod test {
fn packets() {
let message = ChannelVoice1::try_from(&[0x2FD6_0900_u32][..]).unwrap();
let mut packets = message.packets();
assert_eq!(packets.next(), Some(&[0x2FD6_0900_u32][..]));
assert_eq!(&*packets.next().unwrap(), &[0x2FD6_0900_u32][..]);
assert_eq!(packets.next(), None);
}

Expand Down
4 changes: 3 additions & 1 deletion src/channel_voice1/channel_pressure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ mod tests {
fn from_empty_data() {
assert_eq!(
ChannelPressure::try_from(&<[u32; 0] as Default>::default()[..]),
Err(crate::error::InvalidData("Slice is too short")),
Err(crate::error::InvalidData(
crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT
)),
);
}

Expand Down
2 changes: 1 addition & 1 deletion src/channel_voice1/control_change.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ mod tests {
let buffer = [0x2AB7_3637_u32];
let message = ControlChange::try_from(&buffer[..]).unwrap();
let mut packets = message.packets();
assert_eq!(packets.next(), Some(&[0x2AB7_3637_u32][..]));
assert_eq!(&*packets.next().unwrap(), &[0x2AB7_3637_u32][..]);
assert_eq!(packets.next(), None);
}
}
106 changes: 106 additions & 0 deletions src/channel_voice1/packet.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use crate::{channel_voice1, error};

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

impl crate::traits::BufferAccess<[u32; 1]> for Packet {
fn buffer_access(&self) -> &[u32; 1] {
&self.0
}
fn buffer_access_mut(&mut self) -> &mut [u32; 1]
where
[u32; 1]: 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.is_empty() {
return Err(error::InvalidData(
crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT,
));
}

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

Ok(Packet({
let mut buffer = [0x0; 1];
buffer[..data.len()].copy_from_slice(data);
buffer
}))
}
}

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

impl crate::Grouped<[u32; 1]> 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; 1]: crate::buffer::BufferMut,
{
use crate::detail::BitOps;
self.0[0].set_nibble(1, group);
}
}

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

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

#[test]
fn construction() {
assert!(Packet::try_from(&[0x2000_0000][..]).is_ok());
}

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

#[test]
fn construction_short_slice() {
assert_eq!(
Packet::try_from(&[][..]),
Err(error::InvalidData(
crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT
)),
);
}
}
8 changes: 6 additions & 2 deletions src/channel_voice2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod controller;
mod key_pressure;
mod note_off;
mod note_on;
mod packet;
mod per_note_management;
mod per_note_pitch_bend;
mod program_change;
Expand All @@ -28,6 +29,7 @@ pub use controller::Controller;
pub use key_pressure::*;
pub use note_off::*;
pub use note_on::*;
pub use packet::Packet;
pub use per_note_management::*;
pub use per_note_pitch_bend::*;
pub use program_change::*;
Expand Down Expand Up @@ -74,7 +76,9 @@ impl<'a> TryFrom<&'a [u32]> for ChannelVoice2<&'a [u32]> {
type Error = crate::error::InvalidData;
fn try_from(buffer: &'a [u32]) -> Result<Self, Self::Error> {
if buffer.is_empty() {
return Err(crate::error::InvalidData("Slice is too short"));
return Err(crate::error::InvalidData(
crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT,
));
};

use crate::detail::BitOps;
Expand Down Expand Up @@ -148,7 +152,7 @@ mod test {

let message = ChannelVoice2::try_from(&[0x4BAC_5900, 0xC0B83064][..]).unwrap();
let mut packets = message.packets();
assert_eq!(packets.next(), Some(&[0x4BAC_5900, 0xC0B83064][..]));
assert_eq!(&*packets.next().unwrap(), &[0x4BAC_5900, 0xC0B83064][..]);
assert_eq!(packets.next(), None);
}

Expand Down
2 changes: 1 addition & 1 deletion src/channel_voice2/assignable_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ mod tests {
let buffer = [0x4C38_5138, 0x3F3ADD42];
let message = AssignableController::try_from(&buffer[..]).unwrap();
let mut packets = message.packets();
assert_eq!(packets.next(), Some(&[0x4C38_5138, 0x3F3ADD42][..]));
assert_eq!(&*packets.next().unwrap(), &[0x4C38_5138, 0x3F3ADD42][..]);
assert_eq!(packets.next(), None);
}
}
106 changes: 106 additions & 0 deletions src/channel_voice2/packet.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use crate::{channel_voice2, error};

#[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.is_empty() {
return Err(error::InvalidData(
crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT,
));
}

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

Ok(Packet({
let mut buffer = [0x0; 2];
buffer[..data.len()].copy_from_slice(data);
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);
}
}

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

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

#[test]
fn construction() {
assert!(Packet::try_from(&[0x4000_0000][..]).is_ok());
}

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

#[test]
fn construction_short_slice() {
assert_eq!(
Packet::try_from(&[][..]),
Err(error::InvalidData(
crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT
)),
);
}
}
1 change: 1 addition & 0 deletions src/detail.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod bit_ops;
mod encode_7bit;

pub mod common_err_strings;
pub mod common_properties;
pub mod helpers;
pub mod property;
Expand Down
2 changes: 2 additions & 0 deletions src/detail/common_err_strings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub const ERR_SLICE_TOO_SHORT: &str = "Slice is too short";
pub const ERR_INCORRECT_UMP_MESSAGE_TYPE: &str = "Incorrect UMP message type";
18 changes: 18 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Error {
BufferOverflow,
InvalidData(InvalidData),
}

impl core::convert::From<BufferOverflow> for Error {
fn from(_value: BufferOverflow) -> Self {
Error::BufferOverflow
}
}

impl core::convert::From<InvalidData> for Error {
fn from(value: InvalidData) -> Self {
Error::InvalidData(value)
}
}

#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct BufferOverflow;

Expand Down
Loading

0 comments on commit 972f639

Please sign in to comment.