diff --git a/core-foundation/src/propertylist.rs b/core-foundation/src/propertylist.rs index 9b175fe..28bc0af 100644 --- a/core-foundation/src/propertylist.rs +++ b/core-foundation/src/propertylist.rs @@ -10,16 +10,18 @@ //! Core Foundation property lists use std::ptr; +use std::mem; use libc::c_void; use error::CFError; use data::CFData; -use base::{TCFType}; +use base::{CFType, TCFType}; pub use core_foundation_sys::propertylist::*; use core_foundation_sys::error::CFErrorRef; -use core_foundation_sys::base::{kCFAllocatorDefault}; +use core_foundation_sys::base::{CFGetRetainCount, CFGetTypeID, CFIndex, CFRelease, CFRetain, + CFShow, CFTypeID, kCFAllocatorDefault}; pub fn create_with_data(data: CFData, options: CFPropertyListMutabilityOptions) @@ -56,8 +58,160 @@ pub fn create_data(property_list: *const c_void, format: CFPropertyListFormat) - } } + +/// Trait for all subclasses of [`CFPropertyList`]. +/// +/// [`CFPropertyList`]: struct.CFPropertyList.html +pub trait CFPropertyListSubClass: TCFType<*const Raw> { + /// Create an instance of the superclass type [`CFPropertyList`] for this instance. + /// + /// [`CFPropertyList`]: struct.CFPropertyList.html + fn to_CFPropertyList(&self) -> CFPropertyList { + unsafe { CFPropertyList::wrap_under_get_rule(self.as_concrete_TypeRef() as *const c_void) } + } +} + +impl CFPropertyListSubClass<::data::__CFData> for ::data::CFData {} +impl CFPropertyListSubClass<::string::__CFString> for ::string::CFString {} +impl CFPropertyListSubClass<::array::__CFArray> for ::array::CFArray {} +impl CFPropertyListSubClass<::dictionary::__CFDictionary> for ::dictionary::CFDictionary {} +impl CFPropertyListSubClass<::date::__CFDate> for ::date::CFDate {} +impl CFPropertyListSubClass<::number::__CFBoolean> for ::boolean::CFBoolean {} +impl CFPropertyListSubClass<::number::__CFNumber> for ::number::CFNumber {} + +/// A CFPropertyList struct. This is superclass to [`CFData`], [`CFString`], [`CFArray`], +/// [`CFDictionary`], [`CFDate`], [`CFBoolean`], and [`CFNumber`]. +/// +/// This superclass type does not have its own `CFTypeID`, instead each instance has the `CFTypeID` +/// of the subclass it is an instance of. Thus, this type cannot implement the [`TCFType`] trait, +/// since it cannot implement the static [`TCFType::type_id()`] method. +/// +/// [`CFData`]: ../data/struct.CFData.html +/// [`CFString`]: ../string/struct.CFString.html +/// [`CFArray`]: ../array/struct.CFArray.html +/// [`CFDictionary`]: ../dictionary/struct.CFDictionary.html +/// [`CFDate`]: ../date/struct.CFDate.html +/// [`CFBoolean`]: ../boolean/struct.CFBoolean.html +/// [`CFNumber`]: ../number/struct.CFNumber.html +/// [`TCFType`]: ../base/trait.TCFType.html +/// [`TCFType::type_id()`]: ../base/trait.TCFType.html#method.type_of +pub struct CFPropertyList(CFPropertyListRef); + +impl Drop for CFPropertyList { + fn drop(&mut self) { + unsafe { CFRelease(self.as_CFTypeRef()) } + } +} + +impl CFPropertyList { + #[inline] + pub fn as_concrete_TypeRef(&self) -> CFPropertyListRef { + self.0 + } + + #[inline] + pub unsafe fn wrap_under_get_rule(reference: CFPropertyListRef) -> CFPropertyList { + let reference = mem::transmute(CFRetain(mem::transmute(reference))); + CFPropertyList(reference) + } + + #[inline] + pub fn as_CFType(&self) -> CFType { + unsafe { CFType::wrap_under_get_rule(self.as_CFTypeRef()) } + } + + #[inline] + pub fn as_CFTypeRef(&self) -> ::core_foundation_sys::base::CFTypeRef { + unsafe { mem::transmute(self.as_concrete_TypeRef()) } + } + + #[inline] + pub unsafe fn wrap_under_create_rule(obj: CFPropertyListRef) -> CFPropertyList { + CFPropertyList(obj) + } + + /// Returns the reference count of the object. It is unwise to do anything other than test + /// whether the return value of this method is greater than zero. + #[inline] + pub fn retain_count(&self) -> CFIndex { + unsafe { CFGetRetainCount(self.as_CFTypeRef()) } + } + + /// Returns the type ID of this object. Will be one of CFData, CFString, CFArray, CFDictionary, + /// CFDate, CFBoolean, or CFNumber. + #[inline] + pub fn type_of(&self) -> CFTypeID { + unsafe { CFGetTypeID(self.as_CFTypeRef()) } + } + + /// Writes a debugging version of this object on standard error. + pub fn show(&self) { + unsafe { CFShow(self.as_CFTypeRef()) } + } + + /// Returns true if this value is an instance of another type. + #[inline] + pub fn instance_of>( + &self, + ) -> bool { + self.type_of() == >::type_id() + } +} + +impl Clone for CFPropertyList { + #[inline] + fn clone(&self) -> CFPropertyList { + unsafe { CFPropertyList::wrap_under_get_rule(self.0) } + } +} + +impl PartialEq for CFPropertyList { + #[inline] + fn eq(&self, other: &CFPropertyList) -> bool { + self.as_CFType().eq(&other.as_CFType()) + } +} + +impl Eq for CFPropertyList {} + +impl CFPropertyList { + /// Try to downcast the [`CFPropertyList`] to a subclass. Checking if the instance is the correct + /// subclass happens at runtime and an error is returned if it is not the correct type. + /// Works similar to [`Box::downcast`]. + /// + /// # Examples + /// + /// ``` + /// # use core_foundation::string::CFString; + /// # use core_foundation::propertylist::{CFPropertyList, CFPropertyListSubClass}; + /// # + /// // Create a string. + /// let string: CFString = CFString::from_static_string("FooBar"); + /// // Cast it up to a property list. + /// let propertylist: CFPropertyList = string.to_CFPropertyList(); + /// // Cast it down again. + /// assert!(propertylist.downcast::<_, CFString>().unwrap().to_string() == "FooBar"); + /// ``` + /// + /// [`CFPropertyList`]: struct.CFPropertyList.html + /// [`Box::downcast`]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast + pub fn downcast>(&self) -> Option { + if self.instance_of::<_, T>() { + Some(unsafe { T::wrap_under_get_rule(self.0 as *const Raw) }) + } else { + None + } + } +} + + + #[cfg(test)] pub mod test { + use super::*; + use string::CFString; + use boolean::CFBoolean; + #[test] fn test_property_list_serialization() { use base::{TCFType, CFEqual}; @@ -84,4 +238,18 @@ pub mod test { assert!(CFEqual(dict1.as_CFTypeRef(), dict2) == 1); } } + + #[test] + fn downcast_string() { + let propertylist = CFString::from_static_string("Bar").to_CFPropertyList(); + assert!(propertylist.downcast::<_, CFString>().unwrap().to_string() == "Bar"); + assert!(propertylist.downcast::<_, CFBoolean>().is_none()); + } + + #[test] + fn downcast_boolean() { + let propertylist = CFBoolean::true_value().to_CFPropertyList(); + assert!(propertylist.downcast::<_, CFBoolean>().is_some()); + assert!(propertylist.downcast::<_, CFString>().is_none()); + } }