use std::collections::HashMap;
use std::ffi::CString;
use std::sync::{Arc, RwLock};
use lazy_static::lazy_static;
use objc::declare::ClassDecl;
use objc::runtime::{objc_getClass, Class};
lazy_static! {
static ref CLASSES: ClassMap = ClassMap::new();
}
#[derive(Debug)]
pub(crate) struct ClassMap(RwLock<HashMap<&'static str, HashMap<&'static str, usize>>>);
impl ClassMap {
pub fn new() -> Self {
ClassMap(RwLock::new({
let mut map = HashMap::new();
map.insert("_supers", HashMap::new());
map
}))
}
pub fn load_subclass(&self, subclass_name: &'static str, superclass_name: &'static str) -> Option<*const Class> {
let reader = self.0.read().unwrap();
if let Some(inner) = (*reader).get(subclass_name) {
if let Some(class) = inner.get(superclass_name) {
return Some(*class as *const Class);
}
}
None
}
pub fn store_subclass(&self, subclass_name: &'static str, superclass_name: &'static str, class: *const Class) {
let mut writer = self.0.write().unwrap();
if let Some(map) = (*writer).get_mut(subclass_name) {
map.insert(superclass_name, class as usize);
} else {
let mut map = HashMap::new();
map.insert(superclass_name, class as usize);
(*writer).insert(subclass_name, map);
}
}
pub fn load_superclass(&self, name: &'static str) -> Option<*const Class> {
{
let reader = self.0.read().unwrap();
if let Some(superclass) = (*reader)["_supers"].get(name) {
return Some(*superclass as *const Class);
}
}
let objc_superclass_name = CString::new(name).unwrap();
let superclass = unsafe { objc_getClass(objc_superclass_name.as_ptr() as *const _) };
if superclass.is_null() {
return None;
}
{
let mut writer = self.0.write().unwrap();
if let Some(supers) = (*writer).get_mut("_supers") {
supers.insert(name, superclass as usize);
}
}
Some(superclass)
}
}
#[inline(always)]
pub fn load_or_register_class<F>(superclass_name: &'static str, subclass_name: &'static str, config: F) -> *const Class
where
F: Fn(&mut ClassDecl) + 'static
{
if let Some(subclass) = CLASSES.load_subclass(subclass_name, superclass_name) {
return subclass;
}
if let Some(superclass) = CLASSES.load_superclass(superclass_name) {
let objc_subclass_name = format!("{}_{}", subclass_name, superclass_name);
match ClassDecl::new(&objc_subclass_name, unsafe { &*superclass }) {
Some(mut decl) => {
config(&mut decl);
let class = decl.register();
CLASSES.store_subclass(subclass_name, superclass_name, class);
return class;
},
None => {
panic!(
"Subclass of type {}_{} could not be allocated.",
subclass_name, superclass_name
);
}
}
}
panic!(
"Attempted to create subclass for {}, but unable to load superclass of type {}.",
subclass_name, superclass_name
);
}