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(CliConstants.Commands.Branch, cli =>
{
cli.HideBranch();
cli.AddCommand(CliConstants.Commands.Version);
cli.AddCommand(CliConstants.Commands.XmlDoc);
cli.AddCommand(CliConstants.Commands.Explain);
});
_executed = true;
}
return await _executor
.Execute(_configurator, args)
.ConfigureAwait(false);
}
catch (Exception ex)
{
// Should we always propagate when debugging?
if (Debugger.IsAttached
&& ex is CommandAppException appException
&& appException.AlwaysPropagateWhenDebugging)
{
throw;
}
if (_configurator.Settings.PropagateExceptions)
{
throw;
}
if (_configurator.Settings.ExceptionHandler != null)
{
return _configurator.Settings.ExceptionHandler(ex);
}
// Render the exception.
var pretty = GetRenderableErrorMessage(ex);
if (pretty != null)
{
_configurator.Settings.Console.SafeRender(pretty);
}
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()
.Text("[red]Error:[/]")
.Space()
.Text(ex.Message.EscapeMarkup())
.LineBreak(),
};
// 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;
}
}