<commit_before>// Copyright 2014 the objc-rs developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! An Objective-C runtime wrapper for Rust.

#![feature(phase)]

extern crate libc;

use std::c_vec::CVec;
use std::fmt;
use std::mem;
use std::string;

/// Foreign functions and types for the Objective-C bridging API.
#[cfg(target_os="macos")]
#[allow(dead_code)]
#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
#[allow(non_upper_case_globals)]
pub mod ffi {
    #[phase(plugin)]
    extern crate bindgen;

    pub const nil: id = 0 as id;
    pub const Nil: Class = 0 as Class;

    pub const YES: BOOL = 1;
    pub const NO:  BOOL = 0;

    pub type __builtin_va_list = ();

    bindgen!("headers.h", link = "objc",
             clang_args = "-I/System/Library/Frameworks/Kernel.framework/Versions/Current/Headers")
}

#[cfg(not(target_os="macos"))]
pub mod ffi {
    #[phase(plugin)]
    extern crate compile_msg;

    compile_error!("The `objc` crate only supports OS X")
}

/// An Objective-C class definition.
#[deriving(PartialEq, Eq, Hash)]
pub struct Class {
    pub raw: ffi::Class,
}

/// A convenience wrapper for `Class::get`.
pub unsafe fn class(name: &str) -> Class {
    Class::get(name)
}

impl Class {
    #[inline]
    pub fn nil() -> Class {
        Class { raw: ffi::Nil }
    }

    #[inline]
    pub fn as_id(self) -> Id {
        Id { raw: self.raw as ffi::id }
    }

    // Working With Classes

    pub unsafe fn get_name(self) -> String {
        string::raw::from_buf(ffi::class_getName(self.raw) as *const libc::c_uchar)
    }

    pub unsafe fn get_super_class(self) -> Class {
        Class { raw: ffi::class_getSuperclass(self.raw) }
    }

    pub unsafe fn is_meta_class(self) -> bool {
        ffi::class_isMetaClass(self.raw) == ffi::YES
    }

    pub unsafe fn get_instance_size(self) -> uint {
        ffi::class_getInstanceSize(self.raw) as uint
    }

    pub unsafe fn get_instance_variable(self, name: &str) -> ffi::Ivar {
        ffi::class_getInstanceVariable(self.raw, name.to_c_str().as_ptr())
    }

    pub unsafe fn get_class_variable(self, name: &str) -> ffi::Ivar {
        ffi::class_getClassVariable(self.raw, name.to_c_str().as_ptr())
    }

    pub unsafe fn add_ivar_raw(self, name: &str, size: uint, alignment: uint, types: &str) -> bool {
        ffi::class_addIvar(self.raw,
                           name.to_c_str().as_ptr(),
                           size as libc::size_t,
                           alignment as u8,
                           types.to_c_str().as_ptr()) == ffi::YES
    }

    pub unsafe fn add_ivar<T>(self, name: &str, types: &str) -> bool {
        self.add_ivar_raw(name, mem::size_of::<T>(), mem::align_of::<T>(), types)
    }

    pub unsafe fn copy_ivar_list(self) -> CVec<ffi::Ivar> {
        let mut count = 0;
        let ptr = ffi::class_copyIvarList(self.raw, &mut count);
        CVec::new_with_dtor(ptr, count as uint, proc() {
            libc::free(ptr as *mut libc::c_void);
        })
    }

    pub unsafe fn get_ivar_layout(self) -> String {
        string::raw::from_buf(ffi::class_getIvarLayout(self.raw))
    }

    pub unsafe fn set_ivar_layout(self, layout: &str) {
        ffi::class_setIvarLayout(self.raw, layout.to_c_str().as_ptr() as *const u8);
    }

    pub unsafe fn get_weak_ivar_layout(self) -> String {
        string::raw::from_buf(ffi::class_getWeakIvarLayout(self.raw))
    }

    pub unsafe fn set_weak_ivar_layout(self, layout: &str) {
        ffi::class_setWeakIvarLayout(self.raw, layout.to_c_str().as_ptr() as *const u8);
    }

    pub unsafe fn get_property(self, name: &str) -> ffi::objc_property_t {
        ffi::class_getProperty(self.raw, name.to_c_str().as_ptr())
    }

    pub unsafe fn copy_property_list(self) -> CVec<ffi::objc_property_t> {
        let mut count = 0;
        let ptr = ffi::class_copyPropertyList(self.raw, &mut count);
        CVec::new_with_dtor(ptr, count as uint, proc() {
            libc::free(ptr as *mut libc::c_void);
        })
    }

    pub unsafe fn add_method(self, name: Selector, imp: Impl, types: &str) -> bool {
        ffi::class_addMethod(self.raw, name.raw, mem::transmute(imp),
                             types.to_c_str().as_ptr()) == ffi::YES
    }

    pub unsafe fn get_instance_method(self, name: Selector) -> ffi::Method {
        ffi::class_getInstanceMethod(self.raw, name.raw)
    }

    pub unsafe fn get_class_method(self, name: Selector) -> ffi::Method {
        ffi::class_getClassMethod(self.raw, name.raw)
    }

    pub unsafe fn copy_method_list(self) -> CVec<ffi::Method> {
        let mut count = 0;
        let ptr = ffi::class_copyMethodList(self.raw, &mut count);
        CVec::new_with_dtor(ptr, count as uint, proc() {
            libc::free(ptr as *mut libc::c_void);
        })
    }

    pub unsafe fn replace_method(self, name: Selector, imp: Impl, types: &str) -> Impl {
        mem::transmute(ffi::class_replaceMethod(self.raw, name.raw, mem::transmute(imp),
                                                types.to_c_str().as_ptr()))
    }

    pub unsafe fn get_method_implementation(self, name: Selector) -> Impl {
        mem::transmute(ffi::class_getMethodImplementation(self.raw, name.raw))
    }

    // TODO: class_getMethodImplementation_stret

    pub unsafe fn responds_to_selector(self, name: ffi::SEL) -> bool {
        ffi::class_respondsToSelector(self.raw, name) == ffi::YES
    }

    pub unsafe fn add_protocol(self, protocol: *mut ffi::Protocol) -> bool {
        ffi::class_addProtocol(self.raw, protocol) == ffi::YES
    }

    pub unsafe fn add_property(self, name: &str, attributes: &[ffi::objc_property_attribute_t]) -> bool {
        ffi::class_addProperty(self.raw,
                               name.to_c_str().as_ptr(),
                               attributes.as_ptr(),
                               attributes.len() as libc::c_uint) == ffi::YES
    }

    pub unsafe fn replace_property(self, name: &str, attributes: &[ffi::objc_property_attribute_t]) {
        ffi::class_replaceProperty(self.raw,
                                   name.to_c_str().as_ptr(),
                                   attributes.as_ptr(),
                                   attributes.len() as libc::c_uint);
    }

    pub unsafe fn conforms_to_protocol(self, protocol: *mut ffi::Protocol) -> bool {
        ffi::class_conformsToProtocol(self.raw, protocol) == ffi::YES
    }

    pub unsafe fn copy_protocol_list(self) -> CVec<*mut ffi::Protocol> {
        let mut count = 0;
        let ptr = ffi::class_copyProtocolList(self.raw, &mut count);
        CVec::new_with_dtor(ptr, count as uint, proc() {
            libc::free(ptr as *mut libc::c_void);
        })
    }

    pub unsafe fn get_version(self) -> int {
        ffi::class_getVersion(self.raw) as int
    }

    pub unsafe fn set_version(self, version: int) {
        ffi::class_setVersion(self.raw, version as libc::c_int)
    }

    // skipped: objc_getFutureClass (not intended for users)
    // skipped: objc_setFutureClass (not intended for users)

    // Adding Classes

    pub unsafe fn allocate_class_pair(self, name: &str, extra_bytes: uint) -> Class {
        Class {
            raw: ffi::objc_allocateClassPair(self.raw, name.to_c_str().as_ptr(),
                                             extra_bytes as libc::size_t),
        }
    }

    pub unsafe fn dispose_class_pair(self) {
        ffi::objc_disposeClassPair(self.raw);
    }

    pub unsafe fn register_class_pair(self) {
        ffi::objc_registerClassPair(self.raw);
    }

    // skipped: objc_duplicateClass (not intended for users)

    // Instantiating Classes

    pub unsafe fn create_instance(self, extra_bytes: uint) -> Id {
        Id { raw: ffi::class_createInstance(self.raw, extra_bytes as libc::size_t) }
    }

    pub unsafe fn construct_instance(self, bytes: *mut libc::c_void) -> Id {
        Id { raw: ffi::objc_constructInstance(self.raw, bytes) }
    }

    // Obtaining Class Definitions

    // FIXME: dunno how best to wrap this
    pub unsafe fn get_list(buffer: *mut ffi::Class, buffer_count: libc::c_int) -> libc::c_int {
        ffi::objc_getClassList(buffer, buffer_count)
    }

    // FIXME: should return a vector of `Class`, not `ffi::Class`
    pub unsafe fn copy_list() -> CVec<ffi::Class> {
        let mut count = 0;
        let ptr = ffi::objc_copyClassList(&mut count);
        CVec::new_with_dtor(ptr, count as uint, proc() {
            libc::free(ptr as *mut libc::c_void);
        })
    }

    pub unsafe fn look_up(name: &str) -> Class {
        Class { raw: ffi::objc_lookUpClass(name.to_c_str().as_ptr()) }
    }

    pub unsafe fn get(name: &str) -> Class {
        Class { raw: ffi::objc_getClass(name.to_c_str().as_ptr()) }
    }

    pub unsafe fn get_required(name: &str) -> Class {
        Class { raw: ffi::objc_getRequiredClass(name.to_c_str().as_ptr()) }
    }

    pub unsafe fn get_meta(name: &str) -> Class {
        Class { raw: ffi::objc_getMetaClass(name.to_c_str().as_ptr()) }
    }

    // Working with Libraries

    pub unsafe fn get_image_name(self) -> String {
        string::raw::from_buf(ffi::class_getImageName(self.raw) as *const libc::c_uchar)
    }
}

impl fmt::Show for Class {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        unsafe {
            if *self != Class::nil() {
                write!(f, "{}", self.get_name())
            } else {
                write!(f, "nil")
            }
        }
    }
}

/// A pointer to an instance of a class.
#[deriving(PartialEq, Eq, Hash)]
pub struct Id {
    pub raw: ffi::id,
}

impl fmt::Show for Id {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        unsafe {
            if *self != Id::nil() {
                write!(f, "instance of {}", self.isa().get_name())
            } else {
                write!(f, "nil")
            }
        }
    }
}

impl Id {
    /// A null instance.
    #[inline]
    pub fn nil() -> Id {
        Id { raw: ffi::nil }
    }

    /// The class definition of which this object is an instance
    #[inline]
    pub unsafe fn isa(self) -> Class {
        Class { raw: (*self.raw).isa }
    }

    // Instantiating Classes

    pub unsafe fn destruct_instance(self) -> *mut libc::c_void {
        ffi::objc_destructInstance(self.raw)
    }

    // Working with Instances

    pub unsafe fn copy(self, size: uint) -> Id {
        Id { raw: ffi::object_copy(self.raw, size as libc::size_t) }
    }

    pub unsafe fn dispose(self) -> Id {
        Id { raw: ffi::object_dispose(self.raw) }
    }

    // Associative References

    // objc_setAssociatedObject
    // objc_getAssociatedObject
    // objc_removeAssociatedObjects
}

/// An instance variable.
pub struct InstanceVariable {
    pub raw: ffi::Ivar,
}

impl InstanceVariable {
    // Working with Instance Variables

    pub unsafe fn get_name(self) -> String {
        string::raw::from_buf(ffi::ivar_getName(self.raw) as *const libc::c_uchar)
    }

    pub unsafe fn get_type_encoding(self) -> String {
        string::raw::from_buf(ffi::ivar_getTypeEncoding(self.raw) as *const libc::c_uchar)
    }

    pub unsafe fn get_offset(self) -> int {
        ffi::ivar_getOffset(self.raw) as int
    }
}

/// The superclass of an instance.
pub struct Super {
    pub raw: *mut ffi::Struct_objc_super,
}

impl Super {
    /// The instance of a class.
    #[inline]
    pub unsafe fn receiver(self) -> Id {
        Id { raw: (*self.raw).receiver }
    }

    /// The superclass to message.
    #[inline]
    pub unsafe fn class(self) -> Class {
        Class { raw: (*self.raw).class }
    }
}

// Sending Messages

/// Returns a function that sends a message to an instance of a class,
/// returning a `T`.
pub fn msg_send<T: 'static>() -> unsafe extern "C" fn(Id, Selector, ...) -> T {
    unsafe {
        use std::intrinsics::type_id;
        match type_id::<T>() {
            // On the i386 platform, the ABI for functions returning a
            // floating-point value is incompatible with that for functions
            // returning an integral type.
            id if (id == type_id::<libc::c_float>()  && cfg!(target_word_size = "32"))
               || (id == type_id::<libc::c_double>() && cfg!(target_word_size = "32"))
                                        => mem::transmute(ffi::objc_msgSend_fpret),
            id if id == type_id::<Id>() => mem::transmute(ffi::objc_msgSend),
            _                           => mem::transmute(ffi::objc_msgSend_stret),

        }
    }
}

/// Returns a function that sends a message to a superclass of an instance of a
/// class, returning a `T`.
pub fn msg_send_super<T: 'static>() -> unsafe extern "C" fn(Super, Selector, ...) -> T {
    unsafe {
        use std::intrinsics::type_id;
        match type_id::<T>() {
            id if id == type_id::<Id>() => mem::transmute(ffi::objc_msgSendSuper),
            _                           => mem::transmute(ffi::objc_msgSendSuper_stret),
        }
    }
}

// Working with Methods

// method_invoke
// method_invoke_stret
// method_getName
// method_getImplementation
// method_getTypeEncoding
// method_copyReturnType
// method_copyArgumentType
// method_getReturnType
// method_getNumberOfArguments
// method_getArgumentType
// method_getDescription
// method_setImplementation
// method_exchangeImplementations

// Working with Libraries

/// Returns the names of all the loaded Objective-C frameworks and dynamic
/// libraries.
pub unsafe fn copy_image_names() -> Vec<String> {
    let mut count = 0;
    let ptr = ffi::objc_copyImageNames(&mut count);
    range(0, count as int).map(|i| {
        string::raw::from_buf(*ptr.offset(i) as *const libc::c_uchar)<commit_msg>Add method stuff<commit_after>// Copyright 2014 the objc-rs developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! An Objective-C runtime wrapper for Rust.

#![feature(phase)]

extern crate libc;

use std::c_str::CString;
use std::c_vec::CVec;
use std::fmt;
use std::intrinsics::type_id;
use std::mem;
use std::string;

pub use ffi::NSUInteger;
pub use ffi::NSInteger;

/// Foreign functions and types for the Objective-C bridging API.
#[cfg(target_os="macos")]
#[allow(dead_code)]
#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
#[allow(non_upper_case_globals)]
pub mod ffi {
    #[phase(plugin)]
    extern crate bindgen;

    pub const nil: id = 0 as id;
    pub const Nil: Class = 0 as Class;

    pub const YES: BOOL = 1;
    pub const NO:  BOOL = 0;

    pub type __builtin_va_list = ();

    bindgen!("headers.h", link = "objc",
             clang_args = "-I/System/Library/Frameworks/Kernel.framework/Versions/Current/Headers")
}

#[cfg(not(target_os="macos"))]
pub mod ffi {
    #[phase(plugin)]
    extern crate compile_msg;

    compile_error!("The `objc` crate only supports OS X")
}

/// An Objective-C class definition.
#[deriving(PartialEq, Eq, Hash)]
pub struct Class {
    pub raw: ffi::Class,
}

/// A convenience wrapper for `Class::get`.
pub unsafe fn class(name: &str) -> Class {
    Class::get(name)
}

impl Class {
    #[inline]
    pub fn nil() -> Class {
        Class { raw: ffi::Nil }
    }

    #[inline]
    pub fn as_id(self) -> Id {
        Id { raw: self.raw as ffi::id }
    }

    // Working With Classes

    pub unsafe fn get_name(self) -> String {
        string::raw::from_buf(ffi::class_getName(self.raw) as *const libc::c_uchar)
    }

    pub unsafe fn get_super_class(self) -> Class {
        Class { raw: ffi::class_getSuperclass(self.raw) }
    }

    pub unsafe fn is_meta_class(self) -> bool {
        ffi::class_isMetaClass(self.raw) == ffi::YES
    }

    pub unsafe fn get_instance_size(self) -> uint {
        ffi::class_getInstanceSize(self.raw) as uint
    }

    pub unsafe fn get_instance_variable(self, name: &str) -> ffi::Ivar {
        ffi::class_getInstanceVariable(self.raw, name.to_c_str().as_ptr())
    }

    pub unsafe fn get_class_variable(self, name: &str) -> ffi::Ivar {
        ffi::class_getClassVariable(self.raw, name.to_c_str().as_ptr())
    }

    pub unsafe fn add_ivar_raw(self, name: &str, size: uint, alignment: uint, types: &str) -> bool {
        ffi::class_addIvar(self.raw,
                           name.to_c_str().as_ptr(),
                           size as libc::size_t,
                           alignment as u8,
                           types.to_c_str().as_ptr()) == ffi::YES
    }

    pub unsafe fn add_ivar<T>(self, name: &str, types: &str) -> bool {
        self.add_ivar_raw(name, mem::size_of::<T>(), mem::align_of::<T>(), types)
    }

    pub unsafe fn copy_ivar_list(self) -> CVec<ffi::Ivar> {
        let mut count = 0;
        let ptr = ffi::class_copyIvarList(self.raw, &mut count);
        CVec::new_with_dtor(ptr, count as uint, proc() {
            libc::free(ptr as *mut libc::c_void);
        })
    }

    pub unsafe fn get_ivar_layout(self) -> String {
        string::raw::from_buf(ffi::class_getIvarLayout(self.raw))
    }

    pub unsafe fn set_ivar_layout(self, layout: &str) {
        ffi::class_setIvarLayout(self.raw, layout.to_c_str().as_ptr() as *const u8);
    }

    pub unsafe fn get_weak_ivar_layout(self) -> String {
        string::raw::from_buf(ffi::class_getWeakIvarLayout(self.raw))
    }

    pub unsafe fn set_weak_ivar_layout(self, layout: &str) {
        ffi::class_setWeakIvarLayout(self.raw, layout.to_c_str().as_ptr() as *const u8);
    }

    pub unsafe fn get_property(self, name: &str) -> ffi::objc_property_t {
        ffi::class_getProperty(self.raw, name.to_c_str().as_ptr())
    }

    pub unsafe fn copy_property_list(self) -> CVec<ffi::objc_property_t> {
        let mut count = 0;
        let ptr = ffi::class_copyPropertyList(self.raw, &mut count);
        CVec::new_with_dtor(ptr, count as uint, proc() {
            libc::free(ptr as *mut libc::c_void);
        })
    }

    pub unsafe fn add_method(self, name: Selector, imp: Impl, types: &str) -> bool {
        ffi::class_addMethod(self.raw, name.raw, mem::transmute(imp),
                             types.to_c_str().as_ptr()) == ffi::YES
    }

    pub unsafe fn get_instance_method(self, name: Selector) -> ffi::Method {
        ffi::class_getInstanceMethod(self.raw, name.raw)
    }

    pub unsafe fn get_class_method(self, name: Selector) -> ffi::Method {
        ffi::class_getClassMethod(self.raw, name.raw)
    }

    pub unsafe fn copy_method_list(self) -> CVec<ffi::Method> {
        let mut count = 0;
        let ptr = ffi::class_copyMethodList(self.raw, &mut count);
        CVec::new_with_dtor(ptr, count as uint, proc() {
            libc::free(ptr as *mut libc::c_void);
        })
    }

    pub unsafe fn replace_method(self, name: Selector, imp: Impl, types: &str) -> Impl {
        mem::transmute(ffi::class_replaceMethod(self.raw, name.raw, mem::transmute(imp),
                                                types.to_c_str().as_ptr()))
    }

    pub unsafe fn get_method_implementation(self, name: Selector) -> Impl {
        mem::transmute(ffi::class_getMethodImplementation(self.raw, name.raw))
    }

    // TODO: class_getMethodImplementation_stret

    pub unsafe fn responds_to_selector(self, name: ffi::SEL) -> bool {
        ffi::class_respondsToSelector(self.raw, name) == ffi::YES
    }

    pub unsafe fn add_protocol(self, protocol: *mut ffi::Protocol) -> bool {
        ffi::class_addProtocol(self.raw, protocol) == ffi::YES
    }

    pub unsafe fn add_property(self, name: &str, attributes: &[ffi::objc_property_attribute_t]) -> bool {
        ffi::class_addProperty(self.raw,
                               name.to_c_str().as_ptr(),
                               attributes.as_ptr(),
                               attributes.len() as libc::c_uint) == ffi::YES
    }

    pub unsafe fn replace_property(self, name: &str, attributes: &[ffi::objc_property_attribute_t]) {
        ffi::class_replaceProperty(self.raw,
                                   name.to_c_str().as_ptr(),
                                   attributes.as_ptr(),
                                   attributes.len() as libc::c_uint);
    }

    pub unsafe fn conforms_to_protocol(self, protocol: *mut ffi::Protocol) -> bool {
        ffi::class_conformsToProtocol(self.raw, protocol) == ffi::YES
    }

    pub unsafe fn copy_protocol_list(self) -> CVec<*mut ffi::Protocol> {
        let mut count = 0;
        let ptr = ffi::class_copyProtocolList(self.raw, &mut count);
        CVec::new_with_dtor(ptr, count as uint, proc() {
            libc::free(ptr as *mut libc::c_void);
        })
    }

    pub unsafe fn get_version(self) -> int {
        ffi::class_getVersion(self.raw) as int
    }

    pub unsafe fn set_version(self, version: int) {
        ffi::class_setVersion(self.raw, version as libc::c_int)
    }

    // skipped: objc_getFutureClass (not intended for users)
    // skipped: objc_setFutureClass (not intended for users)

    // Adding Classes

    pub unsafe fn allocate_class_pair(self, name: &str, extra_bytes: uint) -> Class {
        Class {
            raw: ffi::objc_allocateClassPair(self.raw, name.to_c_str().as_ptr(),
                                             extra_bytes as libc::size_t),
        }
    }

    pub unsafe fn dispose_class_pair(self) {
        ffi::objc_disposeClassPair(self.raw);
    }

    pub unsafe fn register_class_pair(self) {
        ffi::objc_registerClassPair(self.raw);
    }

    // skipped: objc_duplicateClass (not intended for users)

    // Instantiating Classes

    pub unsafe fn create_instance(self, extra_bytes: uint) -> Id {
        Id { raw: ffi::class_createInstance(self.raw, extra_bytes as libc::size_t) }
    }

    pub unsafe fn construct_instance(self, bytes: *mut libc::c_void) -> Id {
        Id { raw: ffi::objc_constructInstance(self.raw, bytes) }
    }

    // Obtaining Class Definitions

    // FIXME: dunno how best to wrap this
    pub unsafe fn get_list(buffer: *mut ffi::Class, buffer_count: libc::c_int) -> libc::c_int {
        ffi::objc_getClassList(buffer, buffer_count)
    }

    // FIXME: should return a vector of `Class`, not `ffi::Class`
    pub unsafe fn copy_list() -> CVec<ffi::Class> {
        let mut count = 0;
        let ptr = ffi::objc_copyClassList(&mut count);
        CVec::new_with_dtor(ptr, count as uint, proc() {
            libc::free(ptr as *mut libc::c_void);
        })
    }

    pub unsafe fn look_up(name: &str) -> Class {
        Class { raw: ffi::objc_lookUpClass(name.to_c_str().as_ptr()) }
    }

    pub unsafe fn get(name: &str) -> Class {
        Class { raw: ffi::objc_getClass(name.to_c_str().as_ptr()) }
    }

    pub unsafe fn get_required(name: &str) -> Class {
        Class { raw: ffi::objc_getRequiredClass(name.to_c_str().as_ptr()) }
    }

    pub unsafe fn get_meta(name: &str) -> Class {
        Class { raw: ffi::objc_getMetaClass(name.to_c_str().as_ptr()) }
    }

    // Working with Libraries

    pub unsafe fn get_image_name(self) -> String {
        string::raw::from_buf(ffi::class_getImageName(self.raw) as *const libc::c_uchar)
    }
}

impl fmt::Show for Class {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        unsafe {
            if *self != Class::nil() {
                write!(f, "{}", self.get_name())
            } else {
                write!(f, "nil")
            }
        }
    }
}

/// A pointer to an instance of a class.
#[deriving(PartialEq, Eq, Hash)]
pub struct Id {
    pub raw: ffi::id,
}

impl fmt::Show for Id {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        unsafe {
            if *self != Id::nil() {
                write!(f, "instance of {}", self.isa().get_name())
            } else {
                write!(f, "nil")
            }
        }
    }
}

impl Id {
    /// A null instance.
    #[inline]
    pub fn nil() -> Id {
        Id { raw: ffi::nil }
    }

    /// The class definition of which this object is an instance
    #[inline]
    pub unsafe fn isa(self) -> Class {
        Class { raw: (*self.raw).isa }
    }

    // Instantiating Classes

    pub unsafe fn destruct_instance(self) -> *mut libc::c_void {
        ffi::objc_destructInstance(self.raw)
    }

    // Working with Instances

    pub unsafe fn copy(self, size: uint) -> Id {
        Id { raw: ffi::object_copy(self.raw, size as libc::size_t) }
    }

    pub unsafe fn dispose(self) -> Id {
        Id { raw: ffi::object_dispose(self.raw) }
    }

    // Associative References

    // objc_setAssociatedObject
    // objc_getAssociatedObject
    // objc_removeAssociatedObjects
}

/// An instance variable.
pub struct InstanceVariable {
    pub raw: ffi::Ivar,
}

impl InstanceVariable {
    // Working with Instance Variables

    pub unsafe fn get_name(self) -> String {
        string::raw::from_buf(ffi::ivar_getName(self.raw) as *const libc::c_uchar)
    }

    pub unsafe fn get_type_encoding(self) -> String {
        string::raw::from_buf(ffi::ivar_getTypeEncoding(self.raw) as *const libc::c_uchar)
    }

    pub unsafe fn get_offset(self) -> int {
        ffi::ivar_getOffset(self.raw) as int
    }
}

/// The superclass of an instance.
pub struct Super {
    pub raw: *mut ffi::Struct_objc_super,
}

impl Super {
    /// The instance of a class.
    #[inline]
    pub unsafe fn receiver(self) -> Id {
        Id { raw: (*self.raw).receiver }
    }

    /// The superclass to message.
    #[inline]
    pub unsafe fn class(self) -> Class {
        Class { raw: (*self.raw).class }
    }
}

// Sending Messages

/// Returns a function that sends a message to an instance of a class,
/// returning a `T`.
pub fn msg_send<T: 'static>() -> unsafe extern "C" fn(Id, Selector, ...) -> T {
    unsafe {
        match type_id::<T>() {
            // On the i386 platform, the ABI for functions returning a
            // floating-point value is incompatible with that for functions
            // returning an integral type.
            id if (id == type_id::<libc::c_float>()  && cfg!(target_word_size = "32"))
               || (id == type_id::<libc::c_double>() && cfg!(target_word_size = "32"))
                                        => mem::transmute(ffi::objc_msgSend_fpret),
            id if id == type_id::<Id>() => mem::transmute(ffi::objc_msgSend),
            _                           => mem::transmute(ffi::objc_msgSend_stret),

        }
    }
}

/// Returns a function that sends a message to a superclass of an instance of a
/// class, returning a `T`.
pub fn msg_send_super<T: 'static>() -> unsafe extern "C" fn(Super, Selector, ...) -> T {
    unsafe {
        match type_id::<T>() {
            id if id == type_id::<Id>() => mem::transmute(ffi::objc_msgSendSuper),
            _                           => mem::transmute(ffi::objc_msgSendSuper_stret),
        }
    }
}


struct Method {
    pub raw: ffi::Method,
}

impl Method {
    // Working with Methods

    #[inline]
    pub unsafe fn invoke<T>(self) -> T {
        unsafe {
            match type_id::<T>() {
                id if id == type_id::<Id>() => mem::transmute(ffi::method_invoke(self.raw)),
                _                           => mem::transmute(ffi::method_invoke_stret(self.raw)),
            }
        }
    }

    #[inline]
    pub unsafe fn get_name(self) -> String {
        string::raw::from_buf(ffi::method_getName(self.raw) as *const libc::c_uchar)
    }

    #[inline]
    pub unsafe fn get_implementation(self) -> Impl {
        mem::transmute(ffi::method_getImplementation(self.raw))
    }

    #[inline]
    pub unsafe fn get_type_encoding(self) -> CString {
        CString::new(ffi::method_getTypeEncoding(self.raw), false)
    }

    #[inline]
    pub unsafe fn copy_return_type(self) -> CString {
        CString::new(ffi::method_copyReturnType(self.raw), true)
    }

    #[inline]
    pub unsafe fn copy_argument_type(self, index: uint) -> CString {
        CString::new(ffi::method_copyArgumentType(self.raw, index as libc::uint), true)
    }

    // method_getReturnType

    #[inline]
    pub unsafe fn get_number_of_arguments(self) -> uint {
        ffi::method_exchangeImplementations(self.raw) as uint
    }

    // method_getArgumentType
    // method_getDescription

    #[inline]
    pub unsafe fn set_implementation(self, imp: Impl) -> Impl {
        mem::transmute(ffi::method_setImplementation(self.raw, mem::transmute(imp)))
    }

    #[inline]
    pub unsafe fn exchange_implementations(self, other: Method) {
        ffi::method_exchangeImplementations(self.raw, other.raw);
    }
}

// Working with Libraries

/// Returns the names of all the loaded Objective-C frameworks and dynamic
/// libraries.
pub unsafe fn copy_image_names() -> Vec<String> {
    let mut count = 0;
    let ptr = ffi::objc_copyImageNames(&mut count);
    range(0, count as int).map(|i| {
        string::raw::from_buf(*ptr.offset(i) as *const libc::c_uchar)
{'fraction_non_alphanumeric': 0.1341111781667002, 'fraction_numerical': 0.0019446120834171528, 'mean_word_length': 2.479990666199977, 'pattern_counts': {'":': 0, '<': 40, '<?xml version=': 0, '>': 170, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '164059', 'n_tokens_mistral': 9892, 'n_tokens_neox': 9634, 'n_words': 2345}