mirror of https://github.com/tildeio/helix
Add hash <-> HashMap coercion
This commit is contained in:
parent
ff2a9d2a59
commit
5ce16e9550
|
@ -49,6 +49,14 @@ impl RubyException {
|
|||
|
||||
pub const EMPTY_EXCEPTION: RubyException = RubyException(0);
|
||||
|
||||
#[repr(C)]
|
||||
pub enum st_retval {
|
||||
ST_CONTINUE,
|
||||
ST_STOP,
|
||||
ST_DELETE,
|
||||
// ST_CHECK
|
||||
}
|
||||
|
||||
#[cfg_attr(windows, link(name="helix-runtime"))]
|
||||
extern "C" {
|
||||
#[link_name = "HELIX_RUNTIME_VERSION"]
|
||||
|
@ -99,6 +107,9 @@ extern "C" {
|
|||
#[link_name = "HELIX_RARRAY_CONST_PTR"]
|
||||
pub fn RARRAY_CONST_PTR(array: VALUE) -> *const VALUE;
|
||||
|
||||
#[link_name = "HELIX_RHASH_SIZE"]
|
||||
pub fn RHASH_SIZE(hash: VALUE) -> isize;
|
||||
|
||||
#[link_name = "HELIX_RB_TYPE_P"]
|
||||
pub fn RB_TYPE_P(val: VALUE, rb_type: isize) -> bool;
|
||||
|
||||
|
@ -143,6 +154,9 @@ extern "C" {
|
|||
#[link_name = "HELIX_T_ARRAY"]
|
||||
pub static T_ARRAY: isize;
|
||||
|
||||
#[link_name = "HELIX_T_HASH"]
|
||||
pub static T_HASH: isize;
|
||||
|
||||
#[link_name = "HELIX_T_TRUE"]
|
||||
pub static T_TRUE: isize;
|
||||
|
||||
|
@ -177,6 +191,9 @@ extern "C" {
|
|||
pub fn rb_ary_new_capa(capa: isize) -> VALUE;
|
||||
pub fn rb_ary_entry(ary: VALUE, offset: isize) -> VALUE;
|
||||
pub fn rb_ary_push(ary: VALUE, item: VALUE) -> VALUE;
|
||||
pub fn rb_hash_new() -> VALUE;
|
||||
pub fn rb_hash_aset(hash: VALUE, key: VALUE, value: VALUE) -> VALUE;
|
||||
pub fn rb_hash_foreach(hash: VALUE, f: extern "C" fn(key: VALUE, value: VALUE, farg: *mut void) -> st_retval, farg: *mut void);
|
||||
|
||||
pub fn rb_raise(exc: VALUE, string: c_string, ...) -> !;
|
||||
pub fn rb_jump_tag(state: RubyException) -> !;
|
||||
|
|
|
@ -5,11 +5,59 @@ describe "TextTransform" do
|
|||
expect(TextTransform.widen("Hello Aaron (@tenderlove)!")).to eq("Hello Aaron (@tenderlove)!")
|
||||
end
|
||||
|
||||
it "can widen array" do
|
||||
expect(TextTransform.widen_array(%w"Hello Aaron (@tenderlove)!")).to eq(%w"Hello Aaron (@tenderlove)!")
|
||||
end
|
||||
|
||||
it "can widen hash" do
|
||||
expect(TextTransform.widen_hash({
|
||||
"message" => "Hello",
|
||||
"name" => "Aaron",
|
||||
"handle" => "@tenderlove"
|
||||
})).to eq({
|
||||
"message" => "Hello",
|
||||
"name" => "Aaron",
|
||||
"handle" => "@tenderlove"
|
||||
})
|
||||
end
|
||||
|
||||
it "can narrowen text" do
|
||||
expect(TextTransform.narrowen("Hello Aaron (@tenderlove)!")).to eq("Hello Aaron (@tenderlove)!")
|
||||
end
|
||||
|
||||
it "can narrowen array" do
|
||||
expect(TextTransform.narrowen_array(%w"Hello Aaron (@tenderlove)!")).to eq(%w"Hello Aaron (@tenderlove)!")
|
||||
end
|
||||
|
||||
it "can narrowen hash" do
|
||||
expect(TextTransform.narrowen_hash({
|
||||
"message" => "Hello",
|
||||
"name" => "Aaron",
|
||||
"handle" => "@tenderlove"
|
||||
})).to eq({
|
||||
"message" => "Hello",
|
||||
"name" => "Aaron",
|
||||
"handle" => "@tenderlove"
|
||||
})
|
||||
end
|
||||
|
||||
it "can flip text" do
|
||||
expect(TextTransform.flip("Hello Aaron (@tenderlove)!")).to eq("¡(ǝʌolɹǝpuǝʇ@) uoɹɐ∀ ollǝH")
|
||||
end
|
||||
|
||||
it "can flip array" do
|
||||
expect(TextTransform.flip_array(%w"Hello Aaron (@tenderlove)!")).to eq(%w"¡(ǝʌolɹǝpuǝʇ@) uoɹɐ∀ ollǝH")
|
||||
end
|
||||
|
||||
it "can flip hash" do
|
||||
expect(TextTransform.flip_hash({
|
||||
"message" => "Hello",
|
||||
"name" => "Aaron",
|
||||
"handle" => "@tenderlove"
|
||||
})).to eq({
|
||||
"ollǝH" => "ǝƃɐssǝɯ",
|
||||
"uoɹɐ∀" => "ǝɯɐu",
|
||||
"ǝʌolɹǝpuǝʇ@" => "ǝlpuɐɥ"
|
||||
})
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
#![recursion_limit="1024"]
|
||||
|
||||
#[macro_use]
|
||||
extern crate helix;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
ruby! {
|
||||
class TextTransform {
|
||||
def widen(text: String) -> String {
|
||||
|
@ -22,6 +26,14 @@ ruby! {
|
|||
}).collect()
|
||||
}
|
||||
|
||||
def widen_array(text: Vec<String>) -> Vec<String> {
|
||||
text.into_iter().map(TextTransform::widen).collect()
|
||||
}
|
||||
|
||||
def widen_hash(text: HashMap<String, String>) -> HashMap<String, String> {
|
||||
text.into_iter().map(|(k,v)| (TextTransform::widen(k), TextTransform::widen(v))).collect()
|
||||
}
|
||||
|
||||
def narrowen(text: String) -> String {
|
||||
text.chars().map(|char| {
|
||||
match char {
|
||||
|
@ -41,6 +53,14 @@ ruby! {
|
|||
}).collect()
|
||||
}
|
||||
|
||||
def narrowen_array(text: Vec<String>) -> Vec<String> {
|
||||
text.into_iter().map(TextTransform::narrowen).collect()
|
||||
}
|
||||
|
||||
def narrowen_hash(text: HashMap<String, String>) -> HashMap<String, String> {
|
||||
text.into_iter().map(|(k,v)| (TextTransform::narrowen(k), TextTransform::narrowen(v))).collect()
|
||||
}
|
||||
|
||||
def flip(text: String) -> String {
|
||||
text.chars().rev().map(|char| {
|
||||
match char {
|
||||
|
@ -58,5 +78,13 @@ ruby! {
|
|||
}
|
||||
}).collect()
|
||||
}
|
||||
|
||||
def flip_array(text: Vec<String>) -> Vec<String> {
|
||||
text.into_iter().rev().map(TextTransform::flip).collect()
|
||||
}
|
||||
|
||||
def flip_hash(text: HashMap<String, String>) -> HashMap<String, String> {
|
||||
text.into_iter().map(|(k,v)| (TextTransform::flip(v), TextTransform::flip(k))).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,10 @@ const void* HELIX_RARRAY_CONST_PTR(VALUE array) {
|
|||
return RARRAY_CONST_PTR(array);
|
||||
}
|
||||
|
||||
long HELIX_RHASH_SIZE(VALUE hash) {
|
||||
return RHASH_SIZE(hash);
|
||||
}
|
||||
|
||||
bool HELIX_RB_TYPE_P(VALUE v, int type) {
|
||||
return RB_TYPE_P(v, type);
|
||||
}
|
||||
|
|
|
@ -55,6 +55,8 @@ HELIX_EXTERN long HELIX_RARRAY_LEN(VALUE array);
|
|||
HELIX_EXTERN void* HELIX_RARRAY_PTR(VALUE array);
|
||||
HELIX_EXTERN const void* HELIX_RARRAY_CONST_PTR(VALUE array);
|
||||
|
||||
HELIX_EXTERN long HELIX_RHASH_SIZE(VALUE hash);
|
||||
|
||||
HELIX_EXTERN bool HELIX_RB_TYPE_P(VALUE v, int type);
|
||||
HELIX_EXTERN int HELIX_TYPE(VALUE v);
|
||||
|
||||
|
|
|
@ -76,6 +76,10 @@ describe HelixRuntime do
|
|||
expect(Dummy.RARRAY_CONST_PTR(arr)).to eq(Dummy::RARRAY_CONST_PTR(arr))
|
||||
end
|
||||
|
||||
it 'exports the RHASH_SIZE macro' do
|
||||
expect(Dummy.RHASH_SIZE({a: 1, b: 2, c: 3, d: 4, e: 5})).to equal(5)
|
||||
end
|
||||
|
||||
describe 'coercions' do
|
||||
it "(INT2FIX)" do
|
||||
expect(Dummy.INT2FIX(10)).to eq(10)
|
||||
|
|
|
@ -39,6 +39,10 @@ static VALUE TEST_RARRAY_CONST_PTR(VALUE _self, VALUE val) {
|
|||
return SIZET2NUM((uintptr_t)HELIX_RARRAY_CONST_PTR(val));
|
||||
}
|
||||
|
||||
static VALUE TEST_RHASH_SIZE(VALUE _self, VALUE val) {
|
||||
return LONG2NUM(HELIX_RHASH_SIZE(val));
|
||||
}
|
||||
|
||||
static VALUE TEST_RB_TYPE_P(VALUE _self, VALUE val, VALUE type) {
|
||||
int result = HELIX_RB_TYPE_P(val, FIX2INT(type));
|
||||
return result ? Qtrue : Qfalse;
|
||||
|
@ -186,6 +190,7 @@ void Init_dummy() {
|
|||
EXPORT_RUBY_FUNC(RARRAY_PTR, 1);
|
||||
EXPORT_FUNC(RARRAY_CONST_PTR, 1);
|
||||
EXPORT_RUBY_FUNC(RARRAY_CONST_PTR, 1);
|
||||
EXPORT_FUNC(RHASH_SIZE, 1);
|
||||
EXPORT_FUNC(RB_TYPE_P, 2);
|
||||
EXPORT_FUNC(TYPE, 1);
|
||||
EXPORT_FUNC(INT2FIX, 1);
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
use sys::{VALUE, RB_TYPE_P, T_HASH, RHASH_SIZE, rb_hash_foreach, rb_hash_new, rb_hash_aset, void, st_retval};
|
||||
use super::{FromRuby, CheckResult, ToRuby, ToRubyResult};
|
||||
use std::collections::hash_map::HashMap;
|
||||
use std::hash::Hash;
|
||||
use ::std::mem::transmute;
|
||||
|
||||
extern "C" fn rb_hash_collect(key: VALUE, value: VALUE, vec: *mut void) -> st_retval {
|
||||
let vec: &mut Vec<(VALUE, VALUE)> = unsafe { transmute(vec) };
|
||||
vec.push((key, value));
|
||||
st_retval::ST_CONTINUE
|
||||
}
|
||||
|
||||
impl<K: FromRuby + Eq + Hash, V: FromRuby> FromRuby for HashMap<K, V> {
|
||||
type Checked = Vec<(K::Checked, V::Checked)>;
|
||||
|
||||
fn from_ruby(value: VALUE) -> CheckResult<Self::Checked> {
|
||||
if unsafe { RB_TYPE_P(value, T_HASH) } {
|
||||
let len = unsafe { RHASH_SIZE(value) };
|
||||
|
||||
let mut pairs = Vec::<(VALUE, VALUE)>::with_capacity(len as usize);
|
||||
unsafe { rb_hash_foreach(value, rb_hash_collect, transmute(&mut pairs)) };
|
||||
|
||||
let mut checked = Vec::<(K::Checked, V::Checked)>::with_capacity(len as usize);
|
||||
|
||||
for (k, v) in pairs.into_iter() {
|
||||
let k = K::from_ruby(k)?;
|
||||
let v = V::from_ruby(v)?;
|
||||
|
||||
checked.push((k, v));
|
||||
}
|
||||
|
||||
Ok(checked)
|
||||
} else {
|
||||
type_error!("a hash");
|
||||
}
|
||||
}
|
||||
|
||||
fn from_checked(checked: Self::Checked) -> HashMap<K, V> {
|
||||
checked.into_iter().map(|(k, v)| (K::from_checked(k), V::from_checked(v))).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: ToRuby + Eq + Hash, V: ToRuby> ToRuby for HashMap<K, V> {
|
||||
fn to_ruby(self) -> ToRubyResult {
|
||||
let hash = unsafe { rb_hash_new() };
|
||||
|
||||
for (k,v) in self.into_iter() {
|
||||
unsafe { rb_hash_aset(hash, k.to_ruby()?, v.to_ruby()?) };
|
||||
}
|
||||
|
||||
Ok(hash)
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ mod option;
|
|||
mod result;
|
||||
mod slice;
|
||||
mod vec;
|
||||
mod hash;
|
||||
|
||||
use sys::{VALUE};
|
||||
use super::{Error, ToError};
|
||||
|
|
Loading…
Reference in New Issue