From ac6cc9f5d9fbd17420928f427ae984ed4c6e7b2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Fri, 24 Nov 2017 15:03:19 +0100 Subject: [PATCH 1/4] Add CFPropertyList and methods for casting to subclasses --- core-foundation/src/propertylist.rs | 154 +++++++++++++++++++++++++++- 1 file changed, 152 insertions(+), 2 deletions(-) diff --git a/core-foundation/src/propertylist.rs b/core-foundation/src/propertylist.rs index 9b175fe..489711b 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,142 @@ pub fn create_data(property_list: *const c_void, format: CFPropertyListFormat) - } } + +/// Trait for all subclasses of CFPropertyList. +pub trait CFPropertyListSubClass: TCFType<*const Raw> { + /// Create an instance of the superclass type `CFPropertyList` for this instance. + 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. +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"); + /// ``` + pub fn downcast>(&self) -> Result { + if self.instance_of::<_, T>() { + Ok(unsafe { T::wrap_under_get_rule(self.0 as *const Raw) }) + } else { + Err(()) + } + } +} + + #[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 +220,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_err()); + } + + #[test] + fn downcast_boolean() { + let propertylist = CFBoolean::true_value().to_CFPropertyList(); + assert!(propertylist.downcast::<_, CFBoolean>().is_ok()); + assert!(propertylist.downcast::<_, CFString>().is_err()); + } } From 2b7d15941ee9e86b1f8f5c71c5cf47ae77785939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Fri, 24 Nov 2017 15:31:24 +0100 Subject: [PATCH 2/4] Add proper error for CFPropertyList::downcast --- core-foundation/src/propertylist.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/core-foundation/src/propertylist.rs b/core-foundation/src/propertylist.rs index 489711b..438f5ba 100644 --- a/core-foundation/src/propertylist.rs +++ b/core-foundation/src/propertylist.rs @@ -11,6 +11,7 @@ use std::ptr; use std::mem; +use std::fmt; use libc::c_void; @@ -178,16 +179,34 @@ impl CFPropertyList { /// // Cast it down again. /// assert!(propertylist.downcast::<_, CFString>().unwrap().to_string() == "FooBar"); /// ``` - pub fn downcast>(&self) -> Result { + pub fn downcast>(&self) -> Result { if self.instance_of::<_, T>() { Ok(unsafe { T::wrap_under_get_rule(self.0 as *const Raw) }) } else { - Err(()) + Err(CFPropertyListCastError) } } } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CFPropertyListCastError; + +impl fmt::Display for CFPropertyListCastError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use ::std::error::Error; + f.write_str(self.description()) + } +} + +impl ::std::error::Error for CFPropertyListCastError { + fn description(&self) -> &str { + "This CFPropertyList is not an instance of the requested type" + } +} + + + #[cfg(test)] pub mod test { use super::*; From 343b0c9a9a431277766e9684d0f75de7cd50d74a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Fri, 24 Nov 2017 15:48:28 +0100 Subject: [PATCH 3/4] Improve documentation with links --- core-foundation/src/propertylist.rs | 35 +++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/core-foundation/src/propertylist.rs b/core-foundation/src/propertylist.rs index 438f5ba..46b14f4 100644 --- a/core-foundation/src/propertylist.rs +++ b/core-foundation/src/propertylist.rs @@ -60,9 +60,13 @@ pub fn create_data(property_list: *const c_void, format: CFPropertyListFormat) - } -/// Trait for all subclasses of CFPropertyList. +/// 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. + /// 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) } } @@ -76,12 +80,22 @@ 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. +/// 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. +/// 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 { @@ -162,9 +176,9 @@ impl PartialEq for CFPropertyList { impl Eq for CFPropertyList {} impl CFPropertyList { - /// Try to downcast the CFPropertyList to a subclass. Checking if the instance is the correct + /// 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`. + /// Works similar to [`Box::downcast`]. /// /// # Examples /// @@ -179,6 +193,9 @@ impl 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) -> Result { if self.instance_of::<_, T>() { Ok(unsafe { T::wrap_under_get_rule(self.0 as *const Raw) }) From cc1197c371c5dd6ef8f84912e31c225edb8128f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Fri, 24 Nov 2017 16:30:03 +0100 Subject: [PATCH 4/4] Change downcast to return Option --- core-foundation/src/propertylist.rs | 30 ++++++----------------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/core-foundation/src/propertylist.rs b/core-foundation/src/propertylist.rs index 46b14f4..28bc0af 100644 --- a/core-foundation/src/propertylist.rs +++ b/core-foundation/src/propertylist.rs @@ -11,7 +11,6 @@ use std::ptr; use std::mem; -use std::fmt; use libc::c_void; @@ -196,33 +195,16 @@ impl CFPropertyList { /// /// [`CFPropertyList`]: struct.CFPropertyList.html /// [`Box::downcast`]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast - pub fn downcast>(&self) -> Result { + pub fn downcast>(&self) -> Option { if self.instance_of::<_, T>() { - Ok(unsafe { T::wrap_under_get_rule(self.0 as *const Raw) }) + Some(unsafe { T::wrap_under_get_rule(self.0 as *const Raw) }) } else { - Err(CFPropertyListCastError) + None } } } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CFPropertyListCastError; - -impl fmt::Display for CFPropertyListCastError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use ::std::error::Error; - f.write_str(self.description()) - } -} - -impl ::std::error::Error for CFPropertyListCastError { - fn description(&self) -> &str { - "This CFPropertyList is not an instance of the requested type" - } -} - - #[cfg(test)] pub mod test { @@ -261,13 +243,13 @@ pub mod 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_err()); + assert!(propertylist.downcast::<_, CFBoolean>().is_none()); } #[test] fn downcast_boolean() { let propertylist = CFBoolean::true_value().to_CFPropertyList(); - assert!(propertylist.downcast::<_, CFBoolean>().is_ok()); - assert!(propertylist.downcast::<_, CFString>().is_err()); + assert!(propertylist.downcast::<_, CFBoolean>().is_some()); + assert!(propertylist.downcast::<_, CFString>().is_none()); } }