#[derive(Debug)]
pub struct MethodFamily<const INNER: u8> {}
pub type NewFamily = MethodFamily<1>;
pub type AllocFamily = MethodFamily<2>;
pub type InitFamily = MethodFamily<3>;
pub type CopyFamily = MethodFamily<4>;
pub type MutableCopyFamily = MethodFamily<5>;
pub type NoneFamily = MethodFamily<6>;
pub type RetainSelector = MethodFamily<8>;
pub type ReleaseSelector = MethodFamily<9>;
pub type AutoreleaseSelector = MethodFamily<10>;
pub type DeallocSelector = MethodFamily<11>;
#[allow(non_camel_case_types)]
pub mod method_family_import {
pub use super::{
AllocFamily as alloc, CopyFamily as copy, InitFamily as init,
MutableCopyFamily as mutableCopy, NewFamily as new, NoneFamily as none,
};
}
pub const fn method_family(first_selector_part: &str) -> u8 {
let first_selector_part = first_selector_part.as_bytes();
match (
in_selector_family(first_selector_part, b"new"),
in_selector_family(first_selector_part, b"alloc"),
in_selector_family(first_selector_part, b"init"),
in_selector_family(first_selector_part, b"copy"),
in_selector_family(first_selector_part, b"mutableCopy"),
) {
(true, false, false, false, false) => 1,
(false, true, false, false, false) => 2,
(false, false, true, false, false) => 3,
(false, false, false, true, false) => 4,
(false, false, false, false, true) => 5,
(false, false, false, false, false) => 6,
_ => unreachable!(),
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __method_family {
(
($($method_family:tt)+)
($($sel:tt)*)
) => {
$crate::__macro_helpers::method_family_import::$($method_family)+
};
(
()
(alloc)
) => {
$crate::__macro_helpers::AllocFamily
};
(
()
(new)
) => {
$crate::__macro_helpers::NewFamily
};
(
()
(init)
) => {
$crate::__macro_helpers::InitFamily
};
(
()
(dealloc)
) => {
$crate::__macro_helpers::DeallocSelector
};
(
()
(retain)
) => {
$crate::__macro_helpers::RetainSelector
};
(
()
(release)
) => {
$crate::__macro_helpers::ReleaseSelector
};
(
()
(autorelease)
) => {
$crate::__macro_helpers::AutoreleaseSelector
};
(
()
($sel_first:tt $($sel_rest:tt)*)
) => {
$crate::__macro_helpers::MethodFamily<{
$crate::__macro_helpers::method_family($crate::__macro_helpers::stringify!($sel_first))
}>
};
(
()
()
) => {
$crate::__macro_helpers::MethodFamily<{
$crate::__macro_helpers::compile_error!("missing selector");
$crate::__macro_helpers::method_family("")
}>
};
}
const fn in_selector_family(mut selector: &[u8], mut family: &[u8]) -> bool {
loop {
selector = match selector {
[b'_', rest @ ..] => rest,
_ => break,
}
}
loop {
(selector, family) = match (selector, family) {
([s, selector @ ..], [f, family @ ..]) => {
if *s == *f {
(selector, family)
} else {
return false;
}
}
([], []) => {
return true;
}
([], _) => {
return false;
}
([s, ..], []) => {
return !s.is_ascii_lowercase();
}
}
}
}
#[cfg(test)]
mod tests {
use alloc::string::ToString;
use super::*;
#[test]
fn test_in_selector_family() {
#[track_caller]
fn assert_in_family(selector: &str, family: &str) {
assert!(in_selector_family(selector.as_bytes(), family.as_bytes()));
let selector = selector.to_string() + "\0";
assert!(in_selector_family(selector.as_bytes(), family.as_bytes()));
}
#[track_caller]
fn assert_not_in_family(selector: &str, family: &str) {
assert!(!in_selector_family(selector.as_bytes(), family.as_bytes()));
let selector = selector.to_string() + "\0";
assert!(!in_selector_family(selector.as_bytes(), family.as_bytes()));
}
assert_in_family("alloc", "alloc");
assert_in_family("allocWithZone:", "alloc");
assert_not_in_family("dealloc", "alloc");
assert_not_in_family("initialize", "init");
assert_not_in_family("decimalNumberWithDecimal:", "init");
assert_in_family("initWithCapacity:", "init");
assert_in_family("_initButPrivate:withParam:", "init");
assert_not_in_family("description", "init");
assert_not_in_family("inIT", "init");
assert_not_in_family("init", "copy");
assert_not_in_family("copyingStuff:", "copy");
assert_in_family("copyWithZone:", "copy");
assert_not_in_family("initWithArray:copyItems:", "copy");
assert_in_family("copyItemAtURL:toURL:error:", "copy");
assert_not_in_family("mutableCopying", "mutableCopy");
assert_in_family("mutableCopyWithZone:", "mutableCopy");
assert_in_family("mutableCopyWithZone:", "mutableCopy");
assert_in_family(
"newScriptingObjectOfClass:forValueForKey:withContentsValue:properties:",
"new",
);
assert_in_family(
"newScriptingObjectOfClass:forValueForKey:withContentsValue:properties:",
"new",
);
assert_not_in_family("newsstandAssetDownload", "new");
assert_in_family("__abcDef", "abc");
assert_in_family("_abcDef", "abc");
assert_in_family("abcDef", "abc");
assert_in_family("___a", "a");
assert_in_family("__a", "a");
assert_in_family("_a", "a");
assert_in_family("a", "a");
assert_not_in_family("_abcdef", "abc");
assert_not_in_family("_abcdef", "def");
assert_not_in_family("_bcdef", "abc");
assert_not_in_family("a_bc", "abc");
assert_not_in_family("abcdef", "abc");
assert_not_in_family("abcdef", "def");
assert_not_in_family("abcdef", "abb");
assert_not_in_family("___", "a");
assert_not_in_family("_", "a");
assert_not_in_family("", "a");
assert_in_family("copy", "copy");
assert_in_family("copy:", "copy");
assert_in_family("copyMe", "copy");
assert_in_family("_copy", "copy");
assert_in_family("_copy:", "copy");
assert_in_family("_copyMe", "copy");
assert_not_in_family("copying", "copy");
assert_not_in_family("copying:", "copy");
assert_not_in_family("_copying", "copy");
assert_not_in_family("Copy", "copy");
assert_not_in_family("COPY", "copy");
assert_in_family("___", "");
assert_in_family("__", "");
assert_in_family("_", "");
assert_in_family("", "");
assert_not_in_family("_a", "");
assert_not_in_family("a", "");
assert_in_family("_A", "");
assert_in_family("A", "");
assert_in_family("abc::abc::", "abc");
assert_in_family("abc:::", "abc");
assert_in_family("abcDef::xyz:", "abc");
assert_not_in_family("::abc:", "abc");
}
#[test]
fn test_method_family() {
#[track_caller]
fn assert_types_eq<T: 'static, U: 'static>() {
assert_eq!(std::any::TypeId::of::<T>(), std::any::TypeId::of::<U>());
}
assert_types_eq::<AllocFamily, __method_family!(()(alloc))>();
assert_types_eq::<AllocFamily, __method_family!(()(allocWithZone:))>();
assert_types_eq::<CopyFamily, __method_family!(()(copyItemAtURL:toURL:error:))>();
assert_types_eq::<NewFamily, __method_family!(()(new))>();
assert_types_eq::<InitFamily, __method_family!(()(initWithArray:))>();
assert_types_eq::<NoneFamily, __method_family!(()(somethingElse:))>();
assert_types_eq::<CopyFamily, __method_family!((copy)(initWithArray:))>();
}
}