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
#![recursion_limit="1024"]

#[allow(unused_imports)]
#[macro_use]
extern crate cstr_macro;

#[doc(hidden)]
pub use cstr_macro::*;

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

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

use std::ffi::CStr;
use sys::VALUE;

#[macro_export]
macro_rules! raise {
    ($msg:expr) => { return Err($crate::ToError::to_error($msg)); };

    ($class:expr, $msg:expr) => {
        return Err($crate::ToError::to_error($msg).with_class($class));
    };
}

#[macro_export]
macro_rules! raise_panic {
    ($msg:expr) => { panic!($crate::ToError::to_error($msg)); };

    ($class:expr, $msg:expr) => {
        panic!($crate::ToError::to_error($msg).with_class($class));
    };
}

#[macro_export]
macro_rules! type_error {
    ($message:expr) => { raise!(unsafe { $crate::Class::from_value($crate::sys::rb_eTypeError) }, $message); };

    ($actual:expr, $expected:expr) => {
        {
            type_error!(format!("Expected {}, got {}", $expected, $crate::inspect($actual)));
        }
    };
}

mod class_definition;
mod coercions;
mod errors;
mod macros;

pub use coercions::*;
pub use errors::*;

pub use class_definition::{ClassDefinition, MethodDefinition};

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

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

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

impl RubyMethod for extern "C" fn(VALUE, VALUE) -> VALUE {
    fn install(self, class: VALUE, name: &CStr) {
        unsafe {
            sys::rb_define_method(
                class,
                name.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 unsafe fn from_value(value: VALUE) -> Class {
        Class(value)
    }

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

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

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

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

pub fn inspect(val: VALUE) -> String {
    unsafe { String::from_ruby_unwrap(sys::rb_inspect(val)) }
}

pub unsafe fn as_usize(value: ::VALUE) -> usize {
    std::mem::transmute(value)
}

pub type Metadata = ::VALUE;