@@ -44,6 +44,83 @@ public abstract class ConsoleServiceBase
4444
4545 private readonly List < string > _commandHistory = new ( ) ;
4646
47+ /// <summary>
48+ /// Parse sequential arguments.
49+ /// For example, if a method defined as `void Method(string arg1, int arg2, bool arg3)`,
50+ /// the arguments will be parsed as `"arg1" 2 true`.
51+ /// </summary>
52+ /// <param name="method">Method</param>
53+ /// <param name="args">Arguments</param>
54+ /// <returns>Arguments</returns>
55+ /// <exception cref="ArgumentException">Missing argument</exception>
56+ internal object ? [ ] ParseSequentialArguments ( MethodInfo method , IList < CommandToken > args )
57+ {
58+ var parameters = method . GetParameters ( ) ;
59+ var arguments = new List < object ? > ( ) ;
60+ foreach ( var parameter in parameters )
61+ {
62+ if ( TryProcessValue ( parameter . ParameterType , args , parameter == parameters . Last ( ) , out var value ) )
63+ {
64+ arguments . Add ( value ) ;
65+ }
66+ else
67+ {
68+ if ( ! parameter . HasDefaultValue )
69+ throw new ArgumentException ( $ "Missing value for parameter: { parameter . Name } ") ;
70+ arguments . Add ( parameter . DefaultValue ) ;
71+ }
72+ }
73+ return arguments . ToArray ( ) ;
74+ }
75+
76+ /// <summary>
77+ /// Parse indicator arguments.
78+ /// For example, if a method defined as `void Method(string arg1, int arg2, bool arg3)`,
79+ /// the arguments will be parsed as `Method --arg1 "arg1" --arg2 2 --arg3`.
80+ /// </summary>
81+ /// <param name="method">Method</param>
82+ /// <param name="args">Arguments</param>
83+ internal object ? [ ] ParseIndicatorArguments ( MethodInfo method , IList < CommandToken > args )
84+ {
85+ var parameters = method . GetParameters ( ) ;
86+ if ( parameters is null || parameters . Length == 0 ) return [ ] ;
87+
88+ var arguments = parameters . Select ( p => p . HasDefaultValue ? p . DefaultValue : null ) . ToArray ( ) ;
89+ var noValues = parameters . Where ( p => ! p . HasDefaultValue ) . Select ( p => p . Name ) . ToHashSet ( ) ;
90+ for ( int i = 0 ; i < args . Count ; i ++ )
91+ {
92+ var token = args [ i ] ;
93+ if ( ! token . IsIndicator ) continue ;
94+
95+ var paramName = token . Value . Substring ( 2 ) ; // Remove "--"
96+ var parameter = parameters . FirstOrDefault ( p => string . Equals ( p . Name , paramName ) ) ;
97+ if ( parameter == null ) throw new ArgumentException ( $ "Unknown parameter: { paramName } ") ;
98+
99+ var paramIndex = Array . IndexOf ( parameters , parameter ) ;
100+ if ( i + 1 < args . Count && args [ i + 1 ] . IsWhiteSpace ) i += 1 ; // Skip the white space token
101+ if ( i + 1 < args . Count && ! args [ i + 1 ] . IsIndicator ) // Check if next token is a value (not an indicator)
102+ {
103+ var valueToken = args [ i + 1 ] ; // Next token is the value for this parameter
104+ if ( ! TryProcessValue ( parameter . ParameterType , [ args [ i + 1 ] ] , false , out var value ) )
105+ throw new ArgumentException ( $ "Cannot parse value for parameter { paramName } : { valueToken . Value } ") ;
106+ arguments [ paramIndex ] = value ;
107+ noValues . Remove ( paramName ) ;
108+ i += 1 ; // Skip the value token in next iteration
109+ }
110+ else
111+ {
112+ if ( parameter . ParameterType != typeof ( bool ) ) // If parameter is not a bool and no value is provided
113+ throw new ArgumentException ( $ "Missing value for parameter: { paramName } ") ;
114+ arguments [ paramIndex ] = true ;
115+ noValues . Remove ( paramName ) ;
116+ }
117+ }
118+
119+ if ( noValues . Count > 0 )
120+ throw new ArgumentException ( $ "Missing value for parameters: { string . Join ( ',' , noValues ) } ") ;
121+ return arguments ;
122+ }
123+
47124 private bool OnCommand ( string commandLine )
48125 {
49126 if ( string . IsNullOrEmpty ( commandLine ) ) return true ;
@@ -58,26 +135,13 @@ private bool OnCommand(string commandLine)
58135 var consumed = command . IsThisCommand ( tokens ) ;
59136 if ( consumed <= 0 ) continue ;
60137
61- var arguments = new List < object ? > ( ) ;
62138 var args = tokens . Skip ( consumed ) . ToList ( ) . Trim ( ) ;
63139 try
64140 {
65- var parameters = command . Method . GetParameters ( ) ;
66- foreach ( var arg in parameters )
67- {
68- // Parse argument
69- if ( TryProcessValue ( arg . ParameterType , args , arg == parameters . Last ( ) , out var value ) )
70- {
71- arguments . Add ( value ) ;
72- }
73- else
74- {
75- if ( ! arg . HasDefaultValue ) throw new ArgumentException ( $ "Missing argument: { arg . Name } ") ;
76- arguments . Add ( arg . DefaultValue ) ;
77- }
78- }
79-
80- availableCommands . Add ( ( command , arguments . ToArray ( ) ) ) ;
141+ if ( args . Any ( u => u . IsIndicator ) )
142+ availableCommands . Add ( ( command , ParseIndicatorArguments ( command . Method , args ) ) ) ;
143+ else
144+ availableCommands . Add ( ( command , ParseSequentialArguments ( command . Method , args ) ) ) ;
81145 }
82146 catch ( Exception ex )
83147 {
@@ -163,7 +227,6 @@ protected void OnHelpCommand(string key = "")
163227 }
164228
165229 // Sort and show
166-
167230 withHelp . Sort ( ( a , b ) =>
168231 {
169232 var cate = string . Compare ( a . HelpCategory , b . HelpCategory , StringComparison . Ordinal ) ;
@@ -174,6 +237,9 @@ protected void OnHelpCommand(string key = "")
174237 return cate ;
175238 } ) ;
176239
240+ var guide = ( ParameterInfo parameterInfo ) => parameterInfo . HasDefaultValue
241+ ? $ "[ --{ parameterInfo . Name } { parameterInfo . DefaultValue ? . ToString ( ) ?? "" } ]"
242+ : $ "--{ parameterInfo . Name } ";
177243 if ( string . IsNullOrEmpty ( key ) || key . Equals ( "help" , StringComparison . InvariantCultureIgnoreCase ) )
178244 {
179245 string ? last = null ;
@@ -186,24 +252,19 @@ protected void OnHelpCommand(string key = "")
186252 }
187253
188254 Console . Write ( $ "\t { command . Key } ") ;
189- Console . WriteLine ( " " + string . Join ( ' ' ,
190- command . Method . GetParameters ( )
191- . Select ( u => u . HasDefaultValue ? $ "[{ u . Name } ={ ( u . DefaultValue == null ? "null" : u . DefaultValue . ToString ( ) ) } ]" : $ "<{ u . Name } >") )
192- ) ;
255+ Console . WriteLine ( " " + string . Join ( ' ' , command . Method . GetParameters ( ) . Select ( guide ) ) ) ;
193256 }
194257 }
195258 else
196259 {
197260 // Show help for this specific command
198-
199261 string ? last = null ;
200262 string ? lastKey = null ;
201263 bool found = false ;
202264
203265 foreach ( var command in withHelp . Where ( u => u . Key == key ) )
204266 {
205267 found = true ;
206-
207268 if ( last != command . HelpMessage )
208269 {
209270 Console . WriteLine ( $ "{ command . HelpMessage } ") ;
@@ -217,10 +278,7 @@ protected void OnHelpCommand(string key = "")
217278 }
218279
219280 Console . Write ( $ "\t { command . Key } ") ;
220- Console . WriteLine ( " " + string . Join ( ' ' ,
221- command . Method . GetParameters ( )
222- . Select ( u => u . HasDefaultValue ? $ "[{ u . Name } ={ u . DefaultValue ? . ToString ( ) ?? "null" } ]" : $ "<{ u . Name } >") )
223- ) ;
281+ Console . WriteLine ( " " + string . Join ( ' ' , command . Method . GetParameters ( ) . Select ( guide ) ) ) ;
224282 }
225283
226284 if ( ! found )
0 commit comments