@@ -271,6 +271,87 @@ def run_no_std(args):
271271 return 0
272272
273273
274+ def list_crates_with_features (args ):
275+ """List crates that have testable features (for matrix generation)."""
276+ metadata = get_workspace_metadata ()
277+ features_config = load_yaml (args .features_file ) or {}
278+
279+ crates_with_features = []
280+
281+ for pkg in metadata ["packages" ]:
282+ crate_name = pkg ["name" ]
283+ cargo_features = set (pkg .get ("features" , {}).keys ())
284+
285+ if not cargo_features :
286+ continue
287+
288+ crate_config = features_config .get (crate_name , {})
289+ skip_list = set (crate_config .get ("skip" , []) or [])
290+ features_to_test = cargo_features - skip_list
291+
292+ if features_to_test :
293+ crates_with_features .append (crate_name )
294+
295+ # Output as JSON for GitHub Actions matrix
296+ import json
297+ print (json .dumps (sorted (crates_with_features )))
298+ return 0
299+
300+
301+ def run_crate_features (args ):
302+ """Run feature tests for a single crate."""
303+ metadata = get_workspace_metadata ()
304+ features_config = load_yaml (args .features_file ) or {}
305+
306+ # Find the crate
307+ pkg = None
308+ for p in metadata ["packages" ]:
309+ if p ["name" ] == args .crate :
310+ pkg = p
311+ break
312+
313+ if not pkg :
314+ github_error (f"Crate not found: { args .crate } " )
315+ return 1
316+
317+ cargo_features = set (pkg .get ("features" , {}).keys ())
318+ if not cargo_features :
319+ print (f"No features to test for { args .crate } " )
320+ return 0
321+
322+ crate_config = features_config .get (args .crate , {})
323+ skip_list = set (crate_config .get ("skip" , []) or [])
324+ features_to_test = sorted (cargo_features - skip_list )
325+
326+ if not features_to_test :
327+ print (f"All features skipped for { args .crate } " )
328+ return 0
329+
330+ failed = []
331+
332+ for feature in features_to_test :
333+ github_group_start (f"{ args .crate } ({ feature } )" )
334+
335+ cmd = ["cargo" , "test" , "-p" , args .crate , "--features" , feature ]
336+ result = subprocess .run (cmd )
337+
338+ github_group_end ()
339+
340+ if result .returncode != 0 :
341+ failed .append (feature )
342+ github_error (f"Feature test failed: { args .crate } --features { feature } " )
343+
344+ if failed :
345+ print ("\n " + "=" * 40 )
346+ print (f"FAILED FEATURES for { args .crate } :" )
347+ for f in failed :
348+ print (f" - { f } " )
349+ print ("=" * 40 )
350+ return 1
351+
352+ return 0
353+
354+
274355def run_feature_checks (args ):
275356 """Run both feature matrix tests and no-std checks."""
276357 print ("=" * 50 )
@@ -362,6 +443,11 @@ def main():
362443 subparsers .add_parser ("run-no-std" , help = "Run no-std checks" )
363444 subparsers .add_parser ("run-feature-checks" , help = "Run both feature tests and no-std checks" )
364445
446+ crate_features_parser = subparsers .add_parser ("run-crate-features" , help = "Run feature tests for a single crate" )
447+ crate_features_parser .add_argument ("crate" , help = "Crate name" )
448+
449+ subparsers .add_parser ("list-crates-with-features" , help = "List crates that have features to test" )
450+
365451 run_group_parser = subparsers .add_parser ("run-group" , help = "Run tests for a group" )
366452 run_group_parser .add_argument ("group" , help = "Group name" )
367453 run_group_parser .add_argument ("--os" , default = "ubuntu-latest" , help = "OS name" )
@@ -376,6 +462,8 @@ def main():
376462 "run-features" : run_features ,
377463 "run-no-std" : run_no_std ,
378464 "run-feature-checks" : run_feature_checks ,
465+ "run-crate-features" : run_crate_features ,
466+ "list-crates-with-features" : list_crates_with_features ,
379467 "run-group" : run_group_tests ,
380468 }
381469
0 commit comments