@@ -22,7 +22,7 @@ pub struct Options {
2222 pub command : Command ,
2323}
2424
25- #[ derive( clap:: Args , Debug ) ]
25+ #[ derive( clap:: Args , Clone , Debug ) ]
2626pub struct Common {
2727 /// File or directory containing WIT document(s)
2828 #[ arg( short = 'd' , long) ]
@@ -35,6 +35,19 @@ pub struct Common {
3535 /// Disable non-error output
3636 #[ arg( short = 'q' , long) ]
3737 pub quiet : bool ,
38+
39+ /// Comma-separated list of features that should be enabled when processing
40+ /// WIT files.
41+ ///
42+ /// This enables using `@unstable` annotations in WIT files.
43+ #[ clap( long) ]
44+ features : Vec < String > ,
45+
46+ /// Whether or not to activate all WIT features when processing WIT files.
47+ ///
48+ /// This enables using `@unstable` annotations in WIT files.
49+ #[ clap( long) ]
50+ all_features : bool ,
3851}
3952
4053#[ derive( clap:: Subcommand , Debug ) ]
@@ -120,6 +133,8 @@ fn generate_bindings(common: Common, bindings: Bindings) -> Result<()> {
120133 . wit_path
121134 . unwrap_or_else ( || Path :: new ( "wit" ) . to_owned ( ) ) ,
122135 common. world . as_deref ( ) ,
136+ & common. features ,
137+ common. all_features ,
123138 bindings. world_module . as_deref ( ) ,
124139 & bindings. output_dir ,
125140 )
@@ -140,6 +155,8 @@ fn componentize(common: Common, componentize: Componentize) -> Result<()> {
140155 Runtime :: new ( ) ?. block_on ( crate :: componentize (
141156 common. wit_path . as_deref ( ) ,
142157 common. world . as_deref ( ) ,
158+ & common. features ,
159+ common. all_features ,
143160 & python_path. iter ( ) . map ( |s| s. as_str ( ) ) . collect :: < Vec < _ > > ( ) ,
144161 & componentize
145162 . module_worlds
@@ -241,3 +258,156 @@ fn find_dir(name: &str, path: &Path) -> Result<Option<PathBuf>> {
241258
242259 Ok ( None )
243260}
261+
262+ #[ cfg( test) ]
263+ mod tests {
264+ use std:: io:: Write ;
265+
266+ use super :: * ;
267+
268+ /// Generates a WIT file which has unstable feature "x"
269+ fn gated_x_wit_file ( ) -> Result < tempfile:: NamedTempFile , anyhow:: Error > {
270+ let mut wit = tempfile:: Builder :: new ( )
271+ . prefix ( "gated" )
272+ . suffix ( ".wit" )
273+ . tempfile ( ) ?;
274+ write ! (
275+ wit,
276+ r#"
277+ package foo:bar@1.2.3;
278+
279+ world bindings {{
280+ @unstable(feature = x)
281+ import x: func();
282+ @since(version = 1.2.3)
283+ export y: func();
284+ }}
285+ "# ,
286+ ) ?;
287+ Ok ( wit)
288+ }
289+
290+ #[ test]
291+ fn unstable_bindings_not_generated ( ) -> Result < ( ) > {
292+ // Given a WIT file with gated features
293+ let wit = gated_x_wit_file ( ) ?;
294+ let out_dir = tempfile:: tempdir ( ) ?;
295+
296+ // When generating the bindings for this WIT world
297+ let common = Common {
298+ wit_path : Some ( wit. path ( ) . into ( ) ) ,
299+ world : None ,
300+ quiet : false ,
301+ features : vec ! [ ] ,
302+ all_features : false ,
303+ } ;
304+ let bindings = Bindings {
305+ output_dir : out_dir. path ( ) . into ( ) ,
306+ world_module : None ,
307+ } ;
308+ generate_bindings ( common, bindings) ?;
309+
310+ // Then the gated feature doesn't appear
311+ let generated = fs:: read_to_string ( out_dir. path ( ) . join ( "bindings/__init__.py" ) ) ?;
312+
313+ assert ! ( !generated. contains( "def x() -> None:" ) ) ;
314+
315+ Ok ( ( ) )
316+ }
317+
318+ #[ test]
319+ fn unstable_bindings_generated_with_feature_flag ( ) -> Result < ( ) > {
320+ // Given a WIT file with gated features
321+ let wit = gated_x_wit_file ( ) ?;
322+ let out_dir = tempfile:: tempdir ( ) ?;
323+
324+ // When generating the bindings for this WIT world
325+ let common = Common {
326+ wit_path : Some ( wit. path ( ) . into ( ) ) ,
327+ world : None ,
328+ quiet : false ,
329+ features : vec ! [ "x" . to_owned( ) ] ,
330+ all_features : false ,
331+ } ;
332+ let bindings = Bindings {
333+ output_dir : out_dir. path ( ) . into ( ) ,
334+ world_module : None ,
335+ } ;
336+ generate_bindings ( common, bindings) ?;
337+
338+ // Then the gated feature doesn't appear
339+ let generated = fs:: read_to_string ( out_dir. path ( ) . join ( "bindings/__init__.py" ) ) ?;
340+
341+ assert ! ( generated. contains( "def x() -> None:" ) ) ;
342+
343+ Ok ( ( ) )
344+ }
345+
346+ #[ test]
347+ fn unstable_bindings_generated_for_all_features ( ) -> Result < ( ) > {
348+ // Given a WIT file with gated features
349+ let wit = gated_x_wit_file ( ) ?;
350+ let out_dir = tempfile:: tempdir ( ) ?;
351+
352+ // When generating the bindings for this WIT world
353+ let common = Common {
354+ wit_path : Some ( wit. path ( ) . into ( ) ) ,
355+ world : None ,
356+ quiet : false ,
357+ features : vec ! [ ] ,
358+ all_features : true ,
359+ } ;
360+ let bindings = Bindings {
361+ output_dir : out_dir. path ( ) . into ( ) ,
362+ world_module : None ,
363+ } ;
364+ generate_bindings ( common, bindings) ?;
365+
366+ // Then the gated feature doesn't appear
367+ let generated = fs:: read_to_string ( out_dir. path ( ) . join ( "bindings/__init__.py" ) ) ?;
368+
369+ assert ! ( generated. contains( "def x() -> None:" ) ) ;
370+
371+ Ok ( ( ) )
372+ }
373+
374+ #[ test]
375+ fn unstable_features_used_in_componentize ( ) -> Result < ( ) > {
376+ // Given bindings to a WIT file with gated features and a Python file that uses them
377+ let wit = gated_x_wit_file ( ) ?;
378+ let out_dir = tempfile:: tempdir ( ) ?;
379+ let common = Common {
380+ wit_path : Some ( wit. path ( ) . into ( ) ) ,
381+ world : None ,
382+ quiet : false ,
383+ features : vec ! [ "x" . to_owned( ) ] ,
384+ all_features : false ,
385+ } ;
386+ let bindings = Bindings {
387+ output_dir : out_dir. path ( ) . into ( ) ,
388+ world_module : None ,
389+ } ;
390+ generate_bindings ( common. clone ( ) , bindings) ?;
391+ fs:: write (
392+ out_dir. path ( ) . join ( "app.py" ) ,
393+ r#"
394+ import bindings
395+ from bindings import x
396+
397+ class Bindings(bindings.Bindings):
398+ def y(self) -> None:
399+ x()
400+ "# ,
401+ ) ?;
402+
403+ // Building the component succeeds
404+ let componentize_opts = Componentize {
405+ app_name : "app" . to_owned ( ) ,
406+ python_path : vec ! [ out_dir. path( ) . to_string_lossy( ) . into( ) ] ,
407+ module_worlds : vec ! [ ] ,
408+ output : out_dir. path ( ) . join ( "app.wasm" ) ,
409+ stub_wasi : false ,
410+ } ;
411+ componentize ( common, componentize_opts)
412+ }
413+ }
0 commit comments