rutie 0.10.1

The tie between Ruby and Rust.
Documentation
use crate::{
    binding::global::RubySpecialConsts,
    rubysys::io,
    types::{c_int, InternalValue, Value},
    util,
};

fn nil() -> Value {
    Value::from(RubySpecialConsts::Nil as InternalValue)
}

pub fn stdin() -> Value {
    unsafe { io::rb_stdin }
}

pub fn stdout() -> Value {
    unsafe { io::rb_stdout }
}

pub fn stderr() -> Value {
    unsafe { io::rb_stderr }
}

pub fn write(io: Value, string: Value) -> Value {
    unsafe { io::rb_io_write(io, string) }
}

pub fn puts(io: Value, arguments: &[Value]) -> Value {
    let (argc, argv) = util::process_arguments(arguments);

    unsafe { io::rb_io_puts(argc, argv, io) }
}

pub fn print(io: Value, arguments: &[Value]) -> Value {
    let (argc, argv) = util::process_arguments(arguments);

    unsafe { io::rb_io_print(argc, argv, io) }
}

pub fn gets(io: Value) -> Value {
    unsafe { io::rb_io_gets(io) }
}

pub fn getbyte(io: Value) -> Value {
    unsafe { io::rb_io_getbyte(io) }
}

pub fn flush(io: Value) -> Value {
    unsafe { io::rb_io_flush(io) }
}

pub fn close(io: Value) -> Value {
    unsafe { io::rb_io_close(io) }
}

pub fn is_eof(io: Value) -> bool {
    unsafe { io::rb_io_eof(io) }.is_true()
}

pub fn binmode(io: Value) -> Value {
    unsafe { io::rb_io_binmode(io) }
}

// Raises (usually `Errno::*`) when the file cannot be opened, so the caller
// owns `path` and `mode`.
pub fn file_open(path: Value, mode: &std::ffi::CStr) -> Value {
    unsafe { io::rb_file_open_str(path, mode.as_ptr()) }
}

pub fn expand_path(path: Value, directory: Option<Value>) -> Value {
    unsafe { io::rb_file_expand_path(path, directory.unwrap_or_else(nil)) }
}

pub fn absolute_path(path: Value, directory: Option<Value>) -> Value {
    unsafe { io::rb_file_absolute_path(path, directory.unwrap_or_else(nil)) }
}

pub fn dirname(path: Value) -> Value {
    unsafe { io::rb_file_dirname(path) }
}

pub fn getwd() -> Value {
    unsafe { io::rb_dir_getwd() }
}

// The full path of `name` found in `$LOAD_PATH`, or `None`.
pub fn find_file(name: Value) -> Option<Value> {
    let found = unsafe { io::rb_find_file(name) };

    if found.value == 0 || found.is_nil() {
        None
    } else {
        Some(found)
    }
}

pub fn marshal_dump(object: Value) -> Value {
    unsafe { io::rb_marshal_dump(object, nil()) }
}

pub fn marshal_load(data: Value) -> Value {
    unsafe { io::rb_marshal_load(data) }
}

// `Ok(())`, or the non-zero state from `rb_load_protect`.
pub fn load_protect(path: Value, wrap: bool) -> Result<(), c_int> {
    let mut state = 0;

    unsafe { io::rb_load_protect(path, util::bool_to_c_int(wrap), &mut state) };

    if state == 0 {
        Ok(())
    } else {
        Err(state)
    }
}

pub fn require(name: Value) -> Value {
    unsafe { io::rb_f_require(nil(), name) }
}

// `rb_provide` keeps the pointer: it goes through `rb_fstring_cstr`, which
// registers a string pointing at `feature` instead of copying it (it is meant
// for C string literals). So the name is leaked on purpose; freeing it left
// `$LOADED_FEATURES` reading freed memory.
pub fn provide(feature: &str) {
    let feature = util::str_to_cstring(feature).into_raw();

    unsafe { io::rb_provide(feature) }
}

pub fn is_provided(feature: &str) -> bool {
    let feature = util::str_to_cstring(feature);

    util::c_int_to_bool(unsafe { io::rb_provided(feature.as_ptr()) })
}

pub fn add_load_path(path: &str) {
    let path = util::str_to_cstring(path);

    unsafe { io::ruby_incpush(path.as_ptr()) }
}