/// <summary> /// Runs the application with specified command line arguments and environment variables, and returns the exit code. /// </summary> /// <remarks> /// If a <see cref="CommandException"/> or <see cref="TypinException"/> 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 { _console.ResetColor(); _console.ForegroundColor = ConsoleColor.Gray; CliContext.EnvironmentVariables = environmentVariables; PrintStartupMessage(); RootSchema root = new RootSchemaResolver(_configuration).Resolve(); CliContext.RootSchema = root; //TODO: when in commandLineArguments is a string.Empty application crashes int exitCode = await ParseInput(commandLineArguments, root); return(exitCode); } // This may throw pre-execution resolving exceptions which are useful only to the end-user catch (TypinException ex) { _configuration.ExceptionHandler.HandleTypinException(CliContext, ex); return(ExitCodes.FromException(ex)); } // 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) { _configuration.ExceptionHandler.HandleException(CliContext, ex); return(ExitCodes.FromException(ex)); } }
public async ValueTask <int> RunAsync(IEnumerable <string> commandLineArguments, IReadOnlyDictionary <string, string> environmentVariables) { try { _logger.LogInformation("Starting CLI application..."); _console.ResetColor(); _environmentVariablesAccessor.EnvironmentVariables = environmentVariables; RootSchema rootSchema = _rootSchemaAccessor.RootSchema; //Force root schema to resolve. TODO: find a solution to enable lazy root schema resolving. //TODO: OnStart() _startupMessage?.Invoke(_metadata, _console); int exitCode = await StartAppAsync(commandLineArguments); //TODO: OnStop() _logger.LogInformation("CLI application stopped."); return(exitCode); } // This may throw pre-execution resolving exceptions which are useful only to the end-user catch (TypinException ex) { _logger.LogDebug(ex, $"{nameof(TypinException)} occured. Trying to find exception handler."); IEnumerable <ICliExceptionHandler> exceptionHandlers = _serviceProvider.GetServices <ICliExceptionHandler>(); foreach (ICliExceptionHandler handler in exceptionHandlers) { if (handler.HandleException(ex)) { _logger.LogDebug(ex, "Exception handled by {ExceptionHandlerType}.", handler.GetType().FullName); break; } } _logger.LogCritical(ex, "Unhandled Typin exception caused app to terminate."); _console.Error.WithForegroundColor(ConsoleColor.DarkRed, (error) => error.WriteLine($"Unhandled Typin exception caused app to terminate.")); _console.Error.WriteLine(); _console.Error.WriteException(ex); return(ExitCodes.FromException(ex)); } // 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) { _logger.LogCritical(ex, "Unhandled exception caused app to terminate."); _console.Error.WithForegroundColor(ConsoleColor.DarkRed, (error) => error.WriteLine($"Fatal error occured in {_metadata.ExecutableName}.")); _console.Error.WriteLine(); _console.Error.WriteException(ex); return(ExitCodes.FromException(ex)); } }