Skip to content

Commit fa9d6dc

Browse files
committed
refactor: simplify world handling in generate_bindings, add test
1 parent b3a3a73 commit fa9d6dc

File tree

1 file changed

+98
-5
lines changed

1 file changed

+98
-5
lines changed

sdk/base-macros/src/generate.rs

Lines changed: 98 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,19 +156,22 @@ pub(crate) fn expand(input: proc_macro::TokenStream) -> proc_macro::TokenStream
156156
}
157157

158158
/// Generates WIT bindings using `wit-bindgen` directly instead of the `generate!` macro.
159+
///
160+
/// The `world` parameter specifies which world to generate bindings for. This should already
161+
/// be resolved by the caller (either from inline WIT or from the local wit/ directory).
162+
/// If `None`, wit-bindgen will attempt to select a default world from the loaded packages.
159163
fn generate_bindings(
160164
args: &GenerateArgs,
161165
config: &manifest_paths::ResolvedWit,
162-
world_override: Option<&str>,
166+
world: Option<&str>,
163167
) -> Result<TokenStream2, Error> {
164168
let inline_src = args.inline.as_ref().map(|src| src.value());
165169
let inline_ref = inline_src.as_deref();
166170
let wit_sources = load_wit_sources(&config.paths, inline_ref)?;
167171

168-
let world_spec = world_override.or(config.world.as_deref());
169-
let world = wit_sources
172+
let world_id = wit_sources
170173
.resolve
171-
.select_world(&wit_sources.packages, world_spec)
174+
.select_world(&wit_sources.packages, world)
172175
.map_err(|err| Error::new(Span::call_site(), err.to_string()))?;
173176

174177
let mut opts = Opts {
@@ -183,7 +186,7 @@ fn generate_bindings(
183186
let mut generated_files = wit_bindgen_core::Files::default();
184187
let mut generator = opts.build();
185188
generator
186-
.generate(&wit_sources.resolve, world, &mut generated_files)
189+
.generate(&wit_sources.resolve, world_id, &mut generated_files)
187190
.map_err(|err| Error::new(Span::call_site(), err.to_string()))?;
188191

189192
let (_, src_bytes) = generated_files
@@ -950,4 +953,94 @@ mod tests {
950953
assert_eq!(parsed.with_entries[0].0, "miden:a/b");
951954
assert_eq!(parsed.with_entries[1].0, "miden:c/d");
952955
}
956+
957+
/// Integration test verifying that `augment_generated_bindings` produces valid Rust code.
958+
///
959+
/// This test simulates realistic wit-bindgen output with custom types, multiple methods,
960+
/// and verifies the augmented output parses as valid Rust and contains the expected
961+
/// wrapper struct with properly qualified type paths.
962+
#[test]
963+
fn test_augment_generated_bindings_integration() {
964+
// Simulate more realistic wit-bindgen output with types and multiple leaf modules
965+
let src = r#"
966+
mod miden {
967+
mod basic_wallet {
968+
mod basic_wallet {
969+
pub struct AssetInfo {
970+
pub amount: u64,
971+
}
972+
973+
pub fn receive_asset(asset: AssetInfo) {}
974+
pub fn move_asset_to_note(asset: AssetInfo, note_idx: u32) -> bool { true }
975+
fn _internal_helper() {} // Should be skipped (underscore prefix)
976+
}
977+
}
978+
mod other_component {
979+
mod other_component {
980+
pub fn do_something(value: u64) -> u64 { value }
981+
}
982+
}
983+
}
984+
mod exports {
985+
mod my_export {
986+
pub fn exported_fn() {} // Should be skipped (exports module)
987+
}
988+
}
989+
"#;
990+
991+
let tokens: TokenStream2 = src.parse().unwrap();
992+
let result = augment_generated_bindings(tokens).unwrap();
993+
994+
// Verify the output parses as valid Rust
995+
let parsed: File =
996+
syn::parse2(result.clone()).expect("augmented bindings should be valid Rust syntax");
997+
998+
// Find the Account struct and impl
999+
let has_account_struct = parsed
1000+
.items
1001+
.iter()
1002+
.any(|item| matches!(item, Item::Struct(s) if s.ident == "Account"));
1003+
let has_account_impl = parsed.items.iter().any(|item| {
1004+
matches!(item, Item::Impl(i) if i.self_ty.to_token_stream().to_string() == "Account")
1005+
});
1006+
1007+
assert!(has_account_struct, "should generate Account struct");
1008+
assert!(has_account_impl, "should generate Account impl");
1009+
1010+
// Find the impl block and verify methods
1011+
let impl_block = parsed
1012+
.items
1013+
.iter()
1014+
.find_map(|item| match item {
1015+
Item::Impl(i) if i.self_ty.to_token_stream().to_string() == "Account" => Some(i),
1016+
_ => None,
1017+
})
1018+
.expect("Account impl should exist");
1019+
1020+
let method_names: Vec<String> = impl_block
1021+
.items
1022+
.iter()
1023+
.filter_map(|item| match item {
1024+
ImplItem::Fn(f) => Some(f.sig.ident.to_string()),
1025+
_ => None,
1026+
})
1027+
.collect();
1028+
1029+
// Should include methods from both leaf modules
1030+
assert!(method_names.contains(&"receive_asset".to_string()));
1031+
assert!(method_names.contains(&"move_asset_to_note".to_string()));
1032+
assert!(method_names.contains(&"do_something".to_string()));
1033+
1034+
// Should NOT include internal helper or exported functions
1035+
assert!(!method_names.contains(&"_internal_helper".to_string()));
1036+
assert!(!method_names.contains(&"exported_fn".to_string()));
1037+
1038+
// Verify type qualification in the result string
1039+
let result_str = result.to_string();
1040+
// AssetInfo should be qualified with its module path
1041+
assert!(
1042+
result_str.contains("miden :: basic_wallet :: basic_wallet :: AssetInfo"),
1043+
"custom types should be qualified with module path"
1044+
);
1045+
}
9531046
}

0 commit comments

Comments
 (0)