Skip to content

Commit c70f274

Browse files
authored
Merge pull request #138 from ankane/btree-map
Add conversion for BTreeMap
2 parents 548a394 + 5051de9 commit c70f274

File tree

3 files changed

+101
-2
lines changed

3 files changed

+101
-2
lines changed

src/r_hash.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Types and functions for working with Ruby’s Hash class.
22
33
use std::{
4+
collections::BTreeMap,
45
collections::HashMap,
56
convert::Infallible,
67
fmt,
@@ -804,6 +805,46 @@ impl RHash {
804805
Ok(map)
805806
}
806807

808+
/// Return `self` converted to a Rust [`BTreeMap`].
809+
///
810+
/// This will only convert to a map of 'owned' Rust native types. The types
811+
/// representing Ruby objects can not be stored in a heap-allocated
812+
/// datastructure like a [`BTreeMap`] as they are hidden from the mark phase
813+
/// of Ruby's garbage collector, and thus may be prematurely garbage
814+
/// collected in the following sweep phase.
815+
///
816+
/// Errors if the conversion of any key or value fails.
817+
///
818+
/// # Examples
819+
///
820+
/// ```
821+
/// use std::collections::BTreeMap;
822+
///
823+
/// use magnus::{Error, RHash, Ruby};
824+
///
825+
/// fn example(ruby: &Ruby) -> Result<(), Error> {
826+
/// let r_hash: RHash = ruby.eval(r#"{"answer" => 42}"#)?;
827+
/// let mut hash_map = BTreeMap::new();
828+
/// hash_map.insert(String::from("answer"), 42);
829+
/// assert_eq!(r_hash.to_btree_map()?, hash_map);
830+
///
831+
/// Ok(())
832+
/// }
833+
/// # Ruby::init(example).unwrap()
834+
/// ```
835+
pub fn to_btree_map<K, V>(self) -> Result<BTreeMap<K, V>, Error>
836+
where
837+
K: TryConvertOwned + Eq + Hash + Ord,
838+
V: TryConvertOwned,
839+
{
840+
let mut map = BTreeMap::new();
841+
self.foreach(|key, value| {
842+
map.insert(key, value);
843+
Ok(ForEach::Continue)
844+
})?;
845+
Ok(map)
846+
}
847+
807848
/// Convert `self` to a Rust vector of key/value pairs.
808849
///
809850
/// This will only convert to a map of 'owned' Rust native types. The types
@@ -940,6 +981,27 @@ where
940981
{
941982
}
942983

984+
impl<K, V> IntoValue for BTreeMap<K, V>
985+
where
986+
K: IntoValueFromNative,
987+
V: IntoValueFromNative,
988+
{
989+
fn into_value_with(self, handle: &Ruby) -> Value {
990+
let hash = handle.hash_new();
991+
for (k, v) in self {
992+
let _ = hash.aset(k, v);
993+
}
994+
hash.into_value_with(handle)
995+
}
996+
}
997+
998+
unsafe impl<K, V> IntoValueFromNative for BTreeMap<K, V>
999+
where
1000+
K: IntoValueFromNative,
1001+
V: IntoValueFromNative,
1002+
{
1003+
}
1004+
9431005
#[cfg(feature = "old-api")]
9441006
impl<K, V> FromIterator<(K, V)> for RHash
9451007
where

src/try_convert.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,24 @@ where
295295
{
296296
}
297297

298+
impl<K, V> TryConvert for std::collections::BTreeMap<K, V>
299+
where
300+
K: TryConvertOwned + Eq + std::hash::Hash + Ord,
301+
V: TryConvertOwned,
302+
{
303+
#[inline]
304+
fn try_convert(val: Value) -> Result<Self, Error> {
305+
debug_assert_value!(val);
306+
RHash::try_convert(val)?.to_btree_map()
307+
}
308+
}
309+
unsafe impl<K, V> TryConvertOwned for std::collections::BTreeMap<K, V>
310+
where
311+
K: TryConvertOwned + Eq + std::hash::Hash + Ord,
312+
V: TryConvertOwned,
313+
{
314+
}
315+
298316
#[cfg(unix)]
299317
impl TryConvert for PathBuf {
300318
fn try_convert(val: Value) -> Result<Self, Error> {

tests/hash.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use std::collections::HashMap;
1+
use std::collections::{BTreeMap, HashMap};
22

33
#[test]
4-
fn it_converts_hash_map() {
4+
fn it_converts_maps() {
55
let ruby = unsafe { magnus::embed::init() };
66

77
let map: HashMap<String, u8> = ruby
@@ -22,4 +22,23 @@ fn it_converts_hash_map() {
2222
r#"map == {"test" => [0, 0.5], "example" => [1, 3.75]}"#,
2323
map
2424
);
25+
26+
let map: BTreeMap<String, u8> = ruby
27+
.eval(r#"{"foo" => 1, "bar" => 2, "baz" => 3}"#)
28+
.unwrap();
29+
30+
let mut expected = BTreeMap::new();
31+
expected.insert("foo".to_owned(), 1);
32+
expected.insert("bar".to_owned(), 2);
33+
expected.insert("baz".to_owned(), 3);
34+
assert_eq!(expected, map);
35+
36+
let mut map = BTreeMap::new();
37+
map.insert("test", (0, 0.5));
38+
map.insert("example", (1, 3.75));
39+
magnus::rb_assert!(
40+
ruby,
41+
r#"map == {"test" => [0, 0.5], "example" => [1, 3.75]}"#,
42+
map
43+
);
2544
}

0 commit comments

Comments
 (0)