Auto merge of #131 - faern:implement-propertylist, r=jdm
Add CFPropertyList and methods for casting to subclasses https://developer.apple.com/documentation/corefoundation/cfpropertylist-rif specifies the following types as valid subclasess of a property list: CFData, CFString, CFArray, CFDictionary, CFDate, CFBoolean, and CFNumber. So I add a trait, `CFPropertyListSubClass`, and implement it for the valid types. Then add the `CFPropertyList` type and make it work as much as possible as the other `TCFType`s. However, it can't implement that trait, since it can't implement the static method `type_id`. The main point of adding this was to then get to the convenience methods `to_CFPropertyList` and `downcast` that allows safe casting between the subclasses and the superclass. This is handy in calls such as `SCDynamicStoreSetValue` and `SCDynamicStoreCopyValue` where the methods either accept or return a `CFPropertyListRef`. With this addition it becomes easy to write safe wrappers around those functions and make them accept proper, constrained types. Disclaimer: I'm not yet super familiar with CF. So I can only hope this is safe and sound, but I think it is. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/core-foundation-rs/131) <!-- Reviewable:end -->
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user