Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CFPropertyList and methods for casting to subclasses #131

Merged
merged 4 commits into from Nov 24, 2017
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -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<Raw>: 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<OtherConcreteTypeRef, OtherCFType: TCFType<OtherConcreteTypeRef>>(
&self,
) -> bool {
self.type_of() == <OtherCFType as TCFType<_>>::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<Raw, T: CFPropertyListSubClass<Raw>>(&self) -> Option<T> {
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());
}
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.