#![allow(clippy::missing_panics_doc)]
use super::base::{impl_cf_type_wrapper, CFType};
use crate::ffi;
use std::ffi::CString;
use std::fmt;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
const CF_ABSOLUTE_TIME_INTERVAL_SINCE_1970: f64 = 978_307_200.0;
fn to_cstring(value: &str) -> CString {
CString::new(value).expect("Core Foundation strings may not contain interior NUL bytes")
}
impl_cf_type_wrapper!(CFString, cf_string_get_type_id);
impl_cf_type_wrapper!(CFNumber, cf_number_get_type_id);
impl_cf_type_wrapper!(CFData, cf_data_get_type_id);
impl_cf_type_wrapper!(CFDate, cf_date_get_type_id);
impl_cf_type_wrapper!(CFUUID, cf_uuid_get_type_id);
impl_cf_type_wrapper!(CFError, cf_error_get_type_id);
impl CFString {
#[must_use]
pub fn new(value: &str) -> Self {
let value = to_cstring(value);
let ptr = unsafe { ffi::cf_string_create_with_cstring(value.as_ptr()) };
Self::from_raw(ptr).expect("CFStringCreateWithCString returned NULL")
}
#[must_use]
pub fn len(&self) -> usize {
unsafe { ffi::cf_string_get_length(self.as_ptr()) }
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn to_string_lossy(&self) -> String {
let ptr = unsafe { ffi::cf_string_copy_cstring(self.as_ptr()) };
if ptr.is_null() {
return String::new();
}
let string = unsafe { std::ffi::CStr::from_ptr(ptr) }
.to_string_lossy()
.into_owned();
unsafe { ffi::acf_free_string(ptr) };
string
}
}
impl Default for CFString {
fn default() -> Self {
Self::new("")
}
}
impl fmt::Display for CFString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.to_string_lossy())
}
}
impl CFNumber {
#[must_use]
pub fn from_i64(value: i64) -> Self {
let ptr = unsafe { ffi::cf_number_create_i64(value) };
Self::from_raw(ptr).expect("CFNumberCreate returned NULL")
}
#[must_use]
pub fn from_u64(value: u64) -> Self {
let ptr = unsafe { ffi::cf_number_create_u64(value) };
Self::from_raw(ptr).expect("CFNumberCreate returned NULL")
}
#[must_use]
pub fn from_f64(value: f64) -> Self {
let ptr = unsafe { ffi::cf_number_create_f64(value) };
Self::from_raw(ptr).expect("CFNumberCreate returned NULL")
}
#[must_use]
pub fn to_i64(&self) -> Option<i64> {
let mut out = 0_i64;
let ok = unsafe { ffi::cf_number_get_i64(self.as_ptr(), &mut out) };
ok.then_some(out)
}
#[must_use]
pub fn to_u64(&self) -> Option<u64> {
let mut out = 0_u64;
let ok = unsafe { ffi::cf_number_get_u64(self.as_ptr(), &mut out) };
ok.then_some(out)
}
#[must_use]
pub fn to_f64(&self) -> Option<f64> {
let mut out = 0.0_f64;
let ok = unsafe { ffi::cf_number_get_f64(self.as_ptr(), &mut out) };
ok.then_some(out)
}
#[must_use]
pub fn is_float_type(&self) -> bool {
unsafe { ffi::cf_number_is_float_type(self.as_ptr()) }
}
}
impl From<i64> for CFNumber {
fn from(value: i64) -> Self {
Self::from_i64(value)
}
}
impl From<u64> for CFNumber {
fn from(value: u64) -> Self {
Self::from_u64(value)
}
}
impl From<f64> for CFNumber {
fn from(value: f64) -> Self {
Self::from_f64(value)
}
}
impl CFData {
#[must_use]
pub fn from_bytes<B: AsRef<[u8]>>(bytes: B) -> Self {
let bytes = bytes.as_ref();
let ptr = unsafe { ffi::cf_data_create(bytes.as_ptr(), bytes.len()) };
Self::from_raw(ptr).expect("CFDataCreate returned NULL")
}
#[must_use]
pub fn len(&self) -> usize {
unsafe { ffi::cf_data_get_length(self.as_ptr()) }
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn to_vec(&self) -> Vec<u8> {
let mut bytes = vec![0_u8; self.len()];
if !bytes.is_empty() {
unsafe { ffi::cf_data_copy_bytes(self.as_ptr(), bytes.as_mut_ptr()) };
}
bytes
}
}
impl CFDate {
#[must_use]
pub fn from_absolute_time(absolute_time: f64) -> Self {
let ptr = unsafe { ffi::cf_date_create(absolute_time) };
Self::from_raw(ptr).expect("CFDateCreate returned NULL")
}
#[must_use]
pub fn now() -> Self {
Self::from_system_time(SystemTime::now())
}
#[must_use]
pub fn from_system_time(time: SystemTime) -> Self {
let unix_seconds = match time.duration_since(UNIX_EPOCH) {
Ok(duration) => duration.as_secs_f64(),
Err(err) => -err.duration().as_secs_f64(),
};
let absolute = unix_seconds - CF_ABSOLUTE_TIME_INTERVAL_SINCE_1970;
Self::from_absolute_time(absolute)
}
#[must_use]
pub fn absolute_time(&self) -> f64 {
unsafe { ffi::cf_date_get_absolute_time(self.as_ptr()) }
}
#[must_use]
pub fn to_system_time(&self) -> Option<SystemTime> {
let unix_seconds = self.absolute_time() + CF_ABSOLUTE_TIME_INTERVAL_SINCE_1970;
if unix_seconds.is_nan() || !unix_seconds.is_finite() {
return None;
}
if unix_seconds >= 0.0 {
Some(UNIX_EPOCH + Duration::from_secs_f64(unix_seconds))
} else {
Some(UNIX_EPOCH - Duration::from_secs_f64(-unix_seconds))
}
}
}
impl CFUUID {
#[must_use]
pub fn new() -> Self {
let ptr = unsafe { ffi::cf_uuid_create() };
Self::from_raw(ptr).expect("CFUUIDCreate returned NULL")
}
#[must_use]
pub fn parse_str(value: &str) -> Option<Self> {
let value = to_cstring(value);
let ptr = unsafe { ffi::cf_uuid_create_from_string(value.as_ptr()) };
Self::from_raw(ptr)
}
#[must_use]
pub fn string(&self) -> CFString {
let ptr = unsafe { ffi::cf_uuid_copy_string(self.as_ptr()) };
CFString::from_raw(ptr).expect("CFUUIDCreateString returned NULL")
}
#[must_use]
pub fn bytes(&self) -> [u8; 16] {
let mut bytes = [0_u8; 16];
unsafe { ffi::cf_uuid_get_bytes(self.as_ptr(), bytes.as_mut_ptr()) };
bytes
}
}
impl Default for CFUUID {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for CFUUID {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.string(), f)
}
}
impl CFError {
#[must_use]
pub fn new(domain: &CFString, code: i64, description: Option<&str>) -> Self {
let description = description.map(to_cstring);
let description_ptr = description
.as_ref()
.map_or(std::ptr::null(), |s| s.as_ptr());
let ptr = unsafe { ffi::cf_error_create(domain.as_ptr(), code, description_ptr) };
Self::from_raw(ptr).expect("CFErrorCreate returned NULL")
}
#[must_use]
pub fn domain(&self) -> CFString {
let ptr = unsafe { ffi::cf_error_get_domain(self.as_ptr()) };
CFString::from_raw(ptr).expect("CFErrorGetDomain returned NULL")
}
#[must_use]
pub fn code(&self) -> i64 {
unsafe { ffi::cf_error_get_code(self.as_ptr()) }
}
#[must_use]
pub fn description_string(&self) -> Option<CFString> {
let ptr = unsafe { ffi::cf_error_copy_description(self.as_ptr()) };
CFString::from_raw(ptr)
}
#[must_use]
pub fn failure_reason(&self) -> Option<CFString> {
let ptr = unsafe { ffi::cf_error_copy_failure_reason(self.as_ptr()) };
CFString::from_raw(ptr)
}
}
impl fmt::Display for CFError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(description) = self.description_string() {
write!(f, "{} ({})", description, self.code())
} else {
write!(f, "{} ({})", self.domain(), self.code())
}
}
}
impl std::error::Error for CFError {}
impl From<CFString> for CFType {
fn from(value: CFString) -> Self {
value.into_cf_type()
}
}
impl From<CFNumber> for CFType {
fn from(value: CFNumber) -> Self {
value.into_cf_type()
}
}
impl From<CFData> for CFType {
fn from(value: CFData) -> Self {
value.into_cf_type()
}
}
impl From<CFDate> for CFType {
fn from(value: CFDate) -> Self {
value.into_cf_type()
}
}
impl From<CFUUID> for CFType {
fn from(value: CFUUID) -> Self {
value.into_cf_type()
}
}