using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading.Tasks; using Spectre.Console.Cli.Internal; using Spectre.Console.Rendering; namespace Spectre.Console.Cli { /// /// The entry point for a command line application. /// public sealed class CommandApp : ICommandApp { private readonly Configurator _configurator; private readonly CommandExecutor _executor; private bool _executed; /// /// Initializes a new instance of the class. /// /// The registrar. public CommandApp(ITypeRegistrar? registrar = null) { registrar ??= new DefaultTypeRegistrar(); _configurator = new Configurator(registrar); _executor = new CommandExecutor(registrar); } /// /// Configures the command line application. /// /// The configuration. public void Configure(Action configuration) { if (configuration == null) { throw new ArgumentNullException(nameof(configuration)); } configuration(_configurator); } /// /// Sets the default command. /// /// The command type. public void SetDefaultCommand() where TCommand : class, ICommand { GetConfigurator().SetDefaultCommand(); } /// /// Runs the command line application with specified arguments. /// /// The arguments. /// The exit code from the executed command. public int Run(IEnumerable args) { return RunAsync(args).GetAwaiter().GetResult(); } /// /// Runs the command line application with specified arguments. /// /// The arguments. /// The exit code from the executed command. public async Task RunAsync(IEnumerable args) { try { if (!_executed) { // Add built-in (hidden) commands. _configurator.AddBranch(Constants.Commands.Branch, cli => { cli.HideBranch(); cli.AddCommand(Constants.Commands.Version); cli.AddCommand(Constants.Commands.XmlDoc); }); _executed = true; } return await _executor .Execute(_configurator, args) .ConfigureAwait(false); } catch (Exception ex) { // Render the exception. var pretty = GetRenderableErrorMessage(ex); if (pretty != null) { _configurator.Settings.Console.SafeRender(pretty); } // Should we always propagate when debugging? if (Debugger.IsAttached && ex is CommandAppException appException && appException.AlwaysPropagateWhenDebugging) { throw; } if (_configurator.Settings.PropagateExceptions) { throw; } return -1; } } internal Configurator GetConfigurator() { return _configurator; } private static List? GetRenderableErrorMessage(Exception ex, bool convert = true) { if (ex is CommandAppException renderable && renderable.Pretty != null) { return new List { renderable.Pretty }; } if (convert) { var converted = new List { new Composer() .LineBreak() .Text("[red]Error:[/]") .Space().Text(ex.Message.EscapeMarkup()), }; // Got a renderable inner exception? if (ex.InnerException != null) { var innerRenderable = GetRenderableErrorMessage(ex.InnerException, convert: false); if (innerRenderable != null) { converted.AddRange(innerRenderable); } } return converted; } return null; } } }