diff options
Diffstat (limited to 'vendor/core-foundation/src/propertylist.rs')
-rw-r--r-- | vendor/core-foundation/src/propertylist.rs | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/vendor/core-foundation/src/propertylist.rs b/vendor/core-foundation/src/propertylist.rs new file mode 100644 index 000000000..9f34f5658 --- /dev/null +++ b/vendor/core-foundation/src/propertylist.rs @@ -0,0 +1,327 @@ +// 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 { + assert!(!reference.is_null(), "Attempted to create a NULL object."); + let reference = CFRetain(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 { + self.as_concrete_TypeRef() + } + + #[inline] + pub unsafe fn wrap_under_create_rule(obj: CFPropertyListRef) -> CFPropertyList { + assert!(!obj.is_null(), "Attempted to create a NULL object."); + 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_eq!(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(1i64<<33); + + 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_eq!(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_eq!(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_eq!(string2.to_string(), "Bar"); + assert_eq!(string2.retain_count(), 2); + } +} |