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))
}
}
}
pub trait CFPropertyListSubClass: TCFType {
#[inline]
fn to_CFPropertyList(&self) -> CFPropertyList {
unsafe { CFPropertyList::wrap_under_get_rule(self.as_concrete_TypeRef().as_void_ptr()) }
}
#[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!{
CFPropertyList, CFPropertyListRef
}
impl_CFTypeDescription!(CFPropertyList);
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)
}
#[inline]
pub fn retain_count(&self) -> CFIndex {
unsafe { CFGetRetainCount(self.as_CFTypeRef()) }
}
#[inline]
pub fn type_of(&self) -> CFTypeID {
unsafe { CFGetTypeID(self.as_CFTypeRef()) }
}
pub fn show(&self) {
unsafe { CFShow(self.as_CFTypeRef()) }
}
#[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 {
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
}
}
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);
}
}