#[doc(alias = "@protocol")]
#[macro_export]
macro_rules! extern_protocol {
(
// The special #[name = $name:literal] attribute is supported here.
$(#[$($attrs:tt)*])*
$v:vis unsafe trait $protocol:ident $(: $conforms_to:ident $(+ $conforms_to_rest:ident)*)? {
$($methods:tt)*
}
) => {
$crate::__extract_struct_attributes! {
($(#[$($attrs)*])*)
($crate::__inner_extern_protocol)
($protocol)
($v unsafe trait $protocol $(: $conforms_to $(+ $conforms_to_rest)*)? {
$crate::__extern_protocol_rewrite_methods! {
$($methods)*
}
})
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __inner_extern_protocol {
(
($protocol:ident)
($protocol_definition:item)
($($superclasses:tt)*)
($($thread_kind:tt)*)
($($name:tt)*)
($($ivars:tt)*)
($($derives:tt)*)
($($attr_protocol:tt)*)
($($attr_impl:tt)*)
) => {
$($attr_protocol)*
$protocol_definition
$($attr_impl)*
unsafe impl<T> $protocol for $crate::runtime::ProtocolObject<T>
where
T: ?$crate::__macro_helpers::Sized + $protocol
{}
$($attr_impl)*
unsafe impl $crate::ProtocolType for dyn $protocol {
const NAME: &'static $crate::__macro_helpers::str = $crate::__fallback_if_not_set! {
($($name)*)
($crate::__macro_helpers::stringify!($protocol))
};
const __INNER: () = ();
}
$($attr_impl)*
unsafe impl<T> $crate::runtime::ImplementedBy<T> for dyn $protocol
where
T: ?$crate::__macro_helpers::Sized + $crate::Message + $protocol
{
const __INNER: () = ();
}
$crate::__extern_protocol_check_no_super!($($superclasses)*);
$crate::__extern_protocol_check_no_thread_kind!($($thread_kind)*);
$crate::__extern_protocol_check_no_ivars!($($ivars)*);
$crate::__extern_protocol_check_no_derives!($($derives)*);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __extern_protocol_check_no_super {
() => {};
($($ivars:tt)*) => {
$crate::__macro_helpers::compile_error!("#[super] is not supported in extern_protocol!");
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __extern_protocol_check_no_thread_kind {
() => {};
($($ivars:tt)*) => {
$crate::__macro_helpers::compile_error!(
"#[thread_kind = ...] is not supported in extern_protocol!. Add MainThreadOnly or AnyThread bound instead"
);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __extern_protocol_check_no_ivars {
() => {};
($($ivars:tt)*) => {
$crate::__macro_helpers::compile_error!("#[ivars] is not supported in extern_protocol!");
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __extern_protocol_check_no_derives {
() => {};
($($ivars:tt)*) => {
$crate::__macro_helpers::compile_error!(
"#[derive(...)] is not supported in extern_protocol!"
);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __extern_protocol_rewrite_methods {
{} => {};
{
$(#[$($m:tt)*])*
$v:vis unsafe fn $name:ident($($params:tt)*) $(-> $ret:ty)?
$(where $($where:ty : $bound:path),+ $(,)?)?;
$($rest:tt)*
} => {
$crate::__rewrite_self_param! {
($($params)*)
($crate::__extract_method_attributes)
($(#[$($m)*])*)
($crate::__extern_protocol_method_out)
($v unsafe fn $name($($params)*) $(-> $ret)?)
($($($where : $bound ,)+)?)
}
$crate::__extern_protocol_rewrite_methods! {
$($rest)*
}
};
{
$(#[$($m:tt)*])*
$v:vis fn $name:ident($($params:tt)*) $(-> $ret:ty)?
$(where $($where:ty : $bound:path),+ $(,)?)?;
$($rest:tt)*
} => {
$crate::__rewrite_self_param! {
($($params)*)
($crate::__extract_method_attributes)
($(#[$($m)*])*)
($crate::__extern_protocol_method_out)
($v fn $name($($params)*) $(-> $ret)?)
($($($where : $bound ,)+)?)
}
$crate::__extern_protocol_rewrite_methods! {
$($rest)*
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __extern_protocol_method_out {
{
($($function_start:tt)*)
($($where:ty : $bound:path ,)*)
(add_method)
($receiver:expr)
($__receiver_ty:ty)
($($__params_prefix:tt)*)
($($params_rest:tt)*)
($method_or_method_id:ident($($sel:tt)*))
($($method_family:tt)*)
($($optional:tt)*)
($($attr_method:tt)*)
($($attr_use:tt)*)
} => {
$($attr_method)*
$($function_start)*
where
Self: $crate::__macro_helpers::Sized + $crate::Message
$(, $where : $bound)*
{
$crate::__extern_methods_method_id_deprecated!($method_or_method_id($($sel)*));
#[allow(unused_unsafe)]
unsafe {
$crate::__method_msg_send! {
($receiver)
($($sel)*)
($($params_rest)*)
()
()
($($method_family)*)
}
}
}
};
{
($($function_start:tt)*)
($($where:ty : $bound:path ,)*)
(add_class_method)
($receiver:expr)
($__receiver_ty:ty)
($($__params_prefix:tt)*)
($($params_rest:tt)*)
($method_or_method_id:ident($($sel:tt)*))
($($method_family:tt)*)
($($optional:tt)*)
($($attr_method:tt)*)
($($attr_use:tt)*)
} => {
$($attr_method)*
$($function_start)*
where
Self: $crate::__macro_helpers::Sized + $crate::ClassType
$(, $where : $bound)*
{
$crate::__extern_methods_method_id_deprecated!($method_or_method_id($($sel)*));
#[allow(unused_unsafe)]
unsafe {
$crate::__method_msg_send! {
($receiver)
($($sel)*)
($($params_rest)*)
()
()
($($method_family)*)
}
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __extern_protocol_method_id_deprecated {
(method($($sel:tt)*)) => {};
(method_id($($sel:tt)*)) => {{
#[deprecated = $crate::__macro_helpers::concat!(
"using #[unsafe(method_id(",
$crate::__macro_helpers::stringify!($($sel)*),
"))] inside extern_protocol! is deprecated.\nUse #[unsafe(method(",
$crate::__macro_helpers::stringify!($($sel)*),
"))] instead",
)]
#[inline]
fn method_id() {}
method_id();
}};
}
#[cfg(test)]
mod tests {
use crate::{extern_protocol, ProtocolType};
#[test]
fn explicit_name() {
extern_protocol!(
#[allow(clippy::missing_safety_doc)]
#[name = "NSObject"]
unsafe trait Foo {}
);
let proto = <dyn Foo>::protocol().unwrap();
assert_eq!(proto.name().to_str().unwrap(), "NSObject");
assert_eq!(<dyn Foo>::NAME, "NSObject");
}
}