use ffi;
use builder;
use util;
use std;
use libc;
use Value;
pub struct VM;
static mut VM_EXISTS: bool = false;
#[derive(Debug)]
pub enum ErrorKind
{
VM(String),
Exception(Value),
}
impl VM
{
pub fn new() -> Result<Self, ErrorKind> {
unsafe {
if VM_EXISTS {
Err(ErrorKind::VM("can only have one Ruby VM at a time".to_owned()))
} else {
ffi::ruby_init();
VM_EXISTS = true;
Ok(VM)
}
}
}
pub fn eval(&mut self, code: &str) -> Result<Value, ErrorKind> {
let mut state: libc::c_int = 0;
let result = unsafe {
ffi::rb_eval_string_protect(util::c_string(code).as_ptr(), &mut state)
};
if state == 0 {
Ok(Value::from(result))
} else {
Err(ErrorKind::Exception(self.consume_exception().unwrap()))
}
}
pub fn class<S>(&mut self, name: S) -> builder::ClassBuilder
where S: Into<String> {
builder::ClassBuilder::new(name)
}
pub fn consume_exception(&mut self) -> Option<Value> {
let exception = self.current_exception();
unsafe {
ffi::rb_set_errinfo(ffi::Qnil);
}
exception
}
pub fn current_exception(&self) -> Option<Value> {
Some(Value::from(unsafe { ffi::rb_errinfo() }))
}
}
impl std::ops::Drop for VM
{
fn drop(&mut self) {
unsafe {
VM_EXISTS = false;
ffi::ruby_cleanup(0);
};
}
}
#[cfg(test)]
describe! vm {
before_each { let mut vm = VM::new().unwrap(); }
it "can eval a simple assignment" {
vm.eval("a = 1");
}
}