core_foundation/
propertylist.rs1use 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
64pub trait CFPropertyListSubClass: TCFType {
68 #[inline]
72 fn to_CFPropertyList(&self) -> CFPropertyList {
73 unsafe { CFPropertyList::wrap_under_get_rule(self.as_concrete_TypeRef().as_void_ptr()) }
74 }
75
76 #[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 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 #[inline]
162 pub fn retain_count(&self) -> CFIndex {
163 unsafe { CFGetRetainCount(self.as_CFTypeRef()) }
164 }
165
166 #[inline]
169 pub fn type_of(&self) -> CFTypeID {
170 unsafe { CFGetTypeID(self.as_CFTypeRef()) }
171 }
172
173 pub fn show(&self) {
175 unsafe { CFShow(self.as_CFTypeRef()) }
176 }
177
178 #[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 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 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}