use std::fmt::Debug;
use std::marker::PhantomData;
use std::os::fd::FromRawFd;
use std::os::unix::net::UnixStream;
use std::os::unix::prelude::RawFd;
use dispatch_executor::{SyncClone, SyncDrop};
use objc2::rc::Retained;
use objc2_core_bluetooth::{CBL2CAPChannel, CBPeer};
use objc2_core_foundation::{CFString, kCFStreamPropertySocketNativeHandle};
use objc2_foundation::{NSData, NSString};
#[derive(Debug)]
pub struct L2capChannel<P> {
pub(crate) channel: Retained<CBL2CAPChannel>,
phantom: PhantomData<P>,
}
impl<P> Clone for L2capChannel<P> {
fn clone(&self) -> Self {
Self {
channel: self.channel.clone(),
phantom: PhantomData,
}
}
}
unsafe impl<P> SyncDrop for L2capChannel<P> {}
unsafe impl<P> SyncClone for L2capChannel<P> {}
impl<P: PartialEq> PartialEq for L2capChannel<P> {
fn eq(&self, other: &Self) -> bool {
self.channel == other.channel
}
}
impl<P: Eq> Eq for L2capChannel<P> {}
impl<P: std::hash::Hash> std::hash::Hash for L2capChannel<P> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.channel.hash(state);
}
}
impl<P> L2capChannel<P> {
pub(crate) fn new(channel: Retained<CBL2CAPChannel>) -> (Self, UnixStream) {
let stream = unsafe {
let key = &*(kCFStreamPropertySocketNativeHandle.unwrap() as *const CFString
as *const NSString);
let input_stream = channel.inputStream().unwrap();
let output_stream = channel.outputStream().unwrap();
let input_native_socket = input_stream.propertyForKey(key).unwrap();
let output_native_socket = output_stream.propertyForKey(key).unwrap();
let input_native_socket: Retained<NSData> = input_native_socket.downcast().unwrap();
let output_native_socket: Retained<NSData> = output_native_socket.downcast().unwrap();
assert_eq!(input_native_socket, output_native_socket);
let fd =
RawFd::from_ne_bytes(input_native_socket.as_bytes_unchecked().try_into().unwrap());
UnixStream::from_raw_fd(fd)
};
(
Self {
channel,
phantom: PhantomData,
},
stream,
)
}
pub fn psm(&self) -> u16 {
unsafe { self.channel.PSM() }
}
pub fn peer(&self) -> P
where
P: TryFrom<Retained<CBPeer>>,
{
match unsafe { self.channel.peer() }.unwrap().try_into() {
Ok(peer) => peer,
Err(_) => panic!("Unexpected peer type for L2capChannel"),
}
}
#[doc(hidden)]
pub fn map<Q: TryFrom<Retained<CBPeer>>>(self) -> L2capChannel<Q> {
L2capChannel {
channel: self.channel,
phantom: PhantomData,
}
}
}