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 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Next

Add CFPropertyList and methods for casting to subclasses

  • Loading branch information
faern committed Nov 24, 2017
commit ac6cc9f5d9fbd17420928f427ae984ed4c6e7b2a
@@ -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<Raw>: TCFType<*const Raw> {

This comment has been minimized.

@jdm

jdm Nov 24, 2017

Member

We should be able to do pub trait CFPropertyListSubClass: TCFType<*const Self> { and avoid some redundancy in the implementations.

This comment has been minimized.

@faern

faern Nov 24, 2017

Author Contributor

Getting rid of the generic would be awesome. But I don't understand exactly how that would work. Self here is CFString or similar.

This comment has been minimized.

@faern

faern Nov 24, 2017

Author Contributor

Separate from this I'm working on trying to make the ConcreteTypeRef in TCFType a associated type instead of a generic. When/if that works out this can be greatly simplified.

This comment has been minimized.

@jdm

jdm Nov 24, 2017

Member

impl CFPropertyListSubClass<::data::__CFData> for ::data::CFData duplicates the type in two places. Using the snippet I provided, the resulting impl of impl CFPropertyListSubClass for ::data::CFData should work just fine: https://play.rust-lang.org/?gist=b89fc9fe403069fd843f6054897f21db&version=stable

This comment has been minimized.

@faern

faern Nov 24, 2017

Author Contributor

Yes, but impl U<*const u32> for u32 {} does not hold for us.

Our CFString does not impl TCFType<*const CFString>, it implements it for TCFType<*const __CFString>.

This comment has been minimized.

@faern

faern Nov 24, 2017

Author Contributor

The existance of both CFStringRef and __CFString makes it complicated in more than one situation for me. I can't cast between pointers in the downcast method because the compiler does not know that CFStringRef is a raw pointer. It is, and all *Ref are *const _, but the compiler does not know. Which forces me to reference the underlying *const __CF<Foo> types in order to show the compiler the casts are fine. Maybe it's possible to work around it in some way, but I have not found one yet.

This comment has been minimized.

@jdm

jdm Nov 24, 2017

Member

Oh, whoops. I didn't notice the underscores in the type name.

This comment has been minimized.

@faern

faern Nov 24, 2017

Author Contributor

Easy thing to do. Lots of types with very similar names here :)

/// 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<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");
/// ```
pub fn downcast<Raw, T: CFPropertyListSubClass<Raw>>(&self) -> Result<T, ()> {

This comment has been minimized.

@jdm

jdm Nov 24, 2017

Member

I lean towards returning Option instead of Result here.

This comment has been minimized.

@faern

faern Nov 24, 2017

Author Contributor

Done

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());
}
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.