core_bluetooth 0.1.0

Safe API wrapper for Core Bluetooth framework
Documentation
use objc::*;
use objc::runtime::*;
use std::ffi::CStr;
use std::os::raw::*;
use std::ptr::{self, NonNull};

use std::cell::Cell;

#[allow(non_upper_case_globals)]
pub const nil: *mut Object = ptr::null_mut();

pub type NSInteger = isize;
pub type NSUInteger = usize;

#[link(name = "AppKit", kind = "framework")]
#[link(name = "Foundation", kind = "framework")]
#[link(name = "CoreBluetooth", kind = "framework")]
extern {
    pub(in crate) static CBAdvertisementDataIsConnectable: NSString;
    pub(in crate) static CBAdvertisementDataLocalNameKey: NSString;
    pub(in crate) static CBAdvertisementDataManufacturerDataKey: NSString;
    pub(in crate) static CBAdvertisementDataOverflowServiceUUIDsKey: NSString;
    pub(in crate) static CBAdvertisementDataServiceDataKey: NSString;
    pub(in crate) static CBAdvertisementDataServiceUUIDsKey: NSString;
    pub(in crate) static CBAdvertisementDataSolicitedServiceUUIDsKey: NSString;
    pub(in crate) static CBAdvertisementDataTxPowerLevelKey: NSString;
    pub(in crate) static CBCentralManagerScanOptionAllowDuplicatesKey: NSString;
    pub(in crate) static CBCentralManagerScanOptionSolicitedServiceUUIDsKey: NSString;
    pub(in crate) static CBCentralManagerOptionShowPowerAlertKey: NSString;
    pub(in crate) static CBErrorDomain: NSString;
    pub(in crate) static CBATTErrorDomain: NSString;
}

pub trait ObjectPtr {
    unsafe fn retain_count(&self) -> usize {
        let r: usize = msg_send![self.as_ptr(), retainCount];
        r
    }

    unsafe fn class(&self) -> &'static Class {
        &*object_getClass(self.as_ptr())
    }

    unsafe fn ivar(&self, name: &str) -> *mut c_void {
        *self.as_ptr().as_ref().unwrap().get_ivar::<*mut c_void>(name)
    }

    unsafe fn ivar_mut(&mut self, name: &str) -> &mut *mut c_void {
        self.as_ptr().as_mut().unwrap().get_mut_ivar::<*mut c_void>(name)
    }

    fn as_ptr(&self) -> *mut Object;
}

impl ObjectPtr for &mut Object {
    fn as_ptr(&self) -> *mut Object {
        *self as *const Object as *mut _
    }
}

impl ObjectPtr for *mut Object {
    fn as_ptr(&self) -> *mut Object {
        *self
    }
}

impl ObjectPtr for NonNull<Object> {
    fn as_ptr(&self) -> *mut Object {
        NonNull::as_ptr(*self)
    }
}

impl<T: ObjectPtr> ObjectPtr for Option<T> {
    fn as_ptr(&self) -> *mut Object {
        self.as_ref().map(|v| v.as_ptr()).unwrap_or(nil)
    }
}

#[derive(Eq, PartialEq, Hash)]
#[repr(transparent)]
pub struct StrongPtr<T: ObjectPtr>(T);

impl<T: ObjectPtr> StrongPtr<T> {
    pub unsafe fn wrap(inner: T) -> Self {
        Self(inner)
    }

    pub unsafe fn retain(inner: T) -> Self {
        objc_retain(inner.as_ptr());
        Self(inner)
    }
}

impl<T: ObjectPtr> std::ops::Deref for StrongPtr<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<T: ObjectPtr + Clone> Clone for StrongPtr<T> {
    fn clone(&self) -> Self {
        unsafe { objc_retain(self.as_ptr()); }
        Self(self.0.clone())
    }
}

impl<T: ObjectPtr> Drop for StrongPtr<T> {
    fn drop(&mut self) {
        unsafe { objc_release(self.as_ptr()); }
    }
}

impl<T: ObjectPtr + std::fmt::Debug> std::fmt::Debug for StrongPtr<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        std::fmt::Debug::fmt(&self.0, f)
    }
}

impl<T: ObjectPtr> ObjectPtr for StrongPtr<T> {
    fn as_ptr(&self) -> *mut Object {
        self.0.as_ptr()
    }
}

#[allow(non_camel_case_types)]
pub type dispatch_function_t = extern fn(*mut c_void);

pub const DISPATCH_QUEUE_SERIAL: *mut Object = ptr::null_mut();

extern "C" {
    pub fn dispatch_async_f(queue: *mut Object, context: *mut c_void, work: dispatch_function_t);
    pub fn dispatch_queue_create(label: *const c_char, attr: *mut Object) -> *mut Object;
}

object_ptr_wrapper!(NSNumber);

impl NSNumber {
    pub fn new_bool(value: bool) -> Self {
        unsafe {
            let r: *mut Object = msg_send![class!(NSNumber), numberWithBool:value];
            Self::wrap(r)
        }
    }

    pub fn get_bool(&self) -> bool {
        unsafe {
            let r: bool = msg_send![self.as_ptr(), boolValue];
            r
        }
    }

    pub fn get_i32(&self) -> i32 {
        unsafe {
            let r: i32 = msg_send![self.as_ptr(), intValue];
            r
        }
    }
}

object_ptr_wrapper!(NSString);

impl NSString {
    pub fn as_str(&self) -> &str {
        unsafe {
            let r: *const c_char = msg_send![self.as_ptr(), UTF8String];
            &*CStr::from_ptr(r).to_str().unwrap()
        }
    }

    pub fn is_equal_to_string(&self, s: NSString) -> bool {
        unsafe {
            let r: bool = msg_send![self.as_ptr(), isEqualToString:s];
            r
        }
    }
}

object_ptr_wrapper!(NSData);

impl NSData {
    pub fn from_bytes(bytes: &[u8]) -> Self {
        unsafe {
            let r: *mut Object = msg_send![class!(NSData),
                dataWithBytes:bytes.as_ptr() length:bytes.len()];
            Self::wrap(r)
        }
    }

    pub fn as_bytes(&self) -> &[u8] {
        unsafe {
            let len: usize = msg_send![self.as_ptr(), length];
            if len > 0 {
                let r: *const c_void = msg_send![self.as_ptr(), bytes];
                std::slice::from_raw_parts(r as *const u8, len)
            } else {
                &[]
            }
        }
    }
}

object_ptr_wrapper!(NSArray);

impl NSArray {
    pub fn with_capacity(capacity: NSUInteger) -> Self {
        unsafe {
            let r: *mut Object = msg_send![class!(NSMutableArray), arrayWithCapacity:capacity];
            Self::wrap(r)
        }
    }

    pub fn from_iter<T, I>(iter: I) -> Self
    where T: ObjectPtr,
          I: Iterator<Item=T>
    {
        let r = NSArray::with_capacity(iter.size_hint().0);
        for v in iter {
            r.push(v);
        }
        r
    }

    pub fn push(&self, obj: impl ObjectPtr) {
        unsafe {
            let _: () = msg_send![self.as_ptr(), addObject:obj];
        }
    }

    pub fn iter(&self) -> NSEnumerator<Self> {
        let en = unsafe {
            let en: *mut Object = msg_send![self.as_ptr(), objectEnumerator];
            en
        };
        NSEnumerator {
            owner: self,
            en,
            count: Default::default(),
        }
    }
}

object_ptr_wrapper!(NSDictionary);

impl NSDictionary {
    pub fn with_capacity(capacity: NSUInteger) -> Self {
        unsafe {
            let r: *mut Object = msg_send![class!(NSMutableDictionary), dictionaryWithCapacity:capacity];
            Self::wrap(r)
        }
    }

    pub fn get(&self, key: impl ObjectPtr) -> Option<NonNull<Object>> {
        unsafe {
            let r: *mut Object = msg_send![self.as_ptr(), objectForKey:key.as_ptr()];
            NonNull::new(r)
        }
    }

    pub fn insert(&self, key: impl ObjectPtr, value: impl ObjectPtr) {
        unsafe {
            let _: () = msg_send![self.as_ptr(), setObject:value forKey:key];
        }
    }

    pub fn keys(&self) -> NSEnumerator<Self> {
        let en = unsafe {
            let en: *mut Object = msg_send![self.as_ptr(), keyEnumerator];
            en
        };
        NSEnumerator {
            owner: self,
            en,
            count: Default::default(),
        }
    }

    pub fn iter(&self) -> NSDictionaryIter {
        NSDictionaryIter {
            keys: self.keys(),
        }
    }
}

pub struct NSDictionaryIter<'a> {
    keys: NSEnumerator<'a, NSDictionary>,
}

impl Iterator for NSDictionaryIter<'_> {
    type Item = (NonNull<Object>, NonNull<Object>);

    fn next(&mut self) -> Option<Self::Item> {
        let key = self.keys.next()?;
        let value = self.keys.owner.get(key).unwrap();
        Some((key, value))
    }
}

pub struct NSEnumerator<'a, T> {
    owner: &'a T,
    en: *mut Object,
    count: Cell<Option<NSUInteger>>,
}

impl<T: ObjectPtr> Iterator for NSEnumerator<'_, T> {
    type Item = NonNull<Object>;

    fn next(&mut self) -> Option<Self::Item> {
        unsafe {
            let r: *mut Object = msg_send![self.en.as_ptr(), nextObject];
            NonNull::new(r)
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        if self.count.get().is_none() {
            let v = unsafe {
                let v: NSUInteger = msg_send![self.owner.as_ptr(), count];
                v
            };
            self.count.set(Some(v));
        }
        let c = self.count.get().unwrap();
        (c, Some(c))
    }

    fn count(self) -> usize
        where Self: Sized
    {
        self.size_hint().0
    }
}

object_ptr_wrapper!(NSError);

impl NSError {
    pub fn code(&self) -> NSInteger {
        unsafe {
            let r: NSInteger = msg_send![self.as_ptr(), code];
            r
        }
    }

    pub fn domain(&self) -> NSString {
        unsafe {
            let r: *mut Object = msg_send![self.as_ptr(), domain];
            NSString::wrap(r)
        }
    }

    pub fn description(&self) -> NSString {
        unsafe {
            let r: *mut Object = msg_send![self.as_ptr(), localizedDescription];
            NSString::wrap(r)
        }
    }
}