1#![allow(clippy::missing_errors_doc)]
4use super::{
26 AsCFType, CFData, CFError as CoreFoundationError, CFReadStream, CFType, CFWriteStream,
27};
28use crate::ffi;
29use std::fmt;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
33#[repr(isize)]
34pub enum CFPropertyListFormat {
35 OpenStep = 1,
36 XmlV1_0 = 100,
37 BinaryV1_0 = 200,
38}
39
40impl TryFrom<isize> for CFPropertyListFormat {
41 type Error = isize;
42
43 fn try_from(value: isize) -> Result<Self, Self::Error> {
44 match value {
45 1 => Ok(Self::OpenStep),
46 100 => Ok(Self::XmlV1_0),
47 200 => Ok(Self::BinaryV1_0),
48 other => Err(other),
49 }
50 }
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
55pub struct CFPropertyListMutabilityOptions(u64);
56
57impl CFPropertyListMutabilityOptions {
58 pub const IMMUTABLE: Self = Self(0);
60 pub const MUTABLE_CONTAINERS: Self = Self(1 << 0);
62 pub const MUTABLE_CONTAINERS_AND_LEAVES: Self = Self(1 << 1);
64
65 #[must_use]
67 pub const fn from_bits(bits: u64) -> Self {
68 Self(bits)
69 }
70
71 #[must_use]
73 pub const fn as_u64(self) -> u64 {
74 self.0
75 }
76
77 #[must_use]
79 pub const fn contains(self, other: Self) -> bool {
80 (self.0 & other.0) == other.0
81 }
82}
83
84impl From<CFPropertyListMutabilityOptions> for u64 {
85 fn from(options: CFPropertyListMutabilityOptions) -> Self {
86 options.0
87 }
88}
89
90#[derive(Debug)]
92pub enum CFPropertyListError {
93 CoreFoundation(CoreFoundationError),
95 Null(crate::NullPointerError),
97 UnknownFormat(isize),
98}
99
100impl fmt::Display for CFPropertyListError {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 match self {
103 Self::CoreFoundation(error) => fmt::Display::fmt(error, f),
104 Self::Null(error) => fmt::Display::fmt(error, f),
105 Self::UnknownFormat(raw) => write!(f, "unknown CFPropertyListFormat {raw}"),
106 }
107 }
108}
109
110impl std::error::Error for CFPropertyListError {}
111
112fn property_list_error(
113 operation: &'static str,
114 error_ptr: *mut std::ffi::c_void,
115) -> CFPropertyListError {
116 unsafe { CoreFoundationError::from_raw(error_ptr) }.map_or_else(
117 || CFPropertyListError::Null(crate::NullPointerError::new(operation)),
118 CFPropertyListError::CoreFoundation,
119 )
120}
121
122fn decoded_property_list(
123 ptr: *mut std::ffi::c_void,
124 format: isize,
125 operation: &'static str,
126 error: *mut std::ffi::c_void,
127) -> Result<(CFType, CFPropertyListFormat), CFPropertyListError> {
128 let value = unsafe { CFType::from_raw(ptr) }.ok_or_else(|| property_list_error(operation, error))?;
129 let format = CFPropertyListFormat::try_from(format).map_err(CFPropertyListError::UnknownFormat)?;
130 Ok((value, format))
131}
132
133#[derive(Debug)]
135pub struct CFPropertyList;
136
137impl CFPropertyList {
138 pub fn create_deep_copy(
140 property_list: &dyn AsCFType,
141 options: CFPropertyListMutabilityOptions,
142 ) -> Result<CFType, crate::NullPointerError> {
143 let ptr = unsafe {
144 ffi::cf_property_list_create_deep_copy(property_list.as_ptr(), options.as_u64())
145 };
146 unsafe { CFType::from_raw(ptr) }.ok_or(crate::NullPointerError::new("CFPropertyListCreateDeepCopy"))
147 }
148
149 pub fn create_with_data(
151 data: &CFData,
152 options: CFPropertyListMutabilityOptions,
153 ) -> Result<(CFType, CFPropertyListFormat), CFPropertyListError> {
154 let mut format = 0_isize;
155 let mut error = std::ptr::null_mut();
156 let ptr = unsafe {
157 ffi::cf_property_list_create_with_data(
158 data.as_ptr(),
159 options.as_u64(),
160 &raw mut format,
161 &raw mut error,
162 )
163 };
164 decoded_property_list(ptr, format, "CFPropertyListCreateWithData", error)
165 }
166
167 pub fn create_with_stream(
169 stream: &CFReadStream,
170 stream_length: usize,
171 options: CFPropertyListMutabilityOptions,
172 ) -> Result<(CFType, CFPropertyListFormat), CFPropertyListError> {
173 let mut format = 0_isize;
174 let mut error = std::ptr::null_mut();
175 let stream_length = isize::try_from(stream_length).unwrap_or(isize::MAX);
176 let ptr = unsafe {
177 ffi::cf_property_list_create_with_stream(
178 stream.as_ptr(),
179 stream_length,
180 options.as_u64(),
181 &raw mut format,
182 &raw mut error,
183 )
184 };
185 decoded_property_list(ptr, format, "CFPropertyListCreateWithStream", error)
186 }
187
188 pub fn create_data(
190 property_list: &dyn AsCFType,
191 format: CFPropertyListFormat,
192 options: u64,
193 ) -> Result<CFData, CFPropertyListError> {
194 let mut error = std::ptr::null_mut();
195 let ptr = unsafe {
196 ffi::cf_property_list_create_data(
197 property_list.as_ptr(),
198 format as isize,
199 options,
200 &raw mut error,
201 )
202 };
203 unsafe { CFData::from_raw(ptr) }
204 .ok_or_else(|| property_list_error("CFPropertyListCreateData", error))
205 }
206
207 pub fn write(
209 property_list: &dyn AsCFType,
210 stream: &CFWriteStream,
211 format: CFPropertyListFormat,
212 options: u64,
213 ) -> Result<usize, CFPropertyListError> {
214 let mut error = std::ptr::null_mut();
215 let written = unsafe {
216 ffi::cf_property_list_write(
217 property_list.as_ptr(),
218 stream.as_ptr(),
219 format as isize,
220 options,
221 &raw mut error,
222 )
223 };
224 if written > 0 {
225 usize::try_from(written).map_err(|_| {
226 CFPropertyListError::Null(crate::NullPointerError::new("CFPropertyListWrite overflow"))
227 })
228 } else {
229 Err(property_list_error("CFPropertyListWrite", error))
230 }
231 }
232
233 #[must_use]
235 pub fn is_valid(property_list: &dyn AsCFType, format: CFPropertyListFormat) -> bool {
236 unsafe { ffi::cf_property_list_is_valid(property_list.as_ptr(), format as isize) }
237 }
238}