99using System . Linq ;
1010using System . Reflection ;
1111using System . Text ;
12+ using System . Threading ;
1213using System . Threading . Tasks ;
1314using McMaster . Extensions . CommandLineUtils . Abstractions ;
1415using McMaster . Extensions . CommandLineUtils . Conventions ;
@@ -26,6 +27,8 @@ public partial class CommandLineApplication : IServiceProvider, IDisposable
2627 private const int HelpExitCode = 0 ;
2728 internal const int ValidationErrorExitCode = 1 ;
2829
30+ private static Task < int > DefaultAction ( CancellationToken ct ) => Task . FromResult ( 0 ) ;
31+ private Func < CancellationToken , Task < int > > _action ;
2932 private List < Action < ParseResult > > ? _onParsingComplete ;
3033 internal readonly Dictionary < string , PropertyInfo > _shortOptions = new Dictionary < string , PropertyInfo > ( ) ;
3134 internal readonly Dictionary < string , PropertyInfo > _longOptions = new Dictionary < string , PropertyInfo > ( ) ;
@@ -100,7 +103,7 @@ internal CommandLineApplication(
100103 Commands = new List < CommandLineApplication > ( ) ;
101104 RemainingArguments = new List < string > ( ) ;
102105 _helpTextGenerator = helpTextGenerator ?? throw new ArgumentNullException ( nameof ( helpTextGenerator ) ) ;
103- Invoke = ( ) => 0 ;
106+ _action = DefaultAction ;
104107 _validationErrorHandler = DefaultValidationErrorHandler ;
105108 Out = context . Console . Out ;
106109 Error = context . Console . Error ;
@@ -246,9 +249,24 @@ public CommandOption? OptionHelp
246249 public bool IsShowingInformation { get ; protected set ; }
247250
248251 /// <summary>
252+ /// <para>
253+ /// This property has been marked as obsolete and will be removed in a future version.
254+ /// The recommended replacement for setting this property is <see cref="OnExecute(Func{int})" />
255+ /// and for invoking this property is <see cref="Execute(string[])" />.
256+ /// </para>
257+ /// <para>
249258 /// The action to call when this command is matched and <see cref="IsShowingInformation"/> is <c>false</c>.
259+ /// </para>
250260 /// </summary>
251- public Func < int > Invoke { get ; set ; }
261+ [ Obsolete ( "This property has been marked as obsolete and will be removed in a future version. " +
262+ "The recommended replacement for setting this property is OnExecute(Func<int>) " +
263+ "and for invoking this property is Execute(string[] args)." ) ]
264+ [ EditorBrowsable ( EditorBrowsableState . Never ) ]
265+ public Func < int > Invoke
266+ {
267+ get => ( ) => _action ( GetDefaultCancellationToken ( ) ) . GetAwaiter ( ) . GetResult ( ) ;
268+ set => _action = _ => Task . FromResult ( value ( ) ) ;
269+ }
252270
253271 /// <summary>
254272 /// The long-form of the version to display in generated help text.
@@ -634,16 +652,22 @@ private void AddArgument(CommandArgument argument)
634652 /// <param name="invoke"></param>
635653 public void OnExecute ( Func < int > invoke )
636654 {
637- Invoke = invoke ;
655+ _action = _ => Task . FromResult ( invoke ( ) ) ;
638656 }
639657
640658 /// <summary>
641659 /// Defines an asynchronous callback.
642660 /// </summary>
643661 /// <param name="invoke"></param>
644- public void OnExecute ( Func < Task < int > > invoke )
662+ public void OnExecute ( Func < Task < int > > invoke ) => OnExecuteAsync ( _ => invoke ( ) ) ;
663+
664+ /// <summary>
665+ /// Defines an asynchronous callback.
666+ /// </summary>
667+ /// <param name="invoke"></param>
668+ public void OnExecuteAsync ( Func < CancellationToken , Task < int > > invoke )
645669 {
646- Invoke = ( ) => invoke ( ) . GetAwaiter ( ) . GetResult ( ) ;
670+ _action = invoke ;
647671 }
648672
649673 /// <summary>
@@ -748,6 +772,29 @@ protected virtual void HandleParseResult(ParseResult parseResult)
748772 /// <param name="args"></param>
749773 /// <returns>The return code from <see cref="Invoke"/>.</returns>
750774 public int Execute ( params string [ ] args )
775+ {
776+ return ExecuteAsync ( args ) . GetAwaiter ( ) . GetResult ( ) ;
777+ }
778+
779+ /// <summary>
780+ /// Parses an array of strings using <see cref="Parse(string[])"/>.
781+ /// <para>
782+ /// If <see cref="OptionHelp"/> was matched, the generated help text is displayed in command line output.
783+ /// </para>
784+ /// <para>
785+ /// If <see cref="OptionVersion"/> was matched, the generated version info is displayed in command line output.
786+ /// </para>
787+ /// <para>
788+ /// If there were any validation errors produced from <see cref="GetValidationResult"/>, <see cref="ValidationErrorHandler"/> is invoked.
789+ /// </para>
790+ /// <para>
791+ /// If the parse result matches this command, <see cref="Invoke"/> will be invoked.
792+ /// </para>
793+ /// </summary>
794+ /// <param name="args"></param>
795+ /// <param name="cancellationToken"></param>
796+ /// <returns>The return code from <see cref="Invoke"/>.</returns>
797+ public async Task < int > ExecuteAsync ( string [ ] args , CancellationToken cancellationToken = default )
751798 {
752799 var parseResult = Parse ( args ) ;
753800 var command = parseResult . SelectedCommand ;
@@ -763,7 +810,12 @@ public int Execute(params string[] args)
763810 return command . ValidationErrorHandler ( validationResult ) ;
764811 }
765812
766- return command . Invoke ( ) ;
813+ if ( cancellationToken == CancellationToken . None )
814+ {
815+ cancellationToken = GetDefaultCancellationToken ( ) ;
816+ }
817+
818+ return await command . _action ( cancellationToken ) ;
767819 }
768820
769821 /// <summary>
@@ -881,7 +933,7 @@ public void ShowHelp(bool usePager)
881933 /// The recommended replacement is <see cref="ShowHelp()" />.
882934 /// </summary>
883935 /// <param name="commandName">The subcommand for which to show help. Leave null to show for the current command.</param>
884- [ Obsolete ( "This method has been marked as obsolete and will be removed in a future version." +
936+ [ Obsolete ( "This method has been marked as obsolete and will be removed in a future version. " +
885937 "The recommended replacement is ShowHelp()" ) ]
886938 [ EditorBrowsable ( EditorBrowsableState . Never ) ]
887939 public void ShowHelp ( string ? commandName = null )
@@ -928,7 +980,7 @@ public virtual string GetHelpText()
928980 /// </summary>
929981 /// <param name="commandName"></param>
930982 /// <returns></returns>
931- [ Obsolete ( "This method has been marked as obsolete and will be removed in a future version." +
983+ [ Obsolete ( "This method has been marked as obsolete and will be removed in a future version. " +
932984 "The recommended replacement is GetHelpText()" ) ]
933985 [ EditorBrowsable ( EditorBrowsableState . Never ) ]
934986 public virtual string GetHelpText ( string ? commandName = null )
@@ -1028,6 +1080,16 @@ internal bool MatchesName(string name)
10281080 return _names . Contains ( name ) ;
10291081 }
10301082
1083+ internal CancellationToken GetDefaultCancellationToken ( )
1084+ {
1085+ if ( _context . Console is ICancellationTokenProvider ctp )
1086+ {
1087+ return ctp . Token ;
1088+ }
1089+
1090+ return default ;
1091+ }
1092+
10311093 private sealed class Builder : IConventionBuilder
10321094 {
10331095 private readonly CommandLineApplication _app ;
0 commit comments