11use anyhow:: { Context , Result } ;
2+ use clap:: Parser ;
23use serde:: Deserialize ;
3- use std:: {
4- collections:: HashMap ,
5- env, fs,
6- path:: Path ,
7- } ;
4+ use std:: { collections:: HashMap , env, fs, path:: Path } ;
85
9- use cliclack:: { clear_screen, intro, multiselect, outro, confirm} ;
10- use std:: time:: Duration ;
11- use std:: process:: Command ;
6+ use cliclack:: { clear_screen, confirm, intro, multiselect, outro} ;
127use std:: process:: Child ;
8+ use std:: process:: Command ;
9+ use std:: time:: Duration ;
10+
11+ #[ derive( Parser ) ]
12+ #[ command( name = "SCORE CLI" ) ]
13+ #[ command( about = "SCORE CLI showcase entrypoint" , long_about = None ) ]
14+ struct Args {
15+ /// Examples to run (comma-separated names, or "all" to run all examples, skips interactive selection)
16+ #[ arg( long) ]
17+ examples : Option < String > ,
18+ }
1319
1420#[ derive( Debug , Deserialize , Clone ) ]
1521struct AppConfig {
@@ -30,7 +36,7 @@ struct ScoreConfig {
3036fn print_banner ( ) {
3137 let color_code = "\x1b [38;5;99m" ;
3238 let reset_code = "\x1b [0m" ;
33-
39+
3440 let banner = r#"
3541 ███████╗ ██████╗ ██████╗ ██████╗ ███████╗
3642 ██╔════╝ ██╔════╝██╔═══██╗██╔══██╗██╔════╝
@@ -39,26 +45,25 @@ fn print_banner() {
3945 ███████║ ╚██████╗╚██████╔╝██║ ██║███████╗
4046 ╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝
4147"# ;
42-
48+
4349 println ! ( "{}{}{}" , color_code, banner, reset_code) ;
4450}
4551
4652fn pause_for_enter ( ) -> Result < ( ) > {
47- confirm ( "Press Enter to select examples to run... " )
53+ let result = confirm ( "Do you want to select examples to run? " )
4854 . initial_value ( true )
4955 . interact ( ) ?;
56+ if !result {
57+ outro ( "Falling back to the console. Goodbye!" ) ?;
58+ std:: process:: exit ( 0 ) ;
59+ }
5060 Ok ( ( ) )
5161}
5262
5363fn main ( ) -> Result < ( ) > {
54- print_banner ( ) ;
55- intro ( "WELCOME TO SHOWCASE ENTRYPOINT" ) ?;
56- pause_for_enter ( ) ?;
64+ let args = Args :: parse ( ) ;
5765
58- clear_screen ( ) ?;
59-
60- let root_dir = env:: var ( "SCORE_CLI_INIT_DIR" )
61- . unwrap_or_else ( |_| "/showcases" . to_string ( ) ) ;
66+ let root_dir = env:: var ( "SCORE_CLI_INIT_DIR" ) . unwrap_or_else ( |_| "/showcases" . to_string ( ) ) ;
6267
6368 let mut configs = Vec :: new ( ) ;
6469 visit_dir ( Path :: new ( & root_dir) , & mut configs) ?;
@@ -67,50 +72,93 @@ fn main() -> Result<()> {
6772 anyhow:: bail!( "No *.score.json files found under {}" , root_dir) ;
6873 }
6974
70- // Create options for multiselect
71- let options: Vec < ( usize , String , String ) > = configs
72- . iter ( )
73- . enumerate ( )
74- . map ( |( i, c) | ( i, c. name . clone ( ) , c. description . clone ( ) ) )
75- . collect ( ) ;
75+ let selected = if let Some ( examples_str) = args. examples {
76+ // Non-interactive mode: use provided examples
77+ let mut selected_indices = Vec :: new ( ) ;
78+
79+ if examples_str. to_lowercase ( ) == "all" {
80+ // Select all available examples
81+ selected_indices = ( 0 ..configs. len ( ) ) . collect ( ) ;
82+ println ! ( "Running all {} examples" , configs. len( ) ) ;
83+ } else {
84+ // Match specific examples
85+ let requested_examples: Vec < & str > = examples_str. split ( ',' ) . map ( |s| s. trim ( ) ) . collect ( ) ;
86+
87+ for ( i, config) in configs. iter ( ) . enumerate ( ) {
88+ if requested_examples. contains ( & config. name . as_str ( ) ) {
89+ selected_indices. push ( i) ;
90+ }
91+ }
92+
93+ if selected_indices. is_empty ( ) {
94+ anyhow:: bail!(
95+ "No examples found matching: {}. Available examples: {}" ,
96+ examples_str,
97+ configs
98+ . iter( )
99+ . map( |c| c. name. as_str( ) )
100+ . collect:: <Vec <_>>( )
101+ . join( ", " )
102+ ) ;
103+ }
76104
77- let selected: Vec < usize > = multiselect ( "Select examples to run (use space to select (multiselect supported), enter to run examples):" )
78- . items ( & options)
79- . interact ( ) ?;
105+ println ! ( "Running examples: {}" , examples_str) ;
106+ }
80107
81- if selected. is_empty ( ) {
82- outro ( "No examples selected. Goodbye!" ) ?;
83- return Ok ( ( ) ) ;
84- }
108+ selected_indices
109+ } else {
110+ // Interactive mode
111+ print_banner ( ) ;
112+ intro ( "WELCOME TO SHOWCASE ENTRYPOINT" ) ?;
113+ pause_for_enter ( ) ?;
114+
115+ clear_screen ( ) ?;
116+
117+ // Create options for multiselect
118+ let options: Vec < ( usize , String , String ) > = configs
119+ . iter ( )
120+ . enumerate ( )
121+ . map ( |( i, c) | ( i, c. name . clone ( ) , c. description . clone ( ) ) )
122+ . collect ( ) ;
123+
124+ let selected: Vec < usize > = multiselect ( "Select examples to run (use space to select (multiselect supported), enter to run examples):" )
125+ . items ( & options)
126+ . interact ( ) ?;
127+
128+ if selected. is_empty ( ) {
129+ outro ( "No examples selected. Goodbye!" ) ?;
130+ return Ok ( ( ) ) ;
131+ }
132+
133+ selected
134+ } ;
85135
86136 for index in selected {
87137 run_score ( & configs[ index] ) ?;
88138 }
89139
90140 outro ( "All done!" ) ?;
91-
141+
92142 Ok ( ( ) )
93143}
94144
95145fn visit_dir ( dir : & Path , configs : & mut Vec < ScoreConfig > ) -> Result < ( ) > {
96- for entry in fs:: read_dir ( dir)
97- . with_context ( || format ! ( "Failed to read directory {:?}" , dir) ) ?
98- {
146+ for entry in fs:: read_dir ( dir) . with_context ( || format ! ( "Failed to read directory {:?}" , dir) ) ? {
99147 let entry = entry?;
100148 let path = entry. path ( ) ;
101-
149+
102150 if path. is_symlink ( ) {
103151 continue ;
104152 }
105-
153+
106154 if path. is_dir ( ) {
107155 visit_dir ( & path, configs) ?;
108156 continue ;
109157 }
110-
158+
111159 if is_score_file ( & path) {
112- let content = fs :: read_to_string ( & path )
113- . with_context ( || format ! ( "Failed reading {:?}" , path) ) ?;
160+ let content =
161+ fs :: read_to_string ( & path ) . with_context ( || format ! ( "Failed reading {:?}" , path) ) ?;
114162 let value: serde_json:: Value = serde_json:: from_str ( & content)
115163 . with_context ( || format ! ( "Invalid JSON in {:?}" , path) ) ?;
116164 if value. is_array ( ) {
@@ -146,7 +194,12 @@ fn run_score(config: &ScoreConfig) -> Result<()> {
146194
147195 if let Some ( delay_secs) = app. delay {
148196 if delay_secs > 0 {
149- println ! ( "{:?} App {}: waiting {} seconds before start..." , now. elapsed( ) , i + 1 , delay_secs) ;
197+ println ! (
198+ "{:?} App {}: waiting {} seconds before start..." ,
199+ now. elapsed( ) ,
200+ i + 1 ,
201+ delay_secs
202+ ) ;
150203 std:: thread:: sleep ( Duration :: from_secs ( delay_secs) ) ;
151204 }
152205 }
0 commit comments