Skip to content

Commit cb05e8a

Browse files
committed
Add tests for WitMap trait and map_type codegen option
- Unit tests for WitMap trait impls (BTreeMap, HashMap, reference blanket) - Proc-macro integration tests exercising map_type with HashMap and default - Codegen test variant running all .wit files with --map-type=HashMap
1 parent cfaba13 commit cb05e8a

4 files changed

Lines changed: 152 additions & 8 deletions

File tree

crates/guest-rust/src/rt/mod.rs

Lines changed: 101 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,16 +74,21 @@ pub type Map<K, V> = alloc::collections::BTreeMap<K, V>;
7474
/// Generated code delegates to these methods rather than calling inherent
7575
/// methods on a concrete type, so users can swap in their own map
7676
/// implementation via the `map_type` bindgen option.
77-
///
78-
/// The concrete map type must also implement `IntoIterator` (both owned
79-
/// and by-ref) yielding key/value pairs.
80-
pub trait WitMap<K, V> {
77+
pub trait WitMap<K, V>: Sized {
78+
type Iter: Iterator;
79+
8180
fn wit_map_new(capacity: usize) -> Self;
8281
fn wit_map_push(&mut self, key: K, value: V);
8382
fn wit_map_len(&self) -> usize;
83+
fn wit_map_into_iter(self) -> Self::Iter;
8484
}
8585

86-
impl<'a, K, V, T: WitMap<K, V>> WitMap<K, V> for &'a T {
86+
impl<'a, K, V, T: WitMap<K, V>> WitMap<K, V> for &'a T
87+
where
88+
&'a T: IntoIterator,
89+
{
90+
type Iter = <&'a T as IntoIterator>::IntoIter;
91+
8792
fn wit_map_new(_capacity: usize) -> Self {
8893
unreachable!()
8994
}
@@ -93,9 +98,14 @@ impl<'a, K, V, T: WitMap<K, V>> WitMap<K, V> for &'a T {
9398
fn wit_map_len(&self) -> usize {
9499
T::wit_map_len(self)
95100
}
101+
fn wit_map_into_iter(self) -> Self::Iter {
102+
self.into_iter()
103+
}
96104
}
97105

98106
impl<K: Ord, V> WitMap<K, V> for alloc::collections::BTreeMap<K, V> {
107+
type Iter = alloc::collections::btree_map::IntoIter<K, V>;
108+
99109
fn wit_map_new(_capacity: usize) -> Self {
100110
alloc::collections::BTreeMap::new()
101111
}
@@ -105,10 +115,15 @@ impl<K: Ord, V> WitMap<K, V> for alloc::collections::BTreeMap<K, V> {
105115
fn wit_map_len(&self) -> usize {
106116
self.len()
107117
}
118+
fn wit_map_into_iter(self) -> Self::Iter {
119+
self.into_iter()
120+
}
108121
}
109122

110123
#[cfg(feature = "std")]
111124
impl<K: core::hash::Hash + Eq, V> WitMap<K, V> for std::collections::HashMap<K, V> {
125+
type Iter = std::collections::hash_map::IntoIter<K, V>;
126+
112127
fn wit_map_new(capacity: usize) -> Self {
113128
std::collections::HashMap::with_capacity(capacity)
114129
}
@@ -118,6 +133,9 @@ impl<K: core::hash::Hash + Eq, V> WitMap<K, V> for std::collections::HashMap<K,
118133
fn wit_map_len(&self) -> usize {
119134
self.len()
120135
}
136+
fn wit_map_into_iter(self) -> Self::Iter {
137+
self.into_iter()
138+
}
121139
}
122140

123141
/// For more information about this see `./ci/rebuild-libwit-bindgen-cabi.sh`.
@@ -283,3 +301,81 @@ impl Drop for Cleanup {
283301
}
284302
}
285303
}
304+
305+
#[cfg(test)]
306+
mod tests {
307+
use super::*;
308+
use alloc::collections::BTreeMap;
309+
use alloc::string::ToString;
310+
311+
#[test]
312+
fn btreemap_new_push_len() {
313+
let mut m: BTreeMap<u32, alloc::string::String> = WitMap::wit_map_new(10);
314+
assert_eq!(WitMap::<u32, alloc::string::String>::wit_map_len(&m), 0);
315+
316+
WitMap::wit_map_push(&mut m, 1, "one".to_string());
317+
WitMap::wit_map_push(&mut m, 2, "two".to_string());
318+
assert_eq!(WitMap::<u32, alloc::string::String>::wit_map_len(&m), 2);
319+
}
320+
321+
#[test]
322+
fn btreemap_into_iter() {
323+
let mut m: BTreeMap<u32, u32> = WitMap::wit_map_new(0);
324+
WitMap::wit_map_push(&mut m, 10, 100);
325+
WitMap::wit_map_push(&mut m, 20, 200);
326+
327+
let entries: alloc::vec::Vec<_> = WitMap::wit_map_into_iter(m).collect();
328+
assert_eq!(entries, alloc::vec![(10, 100), (20, 200)]);
329+
}
330+
331+
#[test]
332+
fn btreemap_duplicate_key_overwrites() {
333+
let mut m: BTreeMap<u32, u32> = WitMap::wit_map_new(0);
334+
WitMap::wit_map_push(&mut m, 1, 10);
335+
WitMap::wit_map_push(&mut m, 1, 20);
336+
assert_eq!(WitMap::<u32, u32>::wit_map_len(&m), 1);
337+
}
338+
339+
#[test]
340+
fn ref_blanket_impl_len() {
341+
let mut m: BTreeMap<u32, u32> = WitMap::wit_map_new(0);
342+
WitMap::wit_map_push(&mut m, 1, 10);
343+
344+
let r = &m;
345+
assert_eq!(WitMap::<u32, u32>::wit_map_len(&r), 1);
346+
}
347+
348+
#[test]
349+
fn ref_blanket_impl_into_iter() {
350+
let mut m: BTreeMap<u32, u32> = WitMap::wit_map_new(0);
351+
WitMap::wit_map_push(&mut m, 5, 50);
352+
353+
let r = &m;
354+
let entries: alloc::vec::Vec<_> = WitMap::wit_map_into_iter(r).collect();
355+
assert_eq!(entries, alloc::vec![(&5, &50)]);
356+
}
357+
358+
#[cfg(feature = "std")]
359+
mod hashmap_tests {
360+
use super::*;
361+
use std::collections::HashMap;
362+
363+
#[test]
364+
fn hashmap_new_push_len() {
365+
let mut m: HashMap<u32, u32> = WitMap::wit_map_new(5);
366+
WitMap::wit_map_push(&mut m, 1, 10);
367+
WitMap::wit_map_push(&mut m, 2, 20);
368+
assert_eq!(WitMap::<u32, u32>::wit_map_len(&m), 2);
369+
}
370+
371+
#[test]
372+
fn hashmap_into_iter() {
373+
let mut m: HashMap<u32, u32> = WitMap::wit_map_new(0);
374+
WitMap::wit_map_push(&mut m, 1, 10);
375+
376+
let entries: alloc::vec::Vec<_> = WitMap::wit_map_into_iter(m).collect();
377+
assert_eq!(entries.len(), 1);
378+
assert_eq!(entries[0], (1, 10));
379+
}
380+
}
381+
}

crates/rust/src/bindgen.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -800,9 +800,10 @@ impl Bindgen for FunctionBindgen<'_, '_> {
800800
"if let Some(cleanup) = {cleanup} {{ cleanup.forget(); }}"
801801
);
802802
}
803-
self.push_str(&format!(
804-
"for (i, (map_key, map_value)) in {map}.into_iter().enumerate() {{\n"
805-
));
803+
uwriteln!(
804+
self.src,
805+
"for (i, (map_key, map_value)) in {wit_map}::wit_map_into_iter({map}).enumerate() {{"
806+
);
806807
self.push_str(&format!(
807808
"let base = {result}.add(i * {});\n",
808809
entry.size.format(POINTER_SIZE_EXPRESSION)

crates/rust/tests/codegen.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,49 @@ mod newtyped_list {
143143
}
144144
}
145145

146+
#[allow(unused, reason = "testing codegen, not functionality")]
147+
mod map_type_hashmap {
148+
wit_bindgen::generate!({
149+
inline: r#"
150+
package test:map-type;
151+
152+
interface maps {
153+
type names-by-id = map<u32, string>;
154+
155+
roundtrip: func(a: names-by-id) -> names-by-id;
156+
inline-roundtrip: func(a: map<string, u32>) -> map<string, u32>;
157+
}
158+
159+
world test {
160+
import maps;
161+
export maps;
162+
}
163+
"#,
164+
map_type: "std::collections::HashMap",
165+
generate_all,
166+
});
167+
}
168+
169+
#[allow(unused, reason = "testing codegen, not functionality")]
170+
mod map_type_default {
171+
wit_bindgen::generate!({
172+
inline: r#"
173+
package test:map-default;
174+
175+
interface maps {
176+
type names-by-id = map<u32, string>;
177+
nested: func(a: map<string, map<u32, string>>) -> map<string, map<u32, string>>;
178+
}
179+
180+
world test {
181+
import maps;
182+
export maps;
183+
}
184+
"#,
185+
generate_all,
186+
});
187+
}
188+
146189
#[allow(unused, reason = "testing codegen, not functionality")]
147190
mod retyped_list {
148191
use std::ops::Deref;

crates/test/src/rust.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ impl LanguageMethods for Rust {
9494
("async", &["--async=all"]),
9595
("no-std", &["--std-feature"]),
9696
("merge-equal", &["--merge-structurally-equal-types"]),
97+
(
98+
"hashmap",
99+
&["--map-type=std::collections::HashMap"],
100+
),
97101
]
98102
}
99103

0 commit comments

Comments
 (0)