Skip to content
This repository was archived by the owner on Feb 2, 2023. It is now read-only.

Commit 1511e43

Browse files
honechancancode
authored andcommitted
Add Ruby Array -> vec coercion
This is essentially a rebase of #46 by @wagenet, updated for the new APIs. While it is not ideal to copy most of the time, it is perfectly safe. Therefore we decide to add this in now to enable use cases that are not otherwise possible while we work on a better non-copy version (something like #100). TODO: port the vec -> Ruby Array code from #46 Paired with @hone
1 parent 78c57aa commit 1511e43

File tree

6 files changed

+83
-41
lines changed

6 files changed

+83
-41
lines changed

crates/libcruby-sys/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ extern "C" {
174174
pub fn rb_sprintf(specifier: c_string, ...) -> VALUE;
175175
pub fn rb_inspect(value: VALUE) -> VALUE;
176176
pub fn rb_intern(string: c_string) -> ID;
177+
pub fn rb_ary_entry(ary: VALUE, offset: isize) -> VALUE;
177178
pub fn rb_raise(exc: VALUE, string: c_string, ...) -> !;
178179

179180
pub fn rb_jump_tag(state: RubyException) -> !;

examples/membership/src/lib.rs

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,36 @@
11
#[macro_use]
22
extern crate helix;
33

4-
pub struct RubyArray<'a>(&'a [usize]);
4+
use helix::{Error, FromRuby};
55

66
ruby! {
77
reopen class Array {
8-
def is_superset_of(&self, needle: RubyArray) -> bool {
9-
let needle = needle.0;
8+
def is_superset_of(&self, needle: Vec<u64>) -> Result<bool, Error> {
9+
if needle.is_empty() { return Ok(true) }
1010

11-
if needle.is_empty() { return true }
11+
let haystack = try!(self.as_vec());
1212

13-
let haystack = self.as_ref();
13+
if haystack.is_empty() { return Ok(false) }
1414

15-
if haystack.is_empty() { return false }
16-
17-
let mut needle = needle.iter();
15+
let mut needle = needle.into_iter();
1816
let mut needle_item = needle.next().unwrap();
1917

2018
for item in haystack {
2119
if item == needle_item {
2220
match needle.next() {
23-
None => return true,
21+
None => return Ok(true),
2422
Some(next_item) => needle_item = next_item
2523
}
2624
}
2725
}
2826

29-
false
27+
Ok(false)
3028
}
3129
}
3230
}
3331

34-
use helix::{FromRuby, CheckedValue, CheckResult, sys};
35-
36-
impl<'a> FromRuby for RubyArray<'a> {
37-
type Checked = CheckedValue<RubyArray<'a>>;
38-
39-
fn from_ruby(value: sys::VALUE) -> CheckResult<Self::Checked> {
40-
if unsafe { sys::RB_TYPE_P(value, sys::T_ARRAY) } {
41-
Ok(unsafe { CheckedValue::new(value) })
42-
} else {
43-
type_error!(value, "an Array of unsigned pointer-sized integers")
44-
}
45-
}
46-
47-
fn from_checked(checked: Self::Checked) -> RubyArray<'a> {
48-
let value = checked.to_value();
49-
let size = unsafe { sys::RARRAY_LEN(value) };
50-
let ptr = unsafe { sys::RARRAY_PTR(value) };
51-
RubyArray(unsafe { std::slice::from_raw_parts(ptr as *const usize, size as usize) })
52-
}
53-
}
54-
55-
impl AsRef<[usize]> for Array {
56-
fn as_ref<'a>(&'a self) -> &'a [usize] {
57-
RubyArray::from_ruby_unwrap(self.helix).0
32+
impl Array {
33+
fn as_vec(&self) -> Result<Vec<u64>, Error> {
34+
Vec::<u64>::from_ruby(self.helix).map(Vec::<u64>::from_checked)
5835
}
5936
}

src/coercions/mod.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
mod string;
1+
mod value;
22
mod unit;
33
mod bool;
44
mod integers;
55
mod float;
6+
mod string;
7+
mod vec;
68
mod option;
79
mod result;
810

@@ -44,8 +46,3 @@ pub trait ToRuby {
4446
fn to_ruby(self) -> ToRubyResult;
4547
}
4648

47-
impl ToRuby for VALUE {
48-
fn to_ruby(self) -> ToRubyResult {
49-
Ok(self)
50-
}
51-
}

src/coercions/value.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use sys::{VALUE};
2+
use super::{FromRuby, CheckResult, ToRuby, ToRubyResult};
3+
4+
impl FromRuby for VALUE {
5+
type Checked = VALUE;
6+
7+
fn from_ruby(value: VALUE) -> CheckResult<VALUE> {
8+
Ok(value)
9+
}
10+
11+
fn from_checked(checked: VALUE) -> VALUE {
12+
checked
13+
}
14+
}
15+
16+
impl ToRuby for VALUE {
17+
fn to_ruby(self) -> ToRubyResult {
18+
Ok(self)
19+
}
20+
}

src/coercions/vec.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use sys::{RARRAY_LEN, RB_TYPE_P, T_ARRAY, VALUE, rb_ary_entry};
2+
use super::{FromRuby, CheckResult};
3+
4+
impl<T: FromRuby> FromRuby for Vec<T> {
5+
type Checked = Vec<T::Checked>;
6+
7+
fn from_ruby(value: VALUE) -> CheckResult<Self::Checked> {
8+
if unsafe { RB_TYPE_P(value, T_ARRAY) } {
9+
let size = unsafe { RARRAY_LEN(value) };
10+
let mut vec = Vec::with_capacity(size as usize);
11+
12+
for i in 0..size {
13+
let entry = unsafe { rb_ary_entry(value, i) };
14+
15+
match T::from_ruby(entry) {
16+
Ok(checked) => vec.push(checked),
17+
Err(reason) => {
18+
type_error!(format!("Cannot convert element {}: {}", i, reason))
19+
}
20+
}
21+
}
22+
23+
Ok(vec)
24+
} else {
25+
type_error!(value, "an array")
26+
}
27+
}
28+
29+
fn from_checked(checked: Self::Checked) -> Vec<T> {
30+
checked.into_iter().map(T::from_checked).collect()
31+
}
32+
}

src/errors.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::{Class, ToRuby};
2-
use std::{any};
2+
use std::{any, fmt};
33
use sys::{VALUE, SPRINTF_TO_S, c_string, rb_eRuntimeError, rb_raise};
44

55
#[derive(Copy, Clone, Debug)]
@@ -43,6 +43,21 @@ impl Error {
4343
}
4444
}
4545

46+
impl fmt::Display for Error {
47+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
48+
match self.message {
49+
ErrorMessage::Static(c_string) => {
50+
use ::std::ffi::CStr;
51+
write!(f, "{}", unsafe { CStr::from_ptr(c_string) }.to_str().unwrap())
52+
},
53+
ErrorMessage::Dynamic(value) => {
54+
use super::FromRuby;
55+
write!(f, "{}", String::from_ruby_unwrap(value))
56+
}
57+
}
58+
}
59+
}
60+
4661
unsafe impl Send for Error {}
4762
unsafe impl Sync for Error {}
4863

0 commit comments

Comments
 (0)