|
| 1 | +use genco::prelude::*; |
| 2 | +use wit_bindgen_core::wit_parser::{Function, Resolve, SizeAlign, World, WorldItem}; |
| 3 | + |
| 4 | +use crate::go::{GoIdentifier, GoImports, GoResult, GoType}; |
| 5 | + |
| 6 | +pub struct ExportConfig<'a> { |
| 7 | + pub instance: &'a GoIdentifier, |
| 8 | + pub go_imports: &'a GoImports, |
| 9 | + pub world: &'a World, |
| 10 | + pub resolve: &'a Resolve, |
| 11 | + pub sizes: &'a SizeAlign, |
| 12 | +} |
| 13 | + |
| 14 | +pub struct ExportGenerator<'a> { |
| 15 | + config: ExportConfig<'a>, |
| 16 | +} |
| 17 | + |
| 18 | +impl<'a> ExportGenerator<'a> { |
| 19 | + pub fn new(config: ExportConfig<'a>) -> Self { |
| 20 | + Self { config } |
| 21 | + } |
| 22 | + |
| 23 | + fn generate_function(&self, func: &Function, tokens: &mut Tokens<Go>) { |
| 24 | + let params = func |
| 25 | + .params |
| 26 | + .iter() |
| 27 | + .map( |
| 28 | + |(name, wit_type)| match crate::resolve_type(wit_type, self.config.resolve) { |
| 29 | + GoType::ValueOrOk(t) => (GoIdentifier::local(name), *t), |
| 30 | + t => (GoIdentifier::local(name), t), |
| 31 | + }, |
| 32 | + ) |
| 33 | + .collect::<Vec<_>>(); |
| 34 | + |
| 35 | + let result = if let Some(wit_type) = &func.result { |
| 36 | + GoResult::Anon(crate::resolve_type(wit_type, self.config.resolve)) |
| 37 | + } else { |
| 38 | + GoResult::Empty |
| 39 | + }; |
| 40 | + |
| 41 | + let mut f = crate::Func::export(result, self.config.sizes); |
| 42 | + wit_bindgen_core::abi::call( |
| 43 | + self.config.resolve, |
| 44 | + wit_bindgen_core::abi::AbiVariant::GuestExport, |
| 45 | + wit_bindgen_core::abi::LiftLower::LowerArgsLiftResults, |
| 46 | + func, |
| 47 | + &mut f, |
| 48 | + // async is not currently supported |
| 49 | + false, |
| 50 | + ); |
| 51 | + |
| 52 | + let arg_assignments = f |
| 53 | + .args() |
| 54 | + .iter() |
| 55 | + .zip(¶ms) |
| 56 | + .map(|(arg, (param, _))| (arg, param)) |
| 57 | + .collect::<Vec<_>>(); |
| 58 | + let fn_name = &GoIdentifier::public(&func.name); |
| 59 | + quote_in! { *tokens => |
| 60 | + $['\n'] |
| 61 | + func (i *$(self.config.instance)) $fn_name( |
| 62 | + $['\r'] |
| 63 | + ctx $(&self.config.go_imports.context), |
| 64 | + $(for (name, typ) in ¶ms join ($['\r']) => $name $typ,) |
| 65 | + ) $(f.result()) { |
| 66 | + $(for (arg, param) in arg_assignments join ($['\r']) => $arg := $param) |
| 67 | + $(f.body()) |
| 68 | + } |
| 69 | + } |
| 70 | + } |
| 71 | +} |
| 72 | + |
| 73 | +impl FormatInto<Go> for ExportGenerator<'_> { |
| 74 | + fn format_into(self, tokens: &mut Tokens<Go>) { |
| 75 | + for item in self.config.world.exports.values() { |
| 76 | + match item { |
| 77 | + WorldItem::Function(func) => self.generate_function(func, tokens), |
| 78 | + WorldItem::Interface { .. } => todo!("generate interface exports"), |
| 79 | + WorldItem::Type(_) => todo!("generate type exports"), |
| 80 | + } |
| 81 | + } |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +#[cfg(test)] |
| 86 | +mod tests { |
| 87 | + use genco::prelude::*; |
| 88 | + use indexmap::indexmap; |
| 89 | + use wit_bindgen_core::wit_parser::{ |
| 90 | + Function, FunctionKind, Resolve, SizeAlign, Type, World, WorldItem, WorldKey, |
| 91 | + }; |
| 92 | + |
| 93 | + use crate::go::{GoIdentifier, GoImports}; |
| 94 | + |
| 95 | + use super::{ExportConfig, ExportGenerator}; |
| 96 | + |
| 97 | + #[test] |
| 98 | + fn test_generate_function_simple_u32_param() { |
| 99 | + let func = Function { |
| 100 | + name: "add_number".to_string(), |
| 101 | + kind: FunctionKind::Freestanding, |
| 102 | + params: vec![("value".to_string(), Type::U32)], |
| 103 | + result: Some(Type::U32), |
| 104 | + docs: Default::default(), |
| 105 | + stability: Default::default(), |
| 106 | + }; |
| 107 | + |
| 108 | + let world = World { |
| 109 | + name: "test-world".to_string(), |
| 110 | + imports: indexmap! {}, |
| 111 | + exports: indexmap! { |
| 112 | + WorldKey::Name("add-number".to_string()) => WorldItem::Function(func.clone()) |
| 113 | + }, |
| 114 | + docs: Default::default(), |
| 115 | + stability: Default::default(), |
| 116 | + includes: Default::default(), |
| 117 | + include_names: Default::default(), |
| 118 | + package: None, |
| 119 | + }; |
| 120 | + |
| 121 | + let resolve = Resolve::new(); |
| 122 | + let mut sizes = SizeAlign::default(); |
| 123 | + sizes.fill(&resolve); |
| 124 | + let instance = GoIdentifier::public("TestInstance"); |
| 125 | + let go_imports = GoImports::new(); |
| 126 | + |
| 127 | + let config = ExportConfig { |
| 128 | + instance: &instance, |
| 129 | + go_imports: &go_imports, |
| 130 | + world: &world, |
| 131 | + resolve: &resolve, |
| 132 | + sizes: &sizes, |
| 133 | + }; |
| 134 | + |
| 135 | + let generator = ExportGenerator::new(config); |
| 136 | + let mut tokens = Tokens::new(); |
| 137 | + |
| 138 | + // Call the actual generate_function method |
| 139 | + generator.generate_function(&func, &mut tokens); |
| 140 | + |
| 141 | + let generated = tokens.to_string().unwrap(); |
| 142 | + println!("Generated: {}", generated); |
| 143 | + |
| 144 | + // Verify basic function structure |
| 145 | + assert!(generated.contains("func (i *TestInstance) AddNumber(")); |
| 146 | + assert!(generated.contains("value uint32")); |
| 147 | + assert!(generated.contains("ctx context.Context")); |
| 148 | + assert!(generated.contains(") uint32 {")); |
| 149 | + |
| 150 | + // Verify function body |
| 151 | + assert!(generated.contains("arg0 := value")); |
| 152 | + assert!( |
| 153 | + generated.contains("i.module.ExportedFunction(\"add_number\").Call(ctx, uint64(arg0))") |
| 154 | + ); |
| 155 | + assert!(generated.contains("if err0 != nil {")); |
| 156 | + assert!(generated.contains("panic(err0)")); |
| 157 | + assert!(generated.contains("results0 := raw0[0]")); |
| 158 | + assert!(generated.contains("result1 := uint32(results0)")); |
| 159 | + assert!(generated.contains("return result1")); |
| 160 | + } |
| 161 | +} |
0 commit comments