1+ use crate :: arg_parsing:: ArgParser ;
12use crate :: commands:: build:: env_toml:: { Account , Contract , Environment , Network } ;
23use clap:: Parser ;
34use degit:: degit;
4- use dialoguer:: { Confirm , Input , Select } ;
55use indexmap:: IndexMap ;
66use std:: fs;
77use std:: fs:: { create_dir_all, metadata, read_dir, write} ;
@@ -20,6 +20,9 @@ pub struct Cmd {
2020 /// The path to the existing workspace (defaults to current directory)
2121 #[ arg( default_value = "." ) ]
2222 pub workspace_path : PathBuf ,
23+ /// Skip the prompt to fill in constructor arguments
24+ #[ arg( long) ]
25+ pub skip_prompt : bool ,
2326}
2427
2528/// Errors that can occur during upgrade
@@ -47,8 +50,8 @@ pub enum Error {
4750 TomlDeserializeError ( #[ from] toml:: de:: Error ) ,
4851 #[ error( transparent) ]
4952 BuildError ( #[ from] build:: Error ) ,
50- #[ error( "Failed to get constructor arguments: {0}" ) ]
51- ConstructorArgsError ( String ) ,
53+ #[ error( "Failed to get constructor arguments: {0:? }" ) ]
54+ ConstructorArgsError ( arg_parsing :: Error ) ,
5255 #[ error( "WASM file not found for contract '{0}'. Please build the contract first." ) ]
5356 WasmFileNotFound ( String ) ,
5457 #[ error( transparent) ]
@@ -350,146 +353,8 @@ impl Cmd {
350353
351354 // Read the WASM file and get spec entries
352355 let raw_wasm = fs:: read ( & wasm_path) ?;
353- let entries = soroban_spec_tools:: contract:: Spec :: new ( & raw_wasm) ?. spec ;
354- let spec = soroban_spec_tools:: Spec :: new ( entries. clone ( ) ) ;
355-
356- // Check if constructor function exists
357- let Ok ( func) = spec. find_function ( "__constructor" ) else {
358- return Ok ( None ) ;
359- } ;
360- if func. inputs . is_empty ( ) {
361- return Ok ( None ) ;
362- }
363-
364- // Build the custom command for the constructor
365- let cmd = arg_parsing:: build_custom_cmd ( "__constructor" , & spec) . map_err ( |e| {
366- Error :: ConstructorArgsError ( format ! ( "Failed to build constructor command: {e}" ) )
367- } ) ?;
368-
369- println ! ( "\n 📋 Contract '{contract_name}' requires constructor arguments:" ) ;
370-
371- let mut args = Vec :: new ( ) ;
372-
373- // Loop through the command arguments, skipping file args
374- for arg in cmd. get_arguments ( ) {
375- let arg_name = arg. get_id ( ) . as_str ( ) ;
376-
377- // Skip file arguments (they end with -file-path)
378- if arg_name. ends_with ( "-file-path" ) {
379- continue ;
380- }
381-
382- if let Some ( arg_value) = Self :: handle_constructor_argument ( arg) ? {
383- args. push ( arg_value) ;
384- }
385- }
386-
387- Ok ( ( !args. is_empty ( ) ) . then ( || args. join ( " " ) ) )
388- }
389-
390- fn handle_constructor_argument ( arg : & clap:: Arg ) -> Result < Option < String > , Error > {
391- let arg_name = arg. get_id ( ) . as_str ( ) ;
392-
393- let help_text = arg. get_long_help ( ) . or ( arg. get_help ( ) ) . map_or_else (
394- || "No description available" . to_string ( ) ,
395- ToString :: to_string,
396- ) ;
397-
398- let value_name = arg
399- . get_value_names ( )
400- . map_or_else ( || "VALUE" . to_string ( ) , |names| names. join ( " " ) ) ;
401-
402- // Display help text before the prompt
403- println ! ( "\n --{arg_name}" ) ;
404- if value_name != "bool" && !help_text. is_empty ( ) {
405- println ! ( " {help_text}" ) ;
406- }
407-
408- if value_name == "bool" {
409- Self :: handle_bool_argument ( arg_name)
410- } else if value_name. contains ( '|' ) && Self :: is_simple_enum ( & value_name) {
411- Self :: handle_simple_enum_argument ( arg_name, & value_name)
412- } else {
413- // For all other types (complex enums, structs, strings), use string input
414- Self :: handle_formatted_input ( arg_name)
415- }
416- }
417-
418- fn is_simple_enum ( value_name : & str ) -> bool {
419- value_name. split ( '|' ) . all ( |part| {
420- let trimmed = part. trim ( ) ;
421- trimmed. parse :: < i32 > ( ) . is_ok ( ) || trimmed. chars ( ) . all ( |c| c. is_alphabetic ( ) || c == '_' )
422- } )
423- }
424-
425- fn handle_formatted_input ( arg_name : & str ) -> Result < Option < String > , Error > {
426- let input_result: Result < String , _ > = Input :: new ( )
427- . with_prompt ( format ! ( "Enter value for --{arg_name}" ) )
428- . allow_empty ( true )
429- . interact ( ) ;
430-
431- let value = input_result
432- . as_deref ( )
433- . map ( str:: trim)
434- . map_err ( |e| Error :: ConstructorArgsError ( format ! ( "Input error: {e}" ) ) ) ?;
435-
436- let value = if value. is_empty ( ) {
437- "# TODO: Fill in value"
438- } else {
439- // Check if the value is already quoted
440- let is_already_quoted = ( value. starts_with ( '"' ) && value. ends_with ( '"' ) )
441- || ( value. starts_with ( '\'' ) && value. ends_with ( '\'' ) ) ;
442-
443- // Only wrap in quotes if it's not already quoted and contains special characters or spaces
444- if !is_already_quoted
445- && ( value. contains ( ' ' )
446- || value. contains ( '{' )
447- || value. contains ( '[' )
448- || value. contains ( '"' ) )
449- {
450- & format ! ( "'{value}'" )
451- } else {
452- value
453- }
454- } ;
455- Ok ( Some ( format ! ( "--{arg_name} {value}" ) ) )
456- }
457-
458- fn handle_simple_enum_argument (
459- arg_name : & str ,
460- value_name : & str ,
461- ) -> Result < Option < String > , Error > {
462- let mut select = Select :: new ( )
463- . with_prompt ( format ! ( "Select value for --{arg_name}" ) )
464- . default ( 0 ) ; // This will show the cursor on the first option initially
465-
466- // Add "Skip" option
467- select = select. item ( "(Skip - leave blank)" ) ;
468-
469- // Parse the values from "a | b | c" format and add numeric options
470- let values: Vec < _ > = value_name. split ( '|' ) . collect ( ) ;
471- for value in & values {
472- select = select. item ( format ! ( "Value: {value}" ) ) ;
473- }
474-
475- let selection = select
476- . interact ( )
477- . map_err ( |e| Error :: ConstructorArgsError ( format ! ( "Input error: {e}" ) ) ) ?;
478-
479- Ok ( ( selection > 0 ) . then ( || {
480- // User selected an actual value (not skip)
481- let selected_value = values[ selection - 1 ] ;
482- format ! ( "--{arg_name} {selected_value}" )
483- } ) )
484- }
485-
486- fn handle_bool_argument ( arg_name : & str ) -> Result < Option < String > , Error > {
487- let bool_value = Confirm :: new ( )
488- . with_prompt ( format ! ( "Set --{arg_name} to true?" ) )
489- . default ( false )
490- . interact ( )
491- . map_err ( |e| Error :: ConstructorArgsError ( format ! ( "Input error: {e}" ) ) ) ?;
492- Ok ( bool_value. then ( || format ! ( "--{arg_name}" ) ) )
356+ ArgParser :: get_constructor_args ( self . skip_prompt , contract_name, & raw_wasm)
357+ . map_err ( Error :: ConstructorArgsError )
493358 }
494359
495360 fn setup_env_file ( & self ) -> Result < ( ) , Error > {
0 commit comments