From 7ecced3a3b68584cdcd68f5e9e79b4950368e92a Mon Sep 17 00:00:00 2001 From: Tim Morgan Date: Sat, 9 Jun 2018 14:21:25 -0500 Subject: [PATCH] Add more docs --- LICENSE | 21 ++++++++++++++++++ README.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 57 ++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 132 insertions(+), 8 deletions(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2ad28e7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Tim Morgan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index e5fec39..72c9373 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,62 @@ what Ruby's `Data_Wrap_Struct` macro does. That is to say, you can store a Rust Any heap-allocated struct, enum, or whatever should work. +## Example + +``` +extern crate ruby_sys; +extern crate ruby_wrap_data; + +use ruby_sys::{ + class::{rb_class_new_instance, rb_define_class}, + rb_cObject, + types::Value, + value::RubySpecialConsts::{Nil}, + vm::ruby_init +}; + +use std::ffi::CString; +use std::mem; + +const RB_NIL: Value = Value { value: Nil as usize }; + +struct MyValue { + pub val: u16 +} + +fn alloc(klass: Value) -> Value { + // build your data and put it on the heap + let data = Box::new(MyValue { val: 1 }); + // call `wrap()`, passing your klass and data + ruby_wrap_data::wrap(klass, data) +} + +fn main() { + // you may need to start the ruby vm + unsafe { ruby_init() }; + + // create a ruby class and attach your alloc function + let name = CString::new("Thing").unwrap().into_raw(); + let klass = unsafe { rb_define_class(name, rb_cObject) }; + ruby_wrap_data::define_alloc_func(klass, alloc); + + // create a new instance of the class + let thing = unsafe { rb_class_new_instance(0, &RB_NIL, klass) }; + + // get the data out of the ruby object + let data: Option> = ruby_wrap_data::remove(thing); + assert!(data.is_some()); + + // if you try to remove it again, you get None + let data: Option> = ruby_wrap_data::remove(thing); + assert!(data.is_none()); + + // set new data on the object + let new_data = Box::new(MyValue { val : 2 }); + ruby_wrap_data::set(thing, new_data); +} +``` + ## Testing Assuming you're using rbenv (if not, sorry, you're on your own): @@ -21,3 +77,9 @@ You may need to help Rust find the libruby.so file, like this: export LD_LIBRARY_PATH=$HOME/.rbenv/versions/2.5.1/lib RUBY=$(rbenv which ruby) cargo test ``` + +## Copyright and License + +Copyright Tim Morgan + +Licensed MIT diff --git a/src/lib.rs b/src/lib.rs index 9c09d71..b15ae19 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ //! //! ## Example //! -//! ``` +//! ```rust //! extern crate ruby_sys; //! extern crate ruby_wrap_data; //! @@ -49,7 +49,12 @@ //! let thing = unsafe { rb_class_new_instance(0, &RB_NIL, klass) }; //! //! // get the data out of the ruby object -//! let data: Box = ruby_wrap_data::remove(thing).unwrap(); +//! let data: Option> = ruby_wrap_data::remove(thing); +//! assert!(data.is_some()); +//! +//! // if you try to remove it again, you get None +//! let data: Option> = ruby_wrap_data::remove(thing); +//! assert!(data.is_none()); //! //! // set new data on the object //! let new_data = Box::new(MyValue { val : 2 }); @@ -91,17 +96,41 @@ pub fn define_alloc_func(klass: Value, alloc: fn(Value) -> Value) { unsafe { rb_define_alloc_func(klass, alloc as CallbackPtr) }; } +/// Creates a new instance of the given class, wrapping the given +/// heap-allocated data type. +/// +/// # Arguments +/// +/// * `klass` - a Ruby Class +/// * `data` - a Box - the data you wish to embed in the Ruby object pub fn wrap(klass: Value, data: Box) -> Value { let datap = Box::into_raw(data) as *mut c_void; unsafe { rb_data_object_wrap(klass, datap, None, Some(free::)) } } -extern "C" fn free(data: *mut c_void) { - // memory is freed when the box goes out of the scope - let datap = data as *mut T; - unsafe { Box::from_raw(datap) }; -} - +/// Removes and returns the wrapped data from the given Ruby object. +/// Returns None if the data is currently NULL. +/// +/// # Arguments +/// +/// * `object` - a Ruby object +/// +/// # Notes +/// +/// You will need to specify the data type of the variable since it +/// cannot be inferred: +/// +/// ```compile_fail +/// let data: Option> = ruby_wrap_data::remove(thing); +/// ``` +/// +/// Also note, if you wish to peek at the data without removing it, +/// you will need to put it back using `set`, like this: +/// +/// ```compile_fail +/// let data: Option> = ruby_wrap_data::remove(thing); +/// ruby_wrap_data::set(thing, data.unwrap()); +/// ``` pub fn remove(object: Value) -> Option> { let rdata = rdata(object); let datap = unsafe { (*rdata).data as *mut T }; @@ -113,12 +142,24 @@ pub fn remove(object: Value) -> Option> { } } +/// Sets the wrapped data on the given Ruby object. +/// +/// # Arguments +/// +/// * `object` - a Ruby object +/// * `data` - a Box - the data you wish to embed in the Ruby object pub fn set(object: Value, data: Box) { let rdata = rdata(object); let datap = Box::into_raw(data) as *mut c_void; unsafe { (*rdata).data = datap }; } +extern "C" fn free(data: *mut c_void) { + // memory is freed when the box goes out of the scope + let datap = data as *mut T; + unsafe { Box::from_raw(datap) }; +} + fn set_none(object: Value) { let rdata = rdata(object); unsafe { (*rdata).data = ptr::null_mut() }; -- 2.22.2