Exemple #1
0
        /// <summary>
        /// Runs the application with specified command line arguments and environment variables, and returns the exit code.
        /// </summary>
        /// <remarks>
        /// If a <see cref="CommandException"/> is thrown during command execution, it will be handled and routed to the console.
        /// Additionally, if the debugger is not attached (i.e. the app is running in production), all other exceptions thrown within
        /// this method will be handled and routed to the console as well.
        /// </remarks>
        public async ValueTask <int> RunAsync(
            IReadOnlyList <string> commandLineArguments,
            IReadOnlyDictionary <string, string> environmentVariables)
        {
            try
            {
                var root  = RootSchema.Resolve(_configuration.CommandTypes);
                var input = CommandInput.Parse(commandLineArguments, root.GetCommandNames());

                // Debug mode
                if (_configuration.IsDebugModeAllowed && input.IsDebugDirectiveSpecified)
                {
                    await LaunchAndWaitForDebuggerAsync();
                }

                // Preview mode
                if (_configuration.IsPreviewModeAllowed && input.IsPreviewDirectiveSpecified)
                {
                    WriteCommandLineInput(input);
                    return(ExitCode.Success);
                }

                // Try to get the command matching the input or fallback to default
                var command =
                    root.TryFindCommand(input.CommandName) ??
                    root.TryFindDefaultCommand() ??
                    StubDefaultCommand.Schema;

                // Version option
                if (command.IsVersionOptionAvailable && input.IsVersionOptionSpecified)
                {
                    _console.Output.WriteLine(_metadata.VersionText);
                    return(ExitCode.Success);
                }

                // Get command instance (also used in help text)
                var instance = GetCommandInstance(command);

                // To avoid instantiating the command twice, we need to get default values
                // before the arguments are bound to the properties
                var defaultValues = command.GetArgumentValues(instance);

                // Help option
                if (command.IsHelpOptionAvailable && input.IsHelpOptionSpecified ||
                    command == StubDefaultCommand.Schema && !input.Parameters.Any() && !input.Options.Any())
                {
                    _helpTextWriter.Write(root, command, defaultValues);
                    return(ExitCode.Success);
                }

                // Bind arguments
                try
                {
                    command.Bind(instance, input, environmentVariables);
                }
                // This may throw exceptions which are useful only to the end-user
                catch (CliFxException ex)
                {
                    WriteError(ex.ToString());
                    _helpTextWriter.Write(root, command, defaultValues);

                    return(ExitCode.FromException(ex));
                }

                // Execute the command
                try
                {
                    await instance.ExecuteAsync(_console);

                    return(ExitCode.Success);
                }
                // Swallow command exceptions and route them to the console
                catch (CommandException ex)
                {
                    WriteError(ex.ToString());

                    if (ex.ShowHelp)
                    {
                        _helpTextWriter.Write(root, command, defaultValues);
                    }

                    return(ex.ExitCode);
                }
            }
            // To prevent the app from showing the annoying Windows troubleshooting dialog,
            // we handle all exceptions and route them to the console nicely.
            // However, we don't want to swallow unhandled exceptions when the debugger is attached,
            // because we still want the IDE to show them to the developer.
            catch (Exception ex) when(!Debugger.IsAttached)
            {
                WriteError(ex.ToString());
                return(ExitCode.FromException(ex));
            }
        }