mirror of
				https://github.com/nsnail/spectre.console.git
				synced 2025-11-01 01:25:27 +08:00 
			
		
		
		
	Added the ITypeResolver to the ExceptionHandler (#1411)
This commit is contained in:
		| @@ -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 | ||||
| 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 | ||||
| the exitCode for the application. | ||||
| @@ -71,7 +75,7 @@ namespace MyApp | ||||
|  | ||||
|             app.Configure(config => | ||||
|             { | ||||
|                 config.SetExceptionHandler(ex => | ||||
|                 config.SetExceptionHandler((ex, resolver) => | ||||
|                 { | ||||
|                     AnsiConsole.WriteException(ex, ExceptionFormats.ShortenEverything); | ||||
|                     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. | ||||
| The exitCode for the application will be `-1`. | ||||
|  | ||||
| @@ -103,7 +107,7 @@ namespace MyApp | ||||
|  | ||||
|             app.Configure(config => | ||||
|             { | ||||
|                 config.SetExceptionHandler(ex => | ||||
|                 config.SetExceptionHandler((ex, resolver) => | ||||
|                 { | ||||
|                     AnsiConsole.WriteException(ex, ExceptionFormats.ShortenEverything); | ||||
|                 }); | ||||
|   | ||||
| @@ -102,7 +102,7 @@ public sealed class CommandApp : ICommandApp | ||||
|  | ||||
|             if (_configurator.Settings.ExceptionHandler != null) | ||||
|             { | ||||
|                 return _configurator.Settings.ExceptionHandler(ex); | ||||
|                 return _configurator.Settings.ExceptionHandler(ex, null); | ||||
|             } | ||||
|  | ||||
|             // Render the exception. | ||||
|   | ||||
| @@ -367,11 +367,11 @@ public static class ConfiguratorExtensions | ||||
|     /// <param name="configurator">The configurator.</param> | ||||
|     /// <param name="exceptionHandler">The Action that handles the exception.</param> | ||||
|     /// <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; | ||||
|         }); | ||||
|     } | ||||
| @@ -382,7 +382,7 @@ public static class ConfiguratorExtensions | ||||
|     /// <param name="configurator">The configurator.</param> | ||||
|     /// <param name="exceptionHandler">The Action that handles the exception.</param> | ||||
|     /// <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) | ||||
|         { | ||||
|   | ||||
| @@ -91,6 +91,8 @@ public interface ICommandAppSettings | ||||
|     /// <summary> | ||||
|     /// 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> | ||||
|     /// 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> | ||||
|     public Func<Exception, int>? ExceptionHandler { get; set; } | ||||
|     public Func<Exception, ITypeResolver?, int>? ExceptionHandler { get; set; } | ||||
| } | ||||
| @@ -128,37 +128,44 @@ internal sealed class CommandExecutor | ||||
|         ITypeResolver resolver, | ||||
|         IConfiguration configuration) | ||||
|     { | ||||
|         // Bind the command tree against the settings. | ||||
|         var settings = CommandBinder.Bind(tree, leaf.Command.SettingsType, resolver); | ||||
|         var interceptors = | ||||
|             ((IEnumerable<ICommandInterceptor>?)resolver.Resolve(typeof(IEnumerable<ICommandInterceptor>)) | ||||
|             ?? Array.Empty<ICommandInterceptor>()).ToList(); | ||||
|         try | ||||
|         { | ||||
|             // Bind the command tree against the settings. | ||||
|             var settings = CommandBinder.Bind(tree, leaf.Command.SettingsType, resolver); | ||||
|             var interceptors = | ||||
|                 ((IEnumerable<ICommandInterceptor>?)resolver.Resolve(typeof(IEnumerable<ICommandInterceptor>)) | ||||
|                 ?? Array.Empty<ICommandInterceptor>()).ToList(); | ||||
| #pragma warning disable CS0618 // Type or member is obsolete | ||||
|         if (configuration.Settings.Interceptor != null) | ||||
|         { | ||||
|             interceptors.Add(configuration.Settings.Interceptor); | ||||
|         } | ||||
|             if (configuration.Settings.Interceptor != null) | ||||
|             { | ||||
|                 interceptors.Add(configuration.Settings.Interceptor); | ||||
|             } | ||||
| #pragma warning restore CS0618 // Type or member is obsolete | ||||
|         foreach (var interceptor in interceptors) | ||||
|         { | ||||
|             interceptor.Intercept(context, settings); | ||||
|         } | ||||
|             foreach (var interceptor in interceptors) | ||||
|             { | ||||
|                 interceptor.Intercept(context, settings); | ||||
|             } | ||||
|  | ||||
|         // Create and validate the command. | ||||
|         var command = leaf.CreateCommand(resolver); | ||||
|         var validationResult = command.Validate(context, settings); | ||||
|         if (!validationResult.Successful) | ||||
|         { | ||||
|             throw CommandRuntimeException.ValidationFailed(validationResult); | ||||
|         } | ||||
|             // Create and validate the command. | ||||
|             var command = leaf.CreateCommand(resolver); | ||||
|             var validationResult = command.Validate(context, settings); | ||||
|             if (!validationResult.Successful) | ||||
|             { | ||||
|                 throw CommandRuntimeException.ValidationFailed(validationResult); | ||||
|             } | ||||
|  | ||||
|         // Execute the command. | ||||
|         var result = await command.Execute(context, settings); | ||||
|         foreach (var interceptor in interceptors) | ||||
|         { | ||||
|             interceptor.InterceptResult(context, settings, ref result); | ||||
|         } | ||||
|             // Execute the command. | ||||
|             var result = await command.Execute(context, settings); | ||||
|             foreach (var interceptor in interceptors) | ||||
|             { | ||||
|                 interceptor.InterceptResult(context, settings, ref result); | ||||
|             } | ||||
|  | ||||
|         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 => | ||||
|         StrictParsing ? ParsingMode.Strict : ParsingMode.Relaxed; | ||||
|  | ||||
|     public Func<Exception, int>? ExceptionHandler { get; set; } | ||||
|     public Func<Exception, ITypeResolver?, int>? ExceptionHandler { get; set; } | ||||
|  | ||||
|     public CommandAppSettings(ITypeRegistrar registrar) | ||||
|     { | ||||
|   | ||||
| @@ -51,7 +51,7 @@ public sealed partial class CommandAppTests | ||||
|             app.Configure(config => | ||||
|             { | ||||
|                 config.AddCommand<ThrowingCommand>("throw"); | ||||
|                 config.SetExceptionHandler(_ => | ||||
|                 config.SetExceptionHandler((_, _) => | ||||
|                 { | ||||
|                     exceptionHandled = true; | ||||
|                 }); | ||||
| @@ -74,7 +74,7 @@ public sealed partial class CommandAppTests | ||||
|             app.Configure(config => | ||||
|             { | ||||
|                 config.AddCommand<ThrowingCommand>("throw"); | ||||
|                 config.SetExceptionHandler(_ => | ||||
|                 config.SetExceptionHandler((_, _) => | ||||
|                 { | ||||
|                     exceptionHandled = true; | ||||
|                     return -99; | ||||
| @@ -88,5 +88,49 @@ public sealed partial class CommandAppTests | ||||
|             result.ExitCode.ShouldBe(-99); | ||||
|             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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Nils Andresen
					Nils Andresen