| | | 1 | | using System.Reflection; |
| | | 2 | | using System.Runtime.ExceptionServices; |
| | | 3 | | using Microsoft.Extensions.DependencyInjection; |
| | | 4 | | |
| | | 5 | | namespace TeleFlow.Telegram.Internal.Handlers; |
| | | 6 | | |
| | | 7 | | internal static class TelegramErrorHandlerInvoker |
| | | 8 | | { |
| | | 9 | | public static async ValueTask<TelegramErrorHandlingResult> InvokeAsync( |
| | | 10 | | TelegramErrorHandlerDescriptor handler, |
| | | 11 | | TelegramErrorContext errorContext, |
| | | 12 | | TelegramUpdateContext telegramContext, |
| | | 13 | | Exception exception, |
| | | 14 | | IReadOnlyDictionary<string, object?> routeValues, |
| | | 15 | | CancellationToken cancellationToken) |
| | | 16 | | { |
| | 22 | 17 | | ArgumentNullException.ThrowIfNull(handler); |
| | 22 | 18 | | ArgumentNullException.ThrowIfNull(errorContext); |
| | 22 | 19 | | ArgumentNullException.ThrowIfNull(telegramContext); |
| | 22 | 20 | | ArgumentNullException.ThrowIfNull(exception); |
| | 22 | 21 | | ArgumentNullException.ThrowIfNull(routeValues); |
| | | 22 | | |
| | 22 | 23 | | var arguments = new object?[handler.Parameters.Count]; |
| | | 24 | | |
| | 156 | 25 | | for (var index = 0; index < handler.Parameters.Count; index++) |
| | | 26 | | { |
| | 56 | 27 | | var parameter = handler.Parameters[index]; |
| | | 28 | | |
| | 56 | 29 | | arguments[index] = parameter.Kind switch |
| | 56 | 30 | | { |
| | 5 | 31 | | TelegramErrorHandlerParameterKind.ErrorContext => errorContext, |
| | 5 | 32 | | TelegramErrorHandlerParameterKind.TelegramContext => GetTelegramContext(handler, parameter, telegramCont |
| | 22 | 33 | | TelegramErrorHandlerParameterKind.Exception => GetException(handler, parameter, exception), |
| | 2 | 34 | | TelegramErrorHandlerParameterKind.RouteValue => GetRouteValue(handler, parameter, routeValues), |
| | 19 | 35 | | TelegramErrorHandlerParameterKind.Service => telegramContext.Services.GetRequiredService(parameter.Param |
| | 3 | 36 | | TelegramErrorHandlerParameterKind.CancellationToken => cancellationToken, |
| | 0 | 37 | | _ => throw new InvalidOperationException($"Unsupported error handler parameter kind '{parameter.Kind}'." |
| | 56 | 38 | | }; |
| | | 39 | | } |
| | | 40 | | |
| | 22 | 41 | | if (handler.GeneratedInvoker is not null) |
| | | 42 | | { |
| | 4 | 43 | | return ValidateResult( |
| | 4 | 44 | | handler, |
| | 4 | 45 | | await handler.GeneratedInvoker(telegramContext.Services, arguments, cancellationToken).ConfigureAwait(fa |
| | | 46 | | } |
| | | 47 | | |
| | 18 | 48 | | if (handler.Method is null) |
| | | 49 | | { |
| | 0 | 50 | | throw new InvalidOperationException( |
| | 0 | 51 | | $"Telegram error handler '{GetDisplayName(handler)}' has no invoker."); |
| | | 52 | | } |
| | | 53 | | |
| | 18 | 54 | | var handlerInstance = telegramContext.Services.GetRequiredService(handler.HandlerType); |
| | | 55 | | object? result; |
| | | 56 | | |
| | | 57 | | try |
| | | 58 | | { |
| | 18 | 59 | | result = handler.Method.Invoke(handlerInstance, arguments); |
| | 18 | 60 | | } |
| | 0 | 61 | | catch (TargetInvocationException reflectionException) when (reflectionException.InnerException is not null) |
| | | 62 | | { |
| | 0 | 63 | | ExceptionDispatchInfo.Capture(reflectionException.InnerException).Throw(); |
| | 0 | 64 | | throw; |
| | | 65 | | } |
| | | 66 | | |
| | 18 | 67 | | return result switch |
| | 18 | 68 | | { |
| | 14 | 69 | | TelegramErrorHandlingResult handlingResult => ValidateResult(handler, handlingResult), |
| | 3 | 70 | | Task<TelegramErrorHandlingResult> task => ValidateResult(handler, await task.ConfigureAwait(false)), |
| | 1 | 71 | | ValueTask<TelegramErrorHandlingResult> valueTask => ValidateResult(handler, await valueTask.ConfigureAwait(f |
| | 0 | 72 | | _ => throw new InvalidOperationException( |
| | 0 | 73 | | $"Telegram error handler '{GetDisplayName(handler)}' returned an unsupported result.") |
| | 18 | 74 | | }; |
| | 21 | 75 | | } |
| | | 76 | | |
| | | 77 | | private static TelegramUpdateContext GetTelegramContext( |
| | | 78 | | TelegramErrorHandlerDescriptor handler, |
| | | 79 | | TelegramErrorHandlerParameterDescriptor parameter, |
| | | 80 | | TelegramUpdateContext context) |
| | | 81 | | { |
| | 5 | 82 | | if (!parameter.ParameterType.IsInstanceOfType(context)) |
| | | 83 | | { |
| | 0 | 84 | | throw new InvalidOperationException( |
| | 0 | 85 | | $"Telegram error handler '{GetDisplayName(handler)}' requires {parameter.ParameterType.Name}, but the se |
| | | 86 | | } |
| | | 87 | | |
| | 5 | 88 | | return context; |
| | | 89 | | } |
| | | 90 | | |
| | | 91 | | private static Exception GetException( |
| | | 92 | | TelegramErrorHandlerDescriptor handler, |
| | | 93 | | TelegramErrorHandlerParameterDescriptor parameter, |
| | | 94 | | Exception exception) |
| | | 95 | | { |
| | 22 | 96 | | if (!parameter.ParameterType.IsInstanceOfType(exception)) |
| | | 97 | | { |
| | 0 | 98 | | throw new InvalidOperationException( |
| | 0 | 99 | | $"Telegram error handler '{GetDisplayName(handler)}' requires {parameter.ParameterType.Name}, but the th |
| | | 100 | | } |
| | | 101 | | |
| | 22 | 102 | | return exception; |
| | | 103 | | } |
| | | 104 | | |
| | | 105 | | private static object? GetRouteValue( |
| | | 106 | | TelegramErrorHandlerDescriptor handler, |
| | | 107 | | TelegramErrorHandlerParameterDescriptor parameter, |
| | | 108 | | IReadOnlyDictionary<string, object?> routeValues) |
| | | 109 | | { |
| | 2 | 110 | | if (string.IsNullOrWhiteSpace(parameter.Name)) |
| | | 111 | | { |
| | 0 | 112 | | throw new InvalidOperationException( |
| | 0 | 113 | | $"Telegram error handler '{GetDisplayName(handler)}' has an unnamed route value parameter."); |
| | | 114 | | } |
| | | 115 | | |
| | 2 | 116 | | if (!routeValues.TryGetValue(parameter.Name, out var value)) |
| | | 117 | | { |
| | 0 | 118 | | throw new InvalidOperationException( |
| | 0 | 119 | | $"Telegram error handler '{GetDisplayName(handler)}' requires route value '{parameter.Name}', but the fa |
| | | 120 | | } |
| | | 121 | | |
| | 2 | 122 | | if (value is null) |
| | | 123 | | { |
| | 0 | 124 | | if (parameter.ParameterType.IsValueType && Nullable.GetUnderlyingType(parameter.ParameterType) is null) |
| | | 125 | | { |
| | 0 | 126 | | throw new InvalidOperationException( |
| | 0 | 127 | | $"Telegram error handler '{GetDisplayName(handler)}' route value '{parameter.Name}' is null and cann |
| | | 128 | | } |
| | | 129 | | |
| | 0 | 130 | | return null; |
| | | 131 | | } |
| | | 132 | | |
| | 2 | 133 | | var underlyingParameterType = Nullable.GetUnderlyingType(parameter.ParameterType); |
| | | 134 | | |
| | 2 | 135 | | if (underlyingParameterType is not null && |
| | 2 | 136 | | underlyingParameterType.IsInstanceOfType(value)) |
| | | 137 | | { |
| | 0 | 138 | | return value; |
| | | 139 | | } |
| | | 140 | | |
| | 2 | 141 | | if (!parameter.ParameterType.IsInstanceOfType(value)) |
| | | 142 | | { |
| | 0 | 143 | | throw new InvalidOperationException( |
| | 0 | 144 | | $"Telegram error handler '{GetDisplayName(handler)}' route value '{parameter.Name}' has type {value.GetT |
| | | 145 | | } |
| | | 146 | | |
| | 2 | 147 | | return value; |
| | | 148 | | } |
| | | 149 | | |
| | | 150 | | private static TelegramErrorHandlingResult ValidateResult( |
| | | 151 | | TelegramErrorHandlerDescriptor handler, |
| | | 152 | | TelegramErrorHandlingResult result) |
| | | 153 | | { |
| | 22 | 154 | | return result switch |
| | 22 | 155 | | { |
| | 18 | 156 | | TelegramErrorHandlingResult.Handled => result, |
| | 3 | 157 | | TelegramErrorHandlingResult.Unhandled => result, |
| | 1 | 158 | | _ => throw new InvalidOperationException( |
| | 1 | 159 | | $"Telegram error handler '{GetDisplayName(handler)}' returned unsupported error handling result '{result |
| | 22 | 160 | | }; |
| | | 161 | | } |
| | | 162 | | |
| | | 163 | | private static string GetDisplayName(TelegramErrorHandlerDescriptor handler) |
| | | 164 | | { |
| | 1 | 165 | | return $"{handler.HandlerType.FullName}.{handler.MethodName}"; |
| | | 166 | | } |
| | | 167 | | } |