#![allow(clippy::missing_errors_doc)]
use super::{AsCFType, CFData, CFError as CoreFoundationError, CFReadStream, CFType, CFWriteStream};
use crate::ffi;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(isize)]
pub enum CFPropertyListFormat {
OpenStep = 1,
XmlV1_0 = 100,
BinaryV1_0 = 200,
}
impl TryFrom<isize> for CFPropertyListFormat {
type Error = isize;
fn try_from(value: isize) -> Result<Self, Self::Error> {
match value {
1 => Ok(Self::OpenStep),
100 => Ok(Self::XmlV1_0),
200 => Ok(Self::BinaryV1_0),
other => Err(other),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct CFPropertyListMutabilityOptions(u64);
impl CFPropertyListMutabilityOptions {
pub const IMMUTABLE: Self = Self(0);
pub const MUTABLE_CONTAINERS: Self = Self(1 << 0);
pub const MUTABLE_CONTAINERS_AND_LEAVES: Self = Self(1 << 1);
#[must_use]
pub const fn from_bits(bits: u64) -> Self {
Self(bits)
}
#[must_use]
pub const fn as_u64(self) -> u64 {
self.0
}
#[must_use]
pub const fn contains(self, other: Self) -> bool {
(self.0 & other.0) == other.0
}
}
impl From<CFPropertyListMutabilityOptions> for u64 {
fn from(options: CFPropertyListMutabilityOptions) -> Self {
options.0
}
}
#[derive(Debug)]
pub enum CFPropertyListError {
CoreFoundation(CoreFoundationError),
Null(crate::CFError),
}
impl fmt::Display for CFPropertyListError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CoreFoundation(error) => fmt::Display::fmt(error, f),
Self::Null(error) => fmt::Display::fmt(error, f),
}
}
}
impl std::error::Error for CFPropertyListError {}
fn property_list_error(
operation: &'static str,
error_ptr: *mut std::ffi::c_void,
) -> CFPropertyListError {
CoreFoundationError::from_raw(error_ptr)
.map_or_else(|| CFPropertyListError::Null(crate::CFError::new(operation)), CFPropertyListError::CoreFoundation)
}
fn property_list_format(raw: isize) -> CFPropertyListFormat {
CFPropertyListFormat::try_from(raw)
.expect("Core Foundation returned an unknown CFPropertyListFormat")
}
pub struct CFPropertyList;
impl CFPropertyList {
pub fn create_deep_copy(
property_list: &dyn AsCFType,
options: CFPropertyListMutabilityOptions,
) -> Result<CFType, crate::CFError> {
let ptr = unsafe {
ffi::cf_property_list_create_deep_copy(property_list.as_ptr(), options.as_u64())
};
CFType::from_raw(ptr).ok_or(crate::CFError::new("CFPropertyListCreateDeepCopy"))
}
pub fn create_with_data(
data: &CFData,
options: CFPropertyListMutabilityOptions,
) -> Result<(CFType, CFPropertyListFormat), CFPropertyListError> {
let mut format = 0_isize;
let mut error = std::ptr::null_mut();
let ptr = unsafe {
ffi::cf_property_list_create_with_data(
data.as_ptr(),
options.as_u64(),
&mut format,
&mut error,
)
};
CFType::from_raw(ptr)
.map(|value| (value, property_list_format(format)))
.ok_or_else(|| property_list_error("CFPropertyListCreateWithData", error))
}
pub fn create_with_stream(
stream: &CFReadStream,
stream_length: usize,
options: CFPropertyListMutabilityOptions,
) -> Result<(CFType, CFPropertyListFormat), CFPropertyListError> {
let mut format = 0_isize;
let mut error = std::ptr::null_mut();
let stream_length = isize::try_from(stream_length).unwrap_or(isize::MAX);
let ptr = unsafe {
ffi::cf_property_list_create_with_stream(
stream.as_ptr(),
stream_length,
options.as_u64(),
&mut format,
&mut error,
)
};
CFType::from_raw(ptr)
.map(|value| (value, property_list_format(format)))
.ok_or_else(|| property_list_error("CFPropertyListCreateWithStream", error))
}
pub fn create_data(
property_list: &dyn AsCFType,
format: CFPropertyListFormat,
options: u64,
) -> Result<CFData, CFPropertyListError> {
let mut error = std::ptr::null_mut();
let ptr = unsafe {
ffi::cf_property_list_create_data(
property_list.as_ptr(),
format as isize,
options,
&mut error,
)
};
CFData::from_raw(ptr).ok_or_else(|| property_list_error("CFPropertyListCreateData", error))
}
pub fn write(
property_list: &dyn AsCFType,
stream: &CFWriteStream,
format: CFPropertyListFormat,
options: u64,
) -> Result<usize, CFPropertyListError> {
let mut error = std::ptr::null_mut();
let written = unsafe {
ffi::cf_property_list_write(
property_list.as_ptr(),
stream.as_ptr(),
format as isize,
options,
&mut error,
)
};
if written > 0 {
usize::try_from(written).map_err(|_| {
CFPropertyListError::Null(crate::CFError::new("CFPropertyListWrite overflow"))
})
} else {
Err(property_list_error("CFPropertyListWrite", error))
}
}
#[must_use]
pub fn is_valid(property_list: &dyn AsCFType, format: CFPropertyListFormat) -> bool {
unsafe { ffi::cf_property_list_is_valid(property_list.as_ptr(), format as isize) }
}
}