| // Copyright 2013 The Servo Project Developers. See the COPYRIGHT |
| // file at the top-level directory of this distribution. |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| |
| //! Core Foundation property lists |
| |
| use std::ptr; |
| use std::mem; |
| use std::os::raw::c_void; |
| |
| use error::CFError; |
| use data::CFData; |
| use base::{CFType, TCFType, TCFTypeRef}; |
| |
| pub use core_foundation_sys::propertylist::*; |
| use core_foundation_sys::error::CFErrorRef; |
| use core_foundation_sys::base::{CFGetRetainCount, CFGetTypeID, CFIndex, CFRetain, |
| CFShow, CFTypeID, kCFAllocatorDefault}; |
| |
| pub fn create_with_data(data: CFData, |
| options: CFPropertyListMutabilityOptions) |
| -> Result<(*const c_void, CFPropertyListFormat), CFError> { |
| unsafe { |
| let mut error: CFErrorRef = ptr::null_mut(); |
| let mut format: CFPropertyListFormat = 0; |
| let property_list = CFPropertyListCreateWithData(kCFAllocatorDefault, |
| data.as_concrete_TypeRef(), |
| options, |
| &mut format, |
| &mut error); |
| if property_list.is_null() { |
| Err(TCFType::wrap_under_create_rule(error)) |
| } else { |
| Ok((property_list, format)) |
| } |
| } |
| } |
| |
| pub fn create_data(property_list: *const c_void, format: CFPropertyListFormat) -> Result<CFData, CFError> { |
| unsafe { |
| let mut error: CFErrorRef = ptr::null_mut(); |
| let data_ref = CFPropertyListCreateData(kCFAllocatorDefault, |
| property_list, |
| format, |
| 0, |
| &mut error); |
| if data_ref.is_null() { |
| Err(TCFType::wrap_under_create_rule(error)) |
| } else { |
| Ok(TCFType::wrap_under_create_rule(data_ref)) |
| } |
| } |
| } |
| |
| |
| /// Trait for all subclasses of [`CFPropertyList`]. |
| /// |
| /// [`CFPropertyList`]: struct.CFPropertyList.html |
| pub trait CFPropertyListSubClass: TCFType { |
| /// Create an instance of the superclass type [`CFPropertyList`] for this instance. |
| /// |
| /// [`CFPropertyList`]: struct.CFPropertyList.html |
| #[inline] |
| fn to_CFPropertyList(&self) -> CFPropertyList { |
| unsafe { CFPropertyList::wrap_under_get_rule(self.as_concrete_TypeRef().as_void_ptr()) } |
| } |
| |
| /// Equal to [`to_CFPropertyList`], but consumes self and avoids changing the reference count. |
| /// |
| /// [`to_CFPropertyList`]: #method.to_CFPropertyList |
| #[inline] |
| fn into_CFPropertyList(self) -> CFPropertyList |
| where |
| Self: Sized, |
| { |
| let reference = self.as_concrete_TypeRef().as_void_ptr(); |
| mem::forget(self); |
| unsafe { CFPropertyList::wrap_under_create_rule(reference) } |
| } |
| } |
| |
| impl CFPropertyListSubClass for ::data::CFData {} |
| impl CFPropertyListSubClass for ::string::CFString {} |
| impl CFPropertyListSubClass for ::array::CFArray {} |
| impl CFPropertyListSubClass for ::dictionary::CFDictionary {} |
| impl CFPropertyListSubClass for ::date::CFDate {} |
| impl CFPropertyListSubClass for ::boolean::CFBoolean {} |
| impl CFPropertyListSubClass for ::number::CFNumber {} |
| |
| |
| declare_TCFType!{ |
| /// 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 |
| CFPropertyList, CFPropertyListRef |
| } |
| |
| 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 into_CFType(self) -> CFType |
| where |
| Self: Sized, |
| { |
| let reference = self.as_CFTypeRef(); |
| mem::forget(self); |
| unsafe { TCFType::wrap_under_create_rule(reference) } |
| } |
| |
| #[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<OtherCFType: TCFType>(&self) -> bool { |
| self.type_of() == OtherCFType::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 `None` is returned if it is not the correct type. |
| /// Works similar to [`Box::downcast`] and [`CFType::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<T: CFPropertyListSubClass>(&self) -> Option<T> { |
| if self.instance_of::<T>() { |
| unsafe { |
| let subclass_ref = T::Ref::from_void_ptr(self.0); |
| Some(T::wrap_under_get_rule(subclass_ref)) |
| } |
| } else { |
| None |
| } |
| } |
| |
| /// Similar to [`downcast`], but consumes self and can thus avoid touching the retain count. |
| /// |
| /// [`downcast`]: #method.downcast |
| pub fn downcast_into<T: CFPropertyListSubClass>(self) -> Option<T> { |
| if self.instance_of::<T>() { |
| unsafe { |
| let subclass_ref = T::Ref::from_void_ptr(self.0); |
| mem::forget(self); |
| Some(T::wrap_under_create_rule(subclass_ref)) |
| } |
| } 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}; |
| use boolean::CFBoolean; |
| use number::CFNumber; |
| use dictionary::CFDictionary; |
| use string::CFString; |
| use super::*; |
| |
| let bar = CFString::from_static_string("Bar"); |
| let baz = CFString::from_static_string("Baz"); |
| let boo = CFString::from_static_string("Boo"); |
| let foo = CFString::from_static_string("Foo"); |
| let tru = CFBoolean::true_value(); |
| let n42 = CFNumber::from(42); |
| |
| let dict1 = CFDictionary::from_CFType_pairs(&[(bar.as_CFType(), boo.as_CFType()), |
| (baz.as_CFType(), tru.as_CFType()), |
| (foo.as_CFType(), n42.as_CFType())]); |
| |
| let data = create_data(dict1.as_CFTypeRef(), kCFPropertyListXMLFormat_v1_0).unwrap(); |
| let (dict2, _) = create_with_data(data, kCFPropertyListImmutable).unwrap(); |
| unsafe { |
| assert!(CFEqual(dict1.as_CFTypeRef(), dict2) == 1); |
| } |
| } |
| |
| #[test] |
| fn to_propertylist_retain_count() { |
| let string = CFString::from_static_string("Bar"); |
| assert_eq!(string.retain_count(), 1); |
| |
| let propertylist = string.to_CFPropertyList(); |
| assert_eq!(string.retain_count(), 2); |
| assert_eq!(propertylist.retain_count(), 2); |
| |
| mem::drop(string); |
| assert_eq!(propertylist.retain_count(), 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()); |
| } |
| |
| #[test] |
| fn downcast_into_fail() { |
| let string = CFString::from_static_string("Bar"); |
| let propertylist = string.to_CFPropertyList(); |
| assert_eq!(string.retain_count(), 2); |
| |
| assert!(propertylist.downcast_into::<CFBoolean>().is_none()); |
| assert_eq!(string.retain_count(), 1); |
| } |
| |
| #[test] |
| fn downcast_into() { |
| let string = CFString::from_static_string("Bar"); |
| let propertylist = string.to_CFPropertyList(); |
| assert_eq!(string.retain_count(), 2); |
| |
| let string2 = propertylist.downcast_into::<CFString>().unwrap(); |
| assert!(string2.to_string() == "Bar"); |
| assert_eq!(string2.retain_count(), 2); |
| } |
| } |