Skip to content

Commit

Permalink
Expand CFType impl
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Jan 9, 2025
1 parent dc7fefc commit de1c9d9
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 9 deletions.
53 changes: 52 additions & 1 deletion framework-crates/objc2-core-foundation/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,13 @@
// That means we can use `isize`/`usize`, which is more ergonomic.

use core::cell::UnsafeCell;
use core::convert::AsRef;
use core::fmt;
use core::hash;
use core::marker::{PhantomData, PhantomPinned};

use crate::{CFEqual, CFHash};

/// [Apple's documentation](https://developer.apple.com/documentation/corefoundation/cftypeid?language=objc)
pub type CFTypeID = usize;

Expand All @@ -62,5 +67,51 @@ pub struct CFType {
_p: UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>,
}

crate::__cf_type_common!(CFType);
// Reflexive AsRef impl.
impl AsRef<Self> for CFType {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}

impl fmt::Debug for CFType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// TODO: Use `CFCopyDescription` here
f.debug_struct("CFType").finish_non_exhaustive()
}
}

// Equality in CF has approximately the same semantics as Rust equality.
//
// From the docs:
// > Equality is something specific to each Core Foundation opaque type. For
// > example, two CFNumber objects are equal if the numeric values they
// > represent are equal. Two CFString objects are equal if they represent
// > identical sequences of characters, regardless of encoding.
impl PartialEq for CFType {
#[inline]
#[doc(alias = "CFEqual")]
fn eq(&self, other: &Self) -> bool {
CFEqual(Some(self), Some(other)) != 0
}
}

// Similar to NSObject, most types' equality is reflexive.
impl Eq for CFType {}

// From the documentation for CFHash:
// > Two objects that are equal (as determined by the `CFEqual` function) have
// > the same hashing value. However, the converse is not true: two objects
// > with the same hashing value might not be equal. That is, hashing values
// > are not necessarily unique.
//
// I.e. the same semantics as Rust's `Hash`.
impl hash::Hash for CFType {
#[doc(alias = "CFHash")]
fn hash<H: hash::Hasher>(&self, state: &mut H) {
CFHash(Some(self)).hash(state);
}
}

crate::__cf_type_objc2!(CFType, crate::__cf_macro_helpers::Encoding::Void);
71 changes: 64 additions & 7 deletions framework-crates/objc2-core-foundation/src/cf_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,22 +64,50 @@ macro_rules! __cf_type_common {
.finish_non_exhaustive()
}
}

$crate::__cf_type_convert_cf_type!($ty);
};
}

#[cfg(feature = "CFBase")]
#[doc(hidden)]
#[macro_export]
macro_rules! __cf_type_convert_cf_type {
($ty:ty) => {
// Allow converting to CFType.

impl $crate::__cf_macro_helpers::AsRef<$crate::CFType> for $ty {
#[inline]
fn as_ref(&self) -> &$crate::CFType {
self // Through Deref of self or superclass
}
}

impl $crate::__cf_macro_helpers::Borrow<$crate::CFType> for $ty {
#[inline]
fn borrow(&self) -> &$crate::CFType {
self // Through Deref of self or superclass
}
}
};
}

#[cfg(not(feature = "CFBase"))]
#[doc(hidden)]
#[macro_export]
macro_rules! __cf_type_convert_cf_type {
($ty:ty) => {};
}

#[doc(hidden)]
#[macro_export]
macro_rules! __cf_type_superclass {
// No superclass
($ty:ident) => {
// NOTE: We intentionally don't implement `Deref` with
// `Target = AnyObject` when there isn't a superclass, as we want
// conversions to Objective-C types to be explicit.
//
// TODO: Maybe implement `Deref<Target = CFTypeRef>`?
($ty:ty) => {
$crate::__cf_type_no_superclass!($ty);
};
// If has superclass.
($ty:ident: $superclass:ty) => {
($ty:ty: $superclass:ty) => {
// Similar to `objc2::extern_class!`, we implement Deref for the
// type to allow easy conversion to the super class.
impl $crate::__cf_macro_helpers::Deref for $ty {
Expand Down Expand Up @@ -111,6 +139,35 @@ macro_rules! __cf_type_superclass {
};
}

#[cfg(feature = "CFBase")]
#[doc(hidden)]
#[macro_export]
macro_rules! __cf_type_no_superclass {
($ty:ty) => {
// NOTE: We intentionally don't implement `Deref` with
// `Target = AnyObject` when there isn't a superclass, as we want
// conversions to Objective-C types to be explicit.
//
// Instead, we prefer a `Deref` impl to `CFType`.
impl $crate::__cf_macro_helpers::Deref for $ty {
type Target = $crate::CFType;

#[inline]
fn deref(&self) -> &Self::Target {
// SAFETY: It is valid to re-interpret a type as CFType.
unsafe { core::mem::transmute(self) }
}
}
};
}

#[cfg(not(feature = "CFBase"))]
#[doc(hidden)]
#[macro_export]
macro_rules! __cf_type_no_superclass {
($ty:ty) => {};
}

#[cfg(feature = "objc2")]
#[doc(hidden)]
#[macro_export]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,10 @@ typedef.CFTypeID.skipped = true
typedef.CFOptionFlags.skipped = true
typedef.CFHashCode.skipped = true
typedef.CFIndex.skipped = true

fn.CFGetTypeID.unsafe = false
fn.CFCopyTypeIDDescription.unsafe = false
fn.CFGetRetainCount.unsafe = false
fn.CFEqual.unsafe = false
fn.CFHash.unsafe = false
fn.CFCopyDescription.unsafe = false
2 changes: 1 addition & 1 deletion generated

0 comments on commit de1c9d9

Please sign in to comment.