1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
extern crate cslice;

#[doc(hidden)]
pub extern crate libc;

#[doc(hidden)]
pub extern crate libcruby_sys as sys;
// pub use rb;

use std::ffi::CString;
use sys::VALUE;

mod macros;
mod class_definition;
mod coercions;

pub use coercions::*;

pub use class_definition::{ClassDefinition, MethodDefinition};

#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct Class(VALUE);

impl Class {
    pub fn inner(&self) -> VALUE {
        self.0
    }
}

pub trait RubyMethod {
    fn install(self, class: VALUE, name: &str);
}

impl RubyMethod for extern "C" fn(VALUE) -> VALUE {
    fn install(self, class: VALUE, name: &str) {
        unsafe {
            sys::rb_define_method(
                class,
                CString::new(name).unwrap().as_ptr(),
                self as *const libc::c_void,
                0
            );
        }
    }
}

impl RubyMethod for extern "C" fn(VALUE, VALUE) -> VALUE {
    fn install(self, class: VALUE, name: &str) {
        unsafe {
            sys::rb_define_method(
                class,
                CString::new(name).unwrap().as_ptr(),
                self as *const libc::c_void,
                1
            );
        }
    }
}

#[allow(non_snake_case)]
#[inline]
fn ObjectClass() -> Class {
    Class(unsafe { sys::rb_cObject })
}

impl Class {
    pub fn new(name: &str) -> Class {
        ObjectClass().subclass(name)
    }

    pub fn subclass(&self, name: &str) -> Class {
        unsafe {
            Class(sys::rb_define_class(CString::new(name).unwrap().as_ptr(), self.0))
        }
    }

    pub fn define_method<T: RubyMethod>(&self, name: &str, method: T) {
        method.install(self.0, name);
    }
}

pub fn inspect(val: VALUE) -> String {
    unsafe { CheckedValue::<String>::new(sys::rb_inspect(val)).to_rust() }
}

pub type Metadata = ::VALUE;

#[derive(Copy, Clone, Debug)]
pub struct ExceptionInfo {
    pub exception: Class,
    pub message: VALUE
}

impl ExceptionInfo {
    pub fn with_message<T: ToRuby>(string: T) -> ExceptionInfo {
        ExceptionInfo {
            exception: Class(unsafe { sys::rb_eRuntimeError }),
            message: string.to_ruby(),
        }
    }

    pub fn type_error<T: ToRuby>(string: T) -> ExceptionInfo {
        ExceptionInfo {
            exception: Class(unsafe { sys::rb_eTypeError }),
            message: string.to_ruby(),
        }
    }

    pub fn from_any(any: Box<std::any::Any>) -> ExceptionInfo {
        match any.downcast_ref::<ExceptionInfo>() {
            Some(e) => *e,
            None => {
                match any.downcast_ref::<&'static str>() {
                    Some(e) => ExceptionInfo::with_message(e.to_string()),
                    None => {
                        match any.downcast_ref::<String>() {
                            Some(e) => ExceptionInfo::with_message(e.as_str()),
                            None => ExceptionInfo::with_message(format!("Unknown Error; err={:?}", any)),
                        }
                    }
                }
            }
        }
    }

    pub fn message(&self) -> VALUE {
        self.message
    }

    pub fn raise(&self) -> ! {
        unsafe {
            sys::rb_raise(self.exception.0,
                          sys::SPRINTF_TO_S,
                          self.message);
        }
    }
}

unsafe impl Send for ExceptionInfo {}
unsafe impl Sync for ExceptionInfo {}