helix/src/coercions/hash.rs

54 lines
1.7 KiB
Rust

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)
}
}