diff --git a/Cargo.toml b/Cargo.toml index b3b5c81a..b2928249 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "coremidi" -version = "0.7.1" +version = "0.8.0" authors = ["Christian Perez-Llamas"] description = "CoreMIDI library for Rust" license = "MIT" diff --git a/README.md b/README.md index efa16316..fcf5f6ca 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ The library is published into [crates.io](https://crates.io/crates/coremidi), so ```toml [dependencies] -coremidi = "^0.7.0" +coremidi = "^0.8.0" ``` If you prefer to live in the edge ;-) you can use the master branch by including this instead: @@ -68,8 +68,7 @@ git clone https://github.com/chris-zen/coremidi.git cd coremidi cargo build cargo test -cargo doc -open target/doc/coremidi/index.html +cargo doc --open ``` # Examples diff --git a/src/any_object.rs b/src/any_object.rs new file mode 100644 index 00000000..1de87f9d --- /dev/null +++ b/src/any_object.rs @@ -0,0 +1,139 @@ +use coremidi_sys::{MIDIObjectRef, MIDIObjectType}; + +use crate::{Destination, Device, Entity, Object, Source}; + +#[derive(Debug, PartialEq)] +pub enum AnyObject { + Other(Object), + Device(Device), + Entity(Entity), + Source(Source), + Destination(Destination), + ExternalDevice(Device), + ExternalEntity(Entity), + ExternalSource(Source), + ExternalDestination(Destination), +} + +impl AnyObject { + pub(crate) fn create(object_type: MIDIObjectType, object_ref: MIDIObjectRef) -> Option { + match object_type { + coremidi_sys::kMIDIObjectType_Other => Some(Self::Other(Object(object_ref))), + coremidi_sys::kMIDIObjectType_Device => Some(Self::Device(Device::new(object_ref))), + coremidi_sys::kMIDIObjectType_Entity => Some(Self::Entity(Entity::new(object_ref))), + coremidi_sys::kMIDIObjectType_Source => Some(Self::Source(Source::new(object_ref))), + coremidi_sys::kMIDIObjectType_Destination => { + Some(Self::Destination(Destination::new(object_ref))) + } + coremidi_sys::kMIDIObjectType_ExternalDevice => { + Some(Self::ExternalDevice(Device::new(object_ref))) + } + coremidi_sys::kMIDIObjectType_ExternalEntity => { + Some(Self::ExternalEntity(Entity::new(object_ref))) + } + coremidi_sys::kMIDIObjectType_ExternalSource => { + Some(Self::ExternalSource(Source::new(object_ref))) + } + coremidi_sys::kMIDIObjectType_ExternalDestination => { + Some(Self::ExternalDestination(Destination::new(object_ref))) + } + _ => None, + } + } +} + +impl AsRef for AnyObject { + fn as_ref(&self) -> &Object { + match self { + Self::Other(object) => object, + Self::Device(device) => &device.object, + Self::Entity(entity) => &entity.object, + Self::Source(source) => &source.object, + Self::Destination(destination) => &destination.object, + Self::ExternalDevice(device) => &device.object, + Self::ExternalEntity(entity) => &entity.object, + Self::ExternalSource(source) => &source.object, + Self::ExternalDestination(destination) => &destination.object, + } + } +} + +#[cfg(test)] +mod tests { + use crate::any_object::AnyObject; + use crate::{Destination, Device, Entity, Object, Source}; + + #[test] + fn any_object_create() { + assert_eq!( + AnyObject::create(coremidi_sys::kMIDIObjectType_Other, 1), + Some(AnyObject::Other(Object(1))) + ); + assert_eq!( + AnyObject::create(coremidi_sys::kMIDIObjectType_Device, 1), + Some(AnyObject::Device(Device::new(1))) + ); + assert_eq!( + AnyObject::create(coremidi_sys::kMIDIObjectType_Entity, 1), + Some(AnyObject::Entity(Entity::new(1))) + ); + assert_eq!( + AnyObject::create(coremidi_sys::kMIDIObjectType_Source, 1), + Some(AnyObject::Source(Source::new(1))) + ); + assert_eq!( + AnyObject::create(coremidi_sys::kMIDIObjectType_Destination, 1), + Some(AnyObject::Destination(Destination::new(1))) + ); + assert_eq!( + AnyObject::create(coremidi_sys::kMIDIObjectType_ExternalDevice, 1), + Some(AnyObject::ExternalDevice(Device::new(1))) + ); + assert_eq!( + AnyObject::create(coremidi_sys::kMIDIObjectType_ExternalEntity, 1), + Some(AnyObject::ExternalEntity(Entity::new(1))) + ); + assert_eq!( + AnyObject::create(coremidi_sys::kMIDIObjectType_ExternalSource, 1), + Some(AnyObject::ExternalSource(Source::new(1))) + ); + assert_eq!( + AnyObject::create(coremidi_sys::kMIDIObjectType_ExternalDestination, 1), + Some(AnyObject::ExternalDestination(Destination::new(1))) + ); + } + + #[test] + fn any_object_as_ref() { + let expected_object = Object(1); + assert_eq!(AnyObject::Other(Object(1)).as_ref(), &expected_object); + assert_eq!(AnyObject::Device(Device::new(1)).as_ref(), &expected_object); + assert_eq!(AnyObject::Entity(Entity::new(1)).as_ref(), &expected_object); + assert_eq!(AnyObject::Source(Source::new(1)).as_ref(), &expected_object); + assert_eq!( + AnyObject::Destination(Destination::new(1)).as_ref(), + &expected_object + ); + assert_eq!( + AnyObject::ExternalDevice(Device::new(1)).as_ref(), + &expected_object + ); + assert_eq!( + AnyObject::ExternalEntity(Entity::new(1)).as_ref(), + &expected_object + ); + assert_eq!( + AnyObject::ExternalSource(Source::new(1)).as_ref(), + &expected_object + ); + assert_eq!( + AnyObject::ExternalDestination(Destination::new(1)).as_ref(), + &expected_object + ); + } + + #[test] + fn any_object_from_error() { + assert_eq!(AnyObject::create(0xffff_i32, 1), None); + } +} diff --git a/src/device.rs b/src/device.rs index 5bde1669..975af874 100644 --- a/src/device.rs +++ b/src/device.rs @@ -7,7 +7,7 @@ use crate::object::Object; /// /// A MIDI device or external device, containing entities. /// -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, PartialEq)] pub struct Device { pub(crate) object: Object, } @@ -20,22 +20,22 @@ impl Device { } } -impl Deref for Device { - type Target = Object; - - fn deref(&self) -> &Object { - &self.object +impl Clone for Device { + fn clone(&self) -> Self { + Self::new(self.object.0) } } -impl From for Device { - fn from(object: Object) -> Self { - Self::new(object.0) +impl AsRef for Device { + fn as_ref(&self) -> &Object { + &self.object } } -impl From for Object { - fn from(device: Device) -> Self { - device.object +impl Deref for Device { + type Target = Object; + + fn deref(&self) -> &Object { + &self.object } } diff --git a/src/endpoints/destinations.rs b/src/endpoints/destinations.rs index ec1fedf9..0cb02281 100644 --- a/src/endpoints/destinations.rs +++ b/src/endpoints/destinations.rs @@ -17,7 +17,7 @@ use crate::Object; /// println!("The source at index 0 has display name '{}'", source.display_name().unwrap()); /// ``` /// -#[derive(Debug, Clone, Hash, Eq, PartialEq)] +#[derive(Debug, Hash, Eq, PartialEq)] pub struct Destination { pub(crate) endpoint: Endpoint, } @@ -41,23 +41,29 @@ impl Destination { } } -impl Deref for Destination { - type Target = Endpoint; +impl Clone for Destination { + fn clone(&self) -> Self { + Self::new(self.endpoint.object.0) + } +} - fn deref(&self) -> &Endpoint { - &self.endpoint +impl AsRef for Destination { + fn as_ref(&self) -> &Object { + &self.endpoint.object } } -impl From for Destination { - fn from(object: Object) -> Self { - Self::new(object.0) +impl AsRef for Destination { + fn as_ref(&self) -> &Endpoint { + &self.endpoint } } -impl From for Object { - fn from(destination: Destination) -> Self { - destination.endpoint.object +impl Deref for Destination { + type Target = Endpoint; + + fn deref(&self) -> &Endpoint { + &self.endpoint } } diff --git a/src/endpoints/endpoint.rs b/src/endpoints/endpoint.rs index d8132251..dd875992 100644 --- a/src/endpoints/endpoint.rs +++ b/src/endpoints/endpoint.rs @@ -10,7 +10,7 @@ use crate::object::Object; /// /// You don't need to create an endpoint directly, instead you can create system sources or virtual ones from a client. /// -#[derive(Debug, Clone, Hash, Eq, PartialEq)] +#[derive(Debug, Hash, Eq, PartialEq)] pub struct Endpoint { pub(crate) object: Object, } diff --git a/src/endpoints/sources.rs b/src/endpoints/sources.rs index 26e3c760..3c4d7db1 100644 --- a/src/endpoints/sources.rs +++ b/src/endpoints/sources.rs @@ -19,7 +19,7 @@ use crate::Object; /// println!("The source at index 0 has display name '{}'", source.display_name().unwrap()); /// ``` /// -#[derive(Debug, Clone, Hash, Eq, PartialEq)] +#[derive(Debug, Hash, Eq, PartialEq)] pub struct Source { pub(crate) endpoint: Endpoint, } @@ -50,23 +50,29 @@ impl Source { } } -impl Deref for Source { - type Target = Endpoint; +impl Clone for Source { + fn clone(&self) -> Self { + Self::new(self.endpoint.object.0) + } +} - fn deref(&self) -> &Endpoint { - &self.endpoint +impl AsRef for Source { + fn as_ref(&self) -> &Object { + &self.endpoint.object } } -impl From for Source { - fn from(object: Object) -> Self { - Self::new(object.0) +impl AsRef for Source { + fn as_ref(&self) -> &Endpoint { + &self.endpoint } } -impl From for Object { - fn from(source: Source) -> Self { - source.endpoint.object +impl Deref for Source { + type Target = Endpoint; + + fn deref(&self) -> &Endpoint { + &self.endpoint } } diff --git a/src/entity.rs b/src/entity.rs index ecdf30d0..1d638e70 100644 --- a/src/entity.rs +++ b/src/entity.rs @@ -7,7 +7,7 @@ use crate::object::Object; /// /// An entity that a device owns and that contains endpoints. /// -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, PartialEq)] pub struct Entity { pub(crate) object: Object, } @@ -20,22 +20,22 @@ impl Entity { } } -impl Deref for Entity { - type Target = Object; - - fn deref(&self) -> &Object { - &self.object +impl Clone for Entity { + fn clone(&self) -> Self { + Self::new(self.object.0) } } -impl From for Entity { - fn from(object: Object) -> Self { - Self::new(object.0) +impl AsRef for Entity { + fn as_ref(&self) -> &Object { + &self.object } } -impl From for Object { - fn from(entity: Entity) -> Self { - entity.object +impl Deref for Entity { + type Target = Object; + + fn deref(&self) -> &Object { + &self.object } } diff --git a/src/lib.rs b/src/lib.rs index bc869385..b0a7aadc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,7 @@ For handling low level MIDI data you may look into: */ +mod any_object; mod client; mod device; mod endpoints; @@ -63,7 +64,7 @@ pub use crate::endpoints::sources::{Source, Sources, VirtualSource}; pub use crate::entity::Entity; pub use crate::events::{EventBuffer, EventList, EventListIter, EventPacket, Timestamp}; pub use crate::notifications::{AddedRemovedInfo, IoErrorInfo, Notification, PropertyChangedInfo}; -pub use crate::object::{Object, ObjectType}; +pub use crate::object::Object; pub use crate::packets::{Packet, PacketBuffer, PacketList, PacketListIterator}; pub use crate::ports::{InputPort, InputPortWithContext, OutputPort}; pub use crate::properties::{ diff --git a/src/notifications.rs b/src/notifications.rs index 09e33a8d..b9996dbe 100644 --- a/src/notifications.rs +++ b/src/notifications.rs @@ -8,21 +8,19 @@ use coremidi_sys::{ MIDIObjectPropertyChangeNotification, }; +use crate::any_object::AnyObject; use crate::device::Device; -use crate::object::{Object, ObjectType}; +use crate::object::Object; #[derive(Debug, PartialEq)] pub struct AddedRemovedInfo { - pub parent: Object, - pub parent_type: ObjectType, - pub child: Object, - pub child_type: ObjectType, + pub parent: AnyObject, + pub child: AnyObject, } #[derive(Debug, PartialEq)] pub struct PropertyChangedInfo { - pub object: Object, - pub object_type: ObjectType, + pub object: AnyObject, pub property_name: String, } @@ -52,48 +50,46 @@ impl Notification { ) -> Result { let add_remove_notification = unsafe { &*(notification as *const _ as *const MIDIObjectAddRemoveNotification) }; - let parent_type = ObjectType::try_from(add_remove_notification.parentType); - let child_type = ObjectType::try_from(add_remove_notification.childType); - match (parent_type, child_type) { - (Ok(parent_type), Ok(child_type)) => { - let add_remove_info = AddedRemovedInfo { - parent: Object(add_remove_notification.parent), - parent_type, - child: Object(add_remove_notification.child), - child_type, - }; - match notification.messageID as ::std::os::raw::c_uint { - coremidi_sys::kMIDIMsgObjectAdded => { - Ok(Notification::ObjectAdded(add_remove_info)) - } - coremidi_sys::kMIDIMsgObjectRemoved => { - Ok(Notification::ObjectRemoved(add_remove_info)) - } - _ => unreachable!(), - } + let parent = AnyObject::create( + add_remove_notification.parentType, + add_remove_notification.parent, + ); + let child = AnyObject::create( + add_remove_notification.childType, + add_remove_notification.child, + ); + if let Some((parent, child)) = parent.zip(child) { + let info = AddedRemovedInfo { parent, child }; + match notification.messageID as ::std::os::raw::c_uint { + coremidi_sys::kMIDIMsgObjectAdded => Ok(Notification::ObjectAdded(info)), + coremidi_sys::kMIDIMsgObjectRemoved => Ok(Notification::ObjectRemoved(info)), + _ => unreachable!(), } - _ => Err(notification.messageID as OSStatus), + } else { + Err(notification.messageID as OSStatus) } } fn try_from_property_changed(notification: &MIDINotification) -> Result { let property_changed_notification = unsafe { &*(notification as *const _ as *const MIDIObjectPropertyChangeNotification) }; - match ObjectType::try_from(property_changed_notification.objectType) { - Ok(object_type) => { - let property_name = { - let name_ref: CFStringRef = property_changed_notification.propertyName; - let name: CFString = unsafe { TCFType::wrap_under_get_rule(name_ref) }; - name.to_string() - }; - let property_changed_info = PropertyChangedInfo { - object: Object(property_changed_notification.object), - object_type, - property_name, - }; - Ok(Notification::PropertyChanged(property_changed_info)) - } - Err(_) => Err(notification.messageID as i32), + let maybe_object = AnyObject::create( + property_changed_notification.objectType, + property_changed_notification.object, + ); + if let Some(object) = maybe_object { + let property_name = { + let name_ref: CFStringRef = property_changed_notification.propertyName; + let name: CFString = unsafe { TCFType::wrap_under_get_rule(name_ref) }; + name.to_string() + }; + let property_changed_info = PropertyChangedInfo { + object, + property_name, + }; + Ok(Notification::PropertyChanged(property_changed_info)) + } else { + Err(notification.messageID as i32) } } @@ -143,9 +139,10 @@ mod tests { MIDIObjectAddRemoveNotification, MIDIObjectPropertyChangeNotification, MIDIObjectRef, }; + use crate::any_object::AnyObject; use crate::device::Device; use crate::notifications::{AddedRemovedInfo, IoErrorInfo, Notification, PropertyChangedInfo}; - use crate::object::{Object, ObjectType}; + use crate::object::Object; #[test] fn notification_from_error() { @@ -191,10 +188,8 @@ mod tests { assert!(notification.is_ok()); let info = AddedRemovedInfo { - parent: Object(1), - parent_type: ObjectType::Device, - child: Object(2), - child_type: ObjectType::Other, + parent: AnyObject::Device(Device::new(1)), + child: AnyObject::Other(Object(2)), }; assert_eq!(notification.unwrap(), Notification::ObjectAdded(info)); @@ -218,10 +213,8 @@ mod tests { assert!(notification.is_ok()); let info = AddedRemovedInfo { - parent: Object(1), - parent_type: ObjectType::Device, - child: Object(2), - child_type: ObjectType::Other, + parent: AnyObject::Device(Device::new(1)), + child: AnyObject::Other(Object(2)), }; assert_eq!(notification.unwrap(), Notification::ObjectRemoved(info)); @@ -286,8 +279,7 @@ mod tests { assert!(notification.is_ok()); let info = PropertyChangedInfo { - object: Object(1), - object_type: ObjectType::Device, + object: AnyObject::Device(Device::new(1)), property_name: "name".to_string(), }; diff --git a/src/object.rs b/src/object.rs index de5c1fd8..95056d52 100644 --- a/src/object.rs +++ b/src/object.rs @@ -7,45 +7,11 @@ use crate::properties::{ BooleanProperty, IntegerProperty, Properties, PropertyGetter, PropertySetter, StringProperty, }; -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub enum ObjectType { - Other, - Device, - Entity, - Source, - Destination, - ExternalDevice, - ExternalEntity, - ExternalSource, - ExternalDestination, -} - -impl TryFrom for ObjectType { - type Error = i32; - - fn try_from(value: i32) -> Result { - match value { - coremidi_sys::kMIDIObjectType_Other => Ok(ObjectType::Other), - coremidi_sys::kMIDIObjectType_Device => Ok(ObjectType::Device), - coremidi_sys::kMIDIObjectType_Entity => Ok(ObjectType::Entity), - coremidi_sys::kMIDIObjectType_Source => Ok(ObjectType::Source), - coremidi_sys::kMIDIObjectType_Destination => Ok(ObjectType::Destination), - coremidi_sys::kMIDIObjectType_ExternalDevice => Ok(ObjectType::ExternalDevice), - coremidi_sys::kMIDIObjectType_ExternalEntity => Ok(ObjectType::ExternalEntity), - coremidi_sys::kMIDIObjectType_ExternalSource => Ok(ObjectType::ExternalSource), - coremidi_sys::kMIDIObjectType_ExternalDestination => { - Ok(ObjectType::ExternalDestination) - } - unknown => Err(unknown), - } - } -} - /// A [MIDI Object](https://developer.apple.com/documentation/coremidi/midiobjectref). /// /// The base class of many CoreMIDI objects. /// -#[derive(Clone, Hash, Eq, PartialEq)] +#[derive(Hash, Eq, PartialEq)] pub struct Object(pub(crate) MIDIObjectRef); impl Object { @@ -128,53 +94,3 @@ impl fmt::Debug for Object { write!(f, "Object({:x})", self.0 as usize) } } - -#[cfg(test)] -mod tests { - use crate::object::ObjectType; - - #[test] - fn objecttype_try_from() { - assert_eq!( - ObjectType::try_from(coremidi_sys::kMIDIObjectType_Other), - Ok(ObjectType::Other) - ); - assert_eq!( - ObjectType::try_from(coremidi_sys::kMIDIObjectType_Device), - Ok(ObjectType::Device) - ); - assert_eq!( - ObjectType::try_from(coremidi_sys::kMIDIObjectType_Entity), - Ok(ObjectType::Entity) - ); - assert_eq!( - ObjectType::try_from(coremidi_sys::kMIDIObjectType_Source), - Ok(ObjectType::Source) - ); - assert_eq!( - ObjectType::try_from(coremidi_sys::kMIDIObjectType_Destination), - Ok(ObjectType::Destination) - ); - assert_eq!( - ObjectType::try_from(coremidi_sys::kMIDIObjectType_ExternalDevice), - Ok(ObjectType::ExternalDevice) - ); - assert_eq!( - ObjectType::try_from(coremidi_sys::kMIDIObjectType_ExternalEntity), - Ok(ObjectType::ExternalEntity) - ); - assert_eq!( - ObjectType::try_from(coremidi_sys::kMIDIObjectType_ExternalSource), - Ok(ObjectType::ExternalSource) - ); - assert_eq!( - ObjectType::try_from(coremidi_sys::kMIDIObjectType_ExternalDestination), - Ok(ObjectType::ExternalDestination) - ); - } - - #[test] - fn objecttype_from_error() { - assert_eq!(ObjectType::try_from(0xffff_i32), Err(0xffff)); - } -}