11use crate :: interface:: InterfaceGenerator ;
2- use anyhow:: Result ;
2+ use anyhow:: { bail , Result } ;
33use core:: fmt:: Display ;
44use heck:: { ToLowerCamelCase , ToSnakeCase , ToUpperCamelCase } ;
55use std:: collections:: { BTreeMap , HashMap , HashSet } ;
@@ -9,21 +9,53 @@ use std::mem;
99use std:: process:: { Command , Stdio } ;
1010use wit_bindgen_core:: {
1111 uwrite, uwriteln,
12- wit_parser:: { Function , InterfaceId , PackageId , Resolve , TypeId , World , WorldId , WorldKey } ,
12+ wit_parser:: {
13+ Function , InterfaceId , PackageId , Resolve , TypeId , World , WorldId , WorldItem , WorldKey ,
14+ } ,
1315 Files , InterfaceGenerator as _, Source , Types , WorldGenerator ,
1416} ;
1517
1618mod interface;
1719
1820#[ derive( Clone ) ]
1921struct InterfaceName {
22+ /// True when this interface name has been remapped through the use of `with`.
23+ remapped : bool ,
24+
2025 /// The string import name for this interface.
2126 import_name : String ,
2227
2328 /// The string import path for this interface.
2429 import_path : String ,
2530}
2631
32+ /// How an interface should be generated.
33+ enum InterfaceGeneration {
34+ /// Remapped to some other type at the given Go import path.
35+ Remap ( String ) ,
36+ /// Generate the interface.
37+ Generate ,
38+ }
39+
40+ #[ derive( Default ) ]
41+ struct GenerationConfiguration {
42+ map : HashMap < String , InterfaceGeneration > ,
43+ generate_by_default : bool ,
44+ }
45+
46+ impl GenerationConfiguration {
47+ fn get ( & self , key : & str ) -> Option < & InterfaceGeneration > {
48+ self . map . get ( key) . or_else ( || {
49+ self . generate_by_default
50+ . then_some ( & InterfaceGeneration :: Generate )
51+ } )
52+ }
53+
54+ fn insert ( & mut self , name : String , generate : InterfaceGeneration ) {
55+ self . map . insert ( name, generate) ;
56+ }
57+ }
58+
2759#[ derive( Default ) ]
2860struct GoWrpc {
2961 types : Types ,
@@ -40,6 +72,9 @@ struct GoWrpc {
4072
4173 export_paths : Vec < String > ,
4274 deps : Deps ,
75+ with_name_counter : usize ,
76+ /// Interface names to how they should be generated.
77+ with : GenerationConfiguration ,
4378}
4479
4580#[ derive( Default ) ]
@@ -151,6 +186,45 @@ fn generated_preamble() -> String {
151186 )
152187}
153188
189+ #[ cfg( feature = "clap" ) ]
190+ fn parse_with ( s : & str ) -> Result < ( String , WithOption ) , String > {
191+ let ( k, v) = s. split_once ( '=' ) . ok_or_else ( || {
192+ format ! ( "expected string of form `<key>=<value>[,<key>=<value>...]`; got `{s}`" )
193+ } ) ?;
194+ let v = match v {
195+ "generate" => WithOption :: Generate ,
196+ other => WithOption :: Path ( other. to_string ( ) ) ,
197+ } ;
198+ Ok ( ( k. to_string ( ) , v) )
199+ }
200+
201+ /// Represents options for remapping interface bindings.
202+ #[ derive( Debug , Clone ) ]
203+ pub enum WithOption {
204+ /// Remap the interface to an existing Go import path.
205+ Path ( String ) ,
206+ /// Generate the interface bindings.
207+ Generate ,
208+ }
209+
210+ impl fmt:: Display for WithOption {
211+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
212+ match self {
213+ WithOption :: Path ( p) => f. write_fmt ( format_args ! ( "\" {p}\" " ) ) ,
214+ WithOption :: Generate => f. write_str ( "generate" ) ,
215+ }
216+ }
217+ }
218+
219+ impl From < WithOption > for InterfaceGeneration {
220+ fn from ( opt : WithOption ) -> Self {
221+ match opt {
222+ WithOption :: Path ( p) => InterfaceGeneration :: Remap ( p) ,
223+ WithOption :: Generate => InterfaceGeneration :: Generate ,
224+ }
225+ }
226+ }
227+
154228#[ derive( Debug , Clone ) ]
155229#[ cfg_attr( feature = "clap" , derive( clap:: Args ) ) ]
156230pub struct Opts {
@@ -161,13 +235,28 @@ pub struct Opts {
161235 /// Go package path containing the generated bindings
162236 #[ cfg_attr( feature = "clap" , arg( long, default_value = "" ) ) ]
163237 pub package : String ,
238+
239+ /// Remapping of interface names to Go import paths.
240+ ///
241+ /// Argument must be of the form `k=v` and this option can be passed
242+ /// multiple times or one option can be comma separated, for example
243+ /// `k1=v1,k2=v2`.
244+ #[ cfg_attr( feature = "clap" , arg( long, value_parser = parse_with, value_delimiter = ',' ) ) ]
245+ pub with : Vec < ( String , WithOption ) > ,
246+
247+ /// Indicates that all interfaces not specified in `with` should be
248+ /// generated.
249+ #[ cfg_attr( feature = "clap" , arg( long) ) ]
250+ pub generate_all : bool ,
164251}
165252
166253impl Default for Opts {
167254 fn default ( ) -> Self {
168255 Self {
169256 gofmt : true ,
170257 package : String :: new ( ) ,
258+ with : Vec :: new ( ) ,
259+ generate_all : false ,
171260 }
172261 }
173262}
@@ -224,21 +313,41 @@ impl GoWrpc {
224313 id : InterfaceId ,
225314 name : & WorldKey ,
226315 is_export : bool ,
227- ) {
228- let path = compute_module_path ( name, resolve, is_export) ;
229- let import_name = path. join ( "__" ) ;
230- let import_path = if !self . opts . package . is_empty ( ) {
231- format ! ( "{}/{}" , self . opts. package, path. join( "/" ) )
232- } else {
233- path. join ( "/" )
316+ ) -> Result < bool > {
317+ let with_name = resolve. name_world_key ( name) ;
318+ let Some ( remapping) = self . with . get ( & with_name) else {
319+ bail ! ( "no remapping found for {with_name:?} - use `--generate-all` or `--with {with_name}=generate` to generate bindings for this interface" ) ;
234320 } ;
235- self . interface_names . insert (
236- id,
237- InterfaceName {
238- import_name,
239- import_path,
240- } ,
241- ) ;
321+
322+ let entry = match remapping {
323+ InterfaceGeneration :: Remap ( remapped_path) => {
324+ let import_name = format ! ( "__with_name{}" , self . with_name_counter) ;
325+ self . with_name_counter += 1 ;
326+ InterfaceName {
327+ remapped : true ,
328+ import_name,
329+ import_path : remapped_path. clone ( ) ,
330+ }
331+ }
332+ InterfaceGeneration :: Generate => {
333+ let path = compute_module_path ( name, resolve, is_export) ;
334+ let import_name = path. join ( "__" ) ;
335+ let import_path = if !self . opts . package . is_empty ( ) {
336+ format ! ( "{}/{}" , self . opts. package, path. join( "/" ) )
337+ } else {
338+ path. join ( "/" )
339+ } ;
340+ InterfaceName {
341+ remapped : false ,
342+ import_name,
343+ import_path,
344+ }
345+ }
346+ } ;
347+
348+ let remapped = entry. remapped ;
349+ self . interface_names . insert ( id, entry) ;
350+ Ok ( remapped)
242351 }
243352
244353 /// Generates imports and a `Serve` function for the world
@@ -365,6 +474,23 @@ impl WorldGenerator for GoWrpc {
365474 fn preprocess ( & mut self , resolve : & Resolve , world : WorldId ) {
366475 self . types . analyze ( resolve) ;
367476 self . world = Some ( world) ;
477+
478+ let world = & resolve. worlds [ world] ;
479+ // Specify that all imports/exports local to the world's package should be generated
480+ for ( key, item) in world. imports . iter ( ) . chain ( world. exports . iter ( ) ) {
481+ if let WorldItem :: Interface { id, .. } = item {
482+ if resolve. interfaces [ * id] . package == world. package {
483+ let name = resolve. name_world_key ( key) ;
484+ if self . with . get ( & name) . is_none ( ) {
485+ self . with . insert ( name, InterfaceGeneration :: Generate ) ;
486+ }
487+ }
488+ }
489+ }
490+ for ( k, v) in & self . opts . with {
491+ self . with . insert ( k. clone ( ) , v. clone ( ) . into ( ) ) ;
492+ }
493+ self . with . generate_by_default = self . opts . generate_all ;
368494 }
369495
370496 fn import_interface (
@@ -377,7 +503,9 @@ impl WorldGenerator for GoWrpc {
377503 self . interface_last_seen_as_import . insert ( id, true ) ;
378504 let mut gen = self . interface ( Identifier :: Interface ( id, name) , resolve, true ) ;
379505 let ( snake, module_path) = gen. start_append_submodule ( name) ;
380- gen. gen . name_interface ( resolve, id, name, false ) ;
506+ if gen. gen . name_interface ( resolve, id, name, false ) ? {
507+ return Ok ( ( ) ) ;
508+ }
381509 gen. types ( id) ;
382510
383511 let identifier = Identifier :: Interface ( id, name) ;
@@ -446,7 +574,9 @@ impl WorldGenerator for GoWrpc {
446574 self . interface_last_seen_as_import . insert ( id, false ) ;
447575 let mut gen = self . interface ( Identifier :: Interface ( id, name) , resolve, false ) ;
448576 let ( snake, module_path) = gen. start_append_submodule ( name) ;
449- gen. gen . name_interface ( resolve, id, name, true ) ;
577+ if gen. gen . name_interface ( resolve, id, name, true ) ? {
578+ return Ok ( ( ) ) ;
579+ }
450580 gen. types ( id) ;
451581 let exports = gen. generate_exports (
452582 Identifier :: Interface ( id, name) ,
@@ -457,6 +587,7 @@ impl WorldGenerator for GoWrpc {
457587 let InterfaceName {
458588 import_name,
459589 import_path,
590+ ..
460591 } = & self . interface_names [ & id] ;
461592 self . export_paths
462593 . push ( self . deps . import ( import_name. clone ( ) , import_path. clone ( ) ) ) ;
0 commit comments