1
0
mirror of https://github.com/nsnail/spectre.console.git synced 2025-04-25 12:42:51 +08:00

Added the ITypeResolver to the ExceptionHandler ()

This commit is contained in:
Nils Andresen 2024-01-07 00:34:02 +01:00 committed by GitHub
parent a94bc15746
commit 544e6a92df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 98 additions and 41 deletions

@ -53,7 +53,11 @@ Using the `SetExceptionHandler()` during configuration it is possible to handle
This method comes in two flavours: One that uses the default exitCode (or `return` value) of `-1` and one This method comes in two flavours: One that uses the default exitCode (or `return` value) of `-1` and one
where the exitCode needs to be supplied. where the exitCode needs to be supplied.
### Using `SetExceptionHandler(Func<Exception, int> handler)` The `ITypeResolver?` parameter will be null, when the exception occurs while no `ITypeResolver` is available.
(Basically the `ITypeResolver` will be set, when the exception occurs during a command execution, but not
during the parsing phase and construction of the command.)
### Using `SetExceptionHandler(Func<Exception, ITypeResolver?, int> handler)`
Using this method exceptions can be handled in a custom way. The return value of the handler is used as Using this method exceptions can be handled in a custom way. The return value of the handler is used as
the exitCode for the application. the exitCode for the application.
@ -71,7 +75,7 @@ namespace MyApp
app.Configure(config => app.Configure(config =>
{ {
config.SetExceptionHandler(ex => config.SetExceptionHandler((ex, resolver) =>
{ {
AnsiConsole.WriteException(ex, ExceptionFormats.ShortenEverything); AnsiConsole.WriteException(ex, ExceptionFormats.ShortenEverything);
return -99; return -99;
@ -84,9 +88,9 @@ namespace MyApp
} }
``` ```
### Using `SetExceptionHandler(Action<Exception> handler)` ### Using `SetExceptionHandler(Action<Exception, ITypeResolver?> handler)`
Using this method exceptions can be handled in a custom way, much the same as with the `SetExceptionHandler(Func<Exception, int> handler)`. Using this method exceptions can be handled in a custom way, much the same as with the `SetExceptionHandler(Func<Exception, ITypeResolver?, int> handler)`.
Using the `Action` as the handler however, it is not possible (or required) to supply a return value. Using the `Action` as the handler however, it is not possible (or required) to supply a return value.
The exitCode for the application will be `-1`. The exitCode for the application will be `-1`.
@ -103,7 +107,7 @@ namespace MyApp
app.Configure(config => app.Configure(config =>
{ {
config.SetExceptionHandler(ex => config.SetExceptionHandler((ex, resolver) =>
{ {
AnsiConsole.WriteException(ex, ExceptionFormats.ShortenEverything); AnsiConsole.WriteException(ex, ExceptionFormats.ShortenEverything);
}); });

@ -102,7 +102,7 @@ public sealed class CommandApp : ICommandApp
if (_configurator.Settings.ExceptionHandler != null) if (_configurator.Settings.ExceptionHandler != null)
{ {
return _configurator.Settings.ExceptionHandler(ex); return _configurator.Settings.ExceptionHandler(ex, null);
} }
// Render the exception. // Render the exception.

@ -367,11 +367,11 @@ public static class ConfiguratorExtensions
/// <param name="configurator">The configurator.</param> /// <param name="configurator">The configurator.</param>
/// <param name="exceptionHandler">The Action that handles the exception.</param> /// <param name="exceptionHandler">The Action that handles the exception.</param>
/// <returns>A configurator that can be used to configure the application further.</returns> /// <returns>A configurator that can be used to configure the application further.</returns>
public static IConfigurator SetExceptionHandler(this IConfigurator configurator, Action<Exception> exceptionHandler) public static IConfigurator SetExceptionHandler(this IConfigurator configurator, Action<Exception, ITypeResolver?> exceptionHandler)
{ {
return configurator.SetExceptionHandler(ex => return configurator.SetExceptionHandler((ex, resolver) =>
{ {
exceptionHandler(ex); exceptionHandler(ex, resolver);
return -1; return -1;
}); });
} }
@ -382,7 +382,7 @@ public static class ConfiguratorExtensions
/// <param name="configurator">The configurator.</param> /// <param name="configurator">The configurator.</param>
/// <param name="exceptionHandler">The Action that handles the exception.</param> /// <param name="exceptionHandler">The Action that handles the exception.</param>
/// <returns>A configurator that can be used to configure the application further.</returns> /// <returns>A configurator that can be used to configure the application further.</returns>
public static IConfigurator SetExceptionHandler(this IConfigurator configurator, Func<Exception, int>? exceptionHandler) public static IConfigurator SetExceptionHandler(this IConfigurator configurator, Func<Exception, ITypeResolver?, int>? exceptionHandler)
{ {
if (configurator == null) if (configurator == null)
{ {

@ -91,6 +91,8 @@ public interface ICommandAppSettings
/// <summary> /// <summary>
/// Gets or sets a handler for Exceptions. /// Gets or sets a handler for Exceptions.
/// <para>This handler will not be called, if <see cref="PropagateExceptions"/> is set to <c>true</c>.</para> /// <para>This handler will not be called, if <see cref="PropagateExceptions"/> is set to <c>true</c>.</para>
/// The <see cref="ITypeResolver"/> argument will only be not-null, when the exception occurs during execution of
/// a command. I.e. only when the resolver is available.
/// </summary> /// </summary>
public Func<Exception, int>? ExceptionHandler { get; set; } public Func<Exception, ITypeResolver?, int>? ExceptionHandler { get; set; }
} }

@ -127,6 +127,8 @@ internal sealed class CommandExecutor
CommandContext context, CommandContext context,
ITypeResolver resolver, ITypeResolver resolver,
IConfiguration configuration) IConfiguration configuration)
{
try
{ {
// Bind the command tree against the settings. // Bind the command tree against the settings.
var settings = CommandBinder.Bind(tree, leaf.Command.SettingsType, resolver); var settings = CommandBinder.Bind(tree, leaf.Command.SettingsType, resolver);
@ -161,4 +163,9 @@ internal sealed class CommandExecutor
return result; return result;
} }
catch (Exception ex) when (configuration.Settings is { ExceptionHandler: not null, PropagateExceptions: false })
{
return configuration.Settings.ExceptionHandler(ex, resolver);
}
}
} }

@ -21,7 +21,7 @@ internal sealed class CommandAppSettings : ICommandAppSettings
public ParsingMode ParsingMode => public ParsingMode ParsingMode =>
StrictParsing ? ParsingMode.Strict : ParsingMode.Relaxed; StrictParsing ? ParsingMode.Strict : ParsingMode.Relaxed;
public Func<Exception, int>? ExceptionHandler { get; set; } public Func<Exception, ITypeResolver?, int>? ExceptionHandler { get; set; }
public CommandAppSettings(ITypeRegistrar registrar) public CommandAppSettings(ITypeRegistrar registrar)
{ {

@ -51,7 +51,7 @@ public sealed partial class CommandAppTests
app.Configure(config => app.Configure(config =>
{ {
config.AddCommand<ThrowingCommand>("throw"); config.AddCommand<ThrowingCommand>("throw");
config.SetExceptionHandler(_ => config.SetExceptionHandler((_, _) =>
{ {
exceptionHandled = true; exceptionHandled = true;
}); });
@ -74,7 +74,7 @@ public sealed partial class CommandAppTests
app.Configure(config => app.Configure(config =>
{ {
config.AddCommand<ThrowingCommand>("throw"); config.AddCommand<ThrowingCommand>("throw");
config.SetExceptionHandler(_ => config.SetExceptionHandler((_, _) =>
{ {
exceptionHandled = true; exceptionHandled = true;
return -99; return -99;
@ -88,5 +88,49 @@ public sealed partial class CommandAppTests
result.ExitCode.ShouldBe(-99); result.ExitCode.ShouldBe(-99);
exceptionHandled.ShouldBeTrue(); exceptionHandled.ShouldBeTrue();
} }
[Fact]
public void Should_Have_Resolver_Set_When_Exception_Occurs_In_Command_Execution()
{
// Given
ITypeResolver resolver = null;
var app = new CommandAppTester();
app.Configure(config =>
{
config.AddCommand<ThrowingCommand>("throw");
config.SetExceptionHandler((_, r) =>
{
resolver = r;
});
});
// When
app.Run(new[] { "throw" });
// Then
resolver.ShouldNotBeNull();
}
[Fact]
public void Should_Not_Have_Resolver_Set_When_Exception_Occurs_Before_Command_Execution()
{
// Given
ITypeResolver resolver = null;
var app = new CommandAppTester();
app.Configure(config =>
{
config.AddCommand<DogCommand>("Лайка");
config.SetExceptionHandler((_, r) =>
{
resolver = r;
});
});
// When
app.Run(new[] { "throw", "on", "arg", "parsing" });
// Then
resolver.ShouldBeNull();
}
} }
} }