core_foundation/
propertylist.rs

1// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! Core Foundation property lists
11
12use std::mem;
13use std::os::raw::c_void;
14use std::ptr;
15
16use crate::base::{CFType, TCFType, TCFTypeRef};
17use crate::data::CFData;
18use crate::error::CFError;
19
20use core_foundation_sys::base::{
21    kCFAllocatorDefault, CFGetRetainCount, CFGetTypeID, CFIndex, CFRetain, CFShow, CFTypeID,
22};
23use core_foundation_sys::error::CFErrorRef;
24pub use core_foundation_sys::propertylist::*;
25
26pub fn create_with_data(
27    data: CFData,
28    options: CFPropertyListMutabilityOptions,
29) -> Result<(*const c_void, CFPropertyListFormat), CFError> {
30    unsafe {
31        let mut error: CFErrorRef = ptr::null_mut();
32        let mut format: CFPropertyListFormat = 0;
33        let property_list = CFPropertyListCreateWithData(
34            kCFAllocatorDefault,
35            data.as_concrete_TypeRef(),
36            options,
37            &mut format,
38            &mut error,
39        );
40        if property_list.is_null() {
41            Err(TCFType::wrap_under_create_rule(error))
42        } else {
43            Ok((property_list, format))
44        }
45    }
46}
47
48pub fn create_data(
49    property_list: *const c_void,
50    format: CFPropertyListFormat,
51) -> Result<CFData, CFError> {
52    unsafe {
53        let mut error: CFErrorRef = ptr::null_mut();
54        let data_ref =
55            CFPropertyListCreateData(kCFAllocatorDefault, property_list, format, 0, &mut error);
56        if data_ref.is_null() {
57            Err(TCFType::wrap_under_create_rule(error))
58        } else {
59            Ok(TCFType::wrap_under_create_rule(data_ref))
60        }
61    }
62}
63
64/// Trait for all subclasses of [`CFPropertyList`].
65///
66/// [`CFPropertyList`]: struct.CFPropertyList.html
67pub trait CFPropertyListSubClass: TCFType {
68    /// Create an instance of the superclass type [`CFPropertyList`] for this instance.
69    ///
70    /// [`CFPropertyList`]: struct.CFPropertyList.html
71    #[inline]
72    fn to_CFPropertyList(&self) -> CFPropertyList {
73        unsafe { CFPropertyList::wrap_under_get_rule(self.as_concrete_TypeRef().as_void_ptr()) }
74    }
75
76    /// Equal to [`to_CFPropertyList`], but consumes self and avoids changing the reference count.
77    ///
78    /// [`to_CFPropertyList`]: #method.to_CFPropertyList
79    #[inline]
80    fn into_CFPropertyList(self) -> CFPropertyList
81    where
82        Self: Sized,
83    {
84        let reference = self.as_concrete_TypeRef().as_void_ptr();
85        mem::forget(self);
86        unsafe { CFPropertyList::wrap_under_create_rule(reference) }
87    }
88}
89
90impl CFPropertyListSubClass for crate::data::CFData {}
91impl CFPropertyListSubClass for crate::string::CFString {}
92impl CFPropertyListSubClass for crate::array::CFArray {}
93impl CFPropertyListSubClass for crate::dictionary::CFDictionary {}
94impl CFPropertyListSubClass for crate::date::CFDate {}
95impl CFPropertyListSubClass for crate::boolean::CFBoolean {}
96impl CFPropertyListSubClass for crate::number::CFNumber {}
97
98declare_TCFType! {
99    /// A CFPropertyList struct. This is superclass to [`CFData`], [`CFString`], [`CFArray`],
100    /// [`CFDictionary`], [`CFDate`], [`CFBoolean`], and [`CFNumber`].
101    ///
102    /// This superclass type does not have its own `CFTypeID`, instead each instance has the `CFTypeID`
103    /// of the subclass it is an instance of. Thus, this type cannot implement the [`TCFType`] trait,
104    /// since it cannot implement the static [`TCFType::type_id()`] method.
105    ///
106    /// [`CFData`]: ../data/struct.CFData.html
107    /// [`CFString`]: ../string/struct.CFString.html
108    /// [`CFArray`]: ../array/struct.CFArray.html
109    /// [`CFDictionary`]: ../dictionary/struct.CFDictionary.html
110    /// [`CFDate`]: ../date/struct.CFDate.html
111    /// [`CFBoolean`]: ../boolean/struct.CFBoolean.html
112    /// [`CFNumber`]: ../number/struct.CFNumber.html
113    /// [`TCFType`]: ../base/trait.TCFType.html
114    /// [`TCFType::type_id()`]: ../base/trait.TCFType.html#method.type_of
115    CFPropertyList, CFPropertyListRef
116}
117
118impl_CFTypeDescription!(CFPropertyList);
119
120impl CFPropertyList {
121    #[inline]
122    pub fn as_concrete_TypeRef(&self) -> CFPropertyListRef {
123        self.0
124    }
125
126    #[inline]
127    pub unsafe fn wrap_under_get_rule(reference: CFPropertyListRef) -> CFPropertyList {
128        assert!(!reference.is_null(), "Attempted to create a NULL object.");
129        let reference = CFRetain(reference);
130        CFPropertyList(reference)
131    }
132
133    #[inline]
134    pub fn as_CFType(&self) -> CFType {
135        unsafe { CFType::wrap_under_get_rule(self.as_CFTypeRef()) }
136    }
137
138    #[inline]
139    pub fn into_CFType(self) -> CFType
140    where
141        Self: Sized,
142    {
143        let reference = self.as_CFTypeRef();
144        mem::forget(self);
145        unsafe { TCFType::wrap_under_create_rule(reference) }
146    }
147
148    #[inline]
149    pub fn as_CFTypeRef(&self) -> ::core_foundation_sys::base::CFTypeRef {
150        self.as_concrete_TypeRef()
151    }
152
153    #[inline]
154    pub unsafe fn wrap_under_create_rule(obj: CFPropertyListRef) -> CFPropertyList {
155        assert!(!obj.is_null(), "Attempted to create a NULL object.");
156        CFPropertyList(obj)
157    }
158
159    /// Returns the reference count of the object. It is unwise to do anything other than test
160    /// whether the return value of this method is greater than zero.
161    #[inline]
162    pub fn retain_count(&self) -> CFIndex {
163        unsafe { CFGetRetainCount(self.as_CFTypeRef()) }
164    }
165
166    /// Returns the type ID of this object. Will be one of `CFData`, `CFString`, `CFArray`,
167    /// `CFDictionary`, `CFDate`, `CFBoolean`, or `CFNumber`.
168    #[inline]
169    pub fn type_of(&self) -> CFTypeID {
170        unsafe { CFGetTypeID(self.as_CFTypeRef()) }
171    }
172
173    /// Writes a debugging version of this object on standard error.
174    pub fn show(&self) {
175        unsafe { CFShow(self.as_CFTypeRef()) }
176    }
177
178    /// Returns `true` if this value is an instance of another type.
179    #[inline]
180    pub fn instance_of<OtherCFType: TCFType>(&self) -> bool {
181        self.type_of() == OtherCFType::type_id()
182    }
183}
184
185impl Clone for CFPropertyList {
186    #[inline]
187    fn clone(&self) -> CFPropertyList {
188        unsafe { CFPropertyList::wrap_under_get_rule(self.0) }
189    }
190}
191
192impl PartialEq for CFPropertyList {
193    #[inline]
194    fn eq(&self, other: &CFPropertyList) -> bool {
195        self.as_CFType().eq(&other.as_CFType())
196    }
197}
198
199impl Eq for CFPropertyList {}
200
201impl CFPropertyList {
202    /// Try to downcast the [`CFPropertyList`] to a subclass. Checking if the instance is the
203    /// correct subclass happens at runtime and `None` is returned if it is not the correct type.
204    /// Works similar to [`Box::downcast`] and [`CFType::downcast`].
205    ///
206    /// # Examples
207    ///
208    /// ```
209    /// # use core_foundation::string::CFString;
210    /// # use core_foundation::propertylist::{CFPropertyList, CFPropertyListSubClass};
211    /// #
212    /// // Create a string.
213    /// let string: CFString = CFString::from_static_string("FooBar");
214    /// // Cast it up to a property list.
215    /// let propertylist: CFPropertyList = string.to_CFPropertyList();
216    /// // Cast it down again.
217    /// assert_eq!(propertylist.downcast::<CFString>().unwrap().to_string(), "FooBar");
218    /// ```
219    ///
220    /// [`CFPropertyList`]: struct.CFPropertyList.html
221    /// [`Box::downcast`]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast
222    pub fn downcast<T: CFPropertyListSubClass>(&self) -> Option<T> {
223        if self.instance_of::<T>() {
224            unsafe {
225                let subclass_ref = T::Ref::from_void_ptr(self.0);
226                Some(T::wrap_under_get_rule(subclass_ref))
227            }
228        } else {
229            None
230        }
231    }
232
233    /// Similar to [`downcast`], but consumes self and can thus avoid touching the retain count.
234    ///
235    /// [`downcast`]: #method.downcast
236    pub fn downcast_into<T: CFPropertyListSubClass>(self) -> Option<T> {
237        if self.instance_of::<T>() {
238            unsafe {
239                let subclass_ref = T::Ref::from_void_ptr(self.0);
240                mem::forget(self);
241                Some(T::wrap_under_create_rule(subclass_ref))
242            }
243        } else {
244            None
245        }
246    }
247}
248
249#[cfg(test)]
250pub mod test {
251    use super::*;
252    use crate::boolean::CFBoolean;
253    use crate::string::CFString;
254
255    #[test]
256    fn test_property_list_serialization() {
257        use super::*;
258        use crate::base::{CFEqual, TCFType};
259        use crate::boolean::CFBoolean;
260        use crate::dictionary::CFDictionary;
261        use crate::number::CFNumber;
262        use crate::string::CFString;
263
264        let bar = CFString::from_static_string("Bar");
265        let baz = CFString::from_static_string("Baz");
266        let boo = CFString::from_static_string("Boo");
267        let foo = CFString::from_static_string("Foo");
268        let tru = CFBoolean::true_value();
269        let n42 = CFNumber::from(1i64 << 33);
270
271        let dict1 = CFDictionary::from_CFType_pairs(&[
272            (bar.as_CFType(), boo.as_CFType()),
273            (baz.as_CFType(), tru.as_CFType()),
274            (foo.as_CFType(), n42.as_CFType()),
275        ]);
276
277        let data = create_data(dict1.as_CFTypeRef(), kCFPropertyListXMLFormat_v1_0).unwrap();
278        let (dict2, _) = create_with_data(data, kCFPropertyListImmutable).unwrap();
279        unsafe {
280            assert_eq!(CFEqual(dict1.as_CFTypeRef(), dict2), 1);
281        }
282    }
283
284    #[test]
285    fn to_propertylist_retain_count() {
286        let string = CFString::from_static_string("alongerstring");
287        assert_eq!(string.retain_count(), 1);
288
289        let propertylist = string.to_CFPropertyList();
290        assert_eq!(string.retain_count(), 2);
291        assert_eq!(propertylist.retain_count(), 2);
292
293        mem::drop(string);
294        assert_eq!(propertylist.retain_count(), 1);
295    }
296
297    #[test]
298    fn downcast_string() {
299        let propertylist = CFString::from_static_string("Bar").to_CFPropertyList();
300        assert_eq!(
301            propertylist.downcast::<CFString>().unwrap().to_string(),
302            "Bar"
303        );
304        assert!(propertylist.downcast::<CFBoolean>().is_none());
305    }
306
307    #[test]
308    fn downcast_boolean() {
309        let propertylist = CFBoolean::true_value().to_CFPropertyList();
310        assert!(propertylist.downcast::<CFBoolean>().is_some());
311        assert!(propertylist.downcast::<CFString>().is_none());
312    }
313
314    #[test]
315    fn downcast_into_fail() {
316        let string = CFString::from_static_string("alongerstring");
317        let propertylist = string.to_CFPropertyList();
318        assert_eq!(string.retain_count(), 2);
319
320        assert!(propertylist.downcast_into::<CFBoolean>().is_none());
321        assert_eq!(string.retain_count(), 1);
322    }
323
324    #[test]
325    fn downcast_into() {
326        let string = CFString::from_static_string("alongerstring");
327        let propertylist = string.to_CFPropertyList();
328        assert_eq!(string.retain_count(), 2);
329
330        let string2 = propertylist.downcast_into::<CFString>().unwrap();
331        assert_eq!(string2.to_string(), "alongerstring");
332        assert_eq!(string2.retain_count(), 2);
333    }
334}