use std::ffi::CString;
use std::mem;
use libc::size_t;
use {encode, Encode};
use runtime::{Class, Imp, Sel};
use runtime;
pub struct MethodDecl {
pub sel: Sel,
pub imp: Imp,
pub types: String,
}
pub struct ClassDecl {
cls: *mut Class,
}
impl ClassDecl {
pub fn new(superclass: &Class, name: &str) -> Option<ClassDecl> {
let name = CString::from_slice(name.as_bytes());
let cls = unsafe {
runtime::objc_allocateClassPair(superclass, name.as_ptr(), 0)
};
if cls.is_null() {
None
} else {
Some(ClassDecl { cls: cls })
}
}
pub fn add_method(&mut self, method: MethodDecl) -> bool {
let MethodDecl { sel, imp, types } = method;
let types = CString::from_vec(types.into_bytes());
unsafe {
runtime::class_addMethod(self.cls, sel, imp, types.as_ptr())
}
}
pub fn add_ivar<T: Encode>(&mut self, name: &str) -> bool {
let name = CString::from_slice(name.as_bytes());
let types = CString::from_slice(encode::<T>().as_bytes());
let size = mem::size_of::<T>() as size_t;
let align = mem::align_of::<T>() as u8;
unsafe {
runtime::class_addIvar(self.cls, name.as_ptr(), size, align,
types.as_ptr())
}
}
pub fn register(self) -> &'static Class {
unsafe {
let cls = self.cls;
runtime::objc_registerClassPair(cls);
mem::forget(self);
&*cls
}
}
}
impl Drop for ClassDecl {
fn drop(&mut self) {
unsafe {
runtime::objc_disposeClassPair(self.cls);
}
}
}
#[cfg(test)]
mod tests {
use runtime::{Class, Object};
use super::ClassDecl;
#[test]
fn test_custom_class() {
let superclass = Class::get("NSObject").unwrap();
let decl = ClassDecl::new(superclass, "MyObject");
assert!(decl.is_some());
let mut decl = decl.unwrap();
decl.add_ivar::<u32>("_foo");
decl.add_method(method!(
(*mut Object)_this, doNothing; {
()
}
));
decl.add_method(method!(
(&mut Object)this, setFoo:(u32)foo, bar:(u32)_bar; {
unsafe {
this.set_ivar::<u32>("_foo", foo);
}
}
));
decl.add_method(method!(
(&Object)this, foo -> u32, {
unsafe {
*this.get_ivar::<u32>("_foo")
}
}
));
decl.add_method(method!(
(*mut Object)this, doSomethingWithFoo:(u32)_foo, bar:(u32)_bar -> *mut Object, {
this
}
));
let cls = decl.register();
unsafe {
let obj = msg_send![cls, alloc];
let obj = msg_send![obj, init];
msg_send![obj, doNothing];
msg_send![obj, setFoo:13u32 bar:0u32];
let result = msg_send![obj, foo] as u32;
assert!(result == 13);
let result = msg_send![obj, doSomethingWithFoo:13u32 bar:0u32];
assert!(result == obj);
msg_send![obj, release];
}
}
}