< Summary

Information
Class: TeleFlow.Telegram.Internal.Handlers.TelegramHandlerServiceRegistrar
Assembly: TeleFlow.Framework
File(s): /_/src/TeleFlow.Framework/Internal/Handlers/TelegramHandlerServiceRegistrar.cs
Line coverage
93%
Covered lines: 166
Uncovered lines: 12
Coverable lines: 178
Total lines: 335
Line coverage: 93.2%
Branch coverage
83%
Covered branches: 40
Total branches: 48
Branch coverage: 83.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/_/src/TeleFlow.Framework/Internal/Handlers/TelegramHandlerServiceRegistrar.cs

#LineLine coverage
 1using System.Reflection;
 2using Microsoft.Extensions.DependencyInjection;
 3using TeleFlow.Framework.Dispatching;
 4
 5namespace TeleFlow.Telegram.Internal.Handlers;
 6
 7internal static class TelegramHandlerServiceRegistrar
 8{
 9    public static bool TryRegisterGeneratedHandlerType(
 10        IServiceCollection services,
 11        Type handlerType,
 12        IReadOnlyList<TelegramGeneratedHandlersAttribute> registrarAttributes)
 13    {
 814        ArgumentNullException.ThrowIfNull(services);
 815        ArgumentNullException.ThrowIfNull(handlerType);
 816        ArgumentNullException.ThrowIfNull(registrarAttributes);
 17
 818        EnsureValidModuleType(handlerType);
 19
 720        var nextOrder = GetNextHandlerRegistrationOrder(services);
 721        var nextErrorOrder = GetNextErrorHandlerRegistrationOrder(services);
 722        var newDescriptors = new List<TelegramHandlerDescriptor>();
 723        var newErrorDescriptors = new List<TelegramErrorHandlerDescriptor>();
 24
 2825        foreach (var attribute in registrarAttributes)
 26        {
 727            var registrar = CreateGeneratedRegistrar(attribute.RegistrarType);
 728            var registry = new TelegramGeneratedHandlerRegistry();
 29
 730            registrar.Register(registry);
 31
 732            var descriptors = registry.BuildDescriptors(
 733                nextOrder,
 23834                descriptor => descriptor.HandlerType == handlerType);
 735            var errorDescriptors = registry.BuildErrorDescriptors(
 736                nextErrorOrder,
 3537                descriptor => descriptor.HandlerType == handlerType);
 38
 739            nextOrder += descriptors.Count;
 740            nextErrorOrder += errorDescriptors.Count;
 741            newDescriptors.AddRange(descriptors);
 742            newErrorDescriptors.AddRange(errorDescriptors);
 43        }
 44
 745        if (newDescriptors.Count == 0 && newErrorDescriptors.Count == 0)
 46        {
 547            return false;
 48        }
 49
 250        TelegramHandlerRegistrationValidator.EnsureHandlerTypesCanRegister(
 251            services,
 252            [handlerType],
 253            TelegramHandlerRegistrationMode.Generated);
 254        TelegramHandlerRegistrationValidator.EnsureNoDuplicateCommands(services, newDescriptors);
 255        RegisterHandlerTypesAsNeeded(services, [handlerType]);
 256        RegisterDescriptors(services, newDescriptors);
 257        RegisterErrorDescriptors(services, newErrorDescriptors);
 258        RegisterTypeMarkers(services, [handlerType], TelegramHandlerRegistrationMode.Generated);
 259        EnsureDefaultDispatcherRegistered(services);
 60
 261        return true;
 62    }
 63
 64    public static void RegisterGeneratedHandlers(
 65        IServiceCollection services,
 66        Assembly assembly,
 67        IReadOnlyList<TelegramGeneratedHandlersAttribute> registrarAttributes)
 68    {
 3069        ArgumentNullException.ThrowIfNull(services);
 3070        ArgumentNullException.ThrowIfNull(assembly);
 3071        ArgumentNullException.ThrowIfNull(registrarAttributes);
 72
 3073        TelegramHandlerRegistrationValidator.EnsureAssemblyCanRegister(
 3074            services,
 3075            assembly,
 3076            TelegramHandlerRegistrationMode.Generated);
 77
 2978        var nextOrder = GetNextHandlerRegistrationOrder(services);
 2979        var nextErrorOrder = GetNextErrorHandlerRegistrationOrder(services);
 2980        var newDescriptors = new List<TelegramHandlerDescriptor>();
 2981        var newErrorDescriptors = new List<TelegramErrorHandlerDescriptor>();
 2982        var handlerTypes = new List<Type>();
 83
 11684        foreach (var attribute in registrarAttributes)
 85        {
 2986            var registrar = CreateGeneratedRegistrar(attribute.RegistrarType);
 2987            var registry = new TelegramGeneratedHandlerRegistry();
 88
 2989            registrar.Register(registry);
 90
 2991            var descriptors = registry.BuildDescriptors(nextOrder);
 2992            var errorDescriptors = registry.BuildErrorDescriptors(nextErrorOrder);
 93
 2994            nextOrder += descriptors.Count;
 2995            nextErrorOrder += errorDescriptors.Count;
 2996            newDescriptors.AddRange(descriptors);
 2997            newErrorDescriptors.AddRange(errorDescriptors);
 2998            handlerTypes.AddRange(registry.HandlerTypes);
 99        }
 100
 29101        if (newDescriptors.Count == 0 && newErrorDescriptors.Count == 0)
 102        {
 0103            throw new InvalidOperationException(
 0104                "Generated Telegram handler registrars did not register any handlers.");
 105        }
 106
 29107        var distinctHandlerTypes = handlerTypes
 29108            .Distinct()
 29109            .ToArray();
 110
 29111        TelegramHandlerRegistrationValidator.EnsureHandlerTypesCanRegister(
 29112            services,
 29113            distinctHandlerTypes,
 29114            TelegramHandlerRegistrationMode.Generated);
 29115        TelegramHandlerRegistrationValidator.EnsureNoDuplicateCommands(services, newDescriptors);
 29116        RegisterHandlerTypesAsNeeded(services, distinctHandlerTypes);
 29117        RegisterDescriptors(services, newDescriptors);
 29118        RegisterErrorDescriptors(services, newErrorDescriptors);
 29119        RegisterAssemblyMarker(services, assembly, TelegramHandlerRegistrationMode.Generated);
 29120        RegisterTypeMarkers(services, distinctHandlerTypes, TelegramHandlerRegistrationMode.Generated);
 29121        EnsureDefaultDispatcherRegistered(services);
 29122    }
 123
 124    public static void RegisterReflectionHandlers(
 125        IServiceCollection services,
 126        Assembly assembly,
 127        IReadOnlyList<Type> handlerTypes)
 128    {
 11129        ArgumentNullException.ThrowIfNull(services);
 11130        ArgumentNullException.ThrowIfNull(assembly);
 11131        ArgumentNullException.ThrowIfNull(handlerTypes);
 132
 11133        TelegramHandlerRegistrationValidator.EnsureAssemblyCanRegister(
 11134            services,
 11135            assembly,
 11136            TelegramHandlerRegistrationMode.Reflection);
 137
 11138        if (handlerTypes.Count == 0)
 139        {
 0140            throw new InvalidOperationException(
 0141                $"Assembly '{assembly.FullName}' does not contain Telegram handler or error handler types for reflection
 142        }
 143
 11144        RegisterHandlerTypes(
 11145            services,
 11146            handlerTypes,
 11147            TelegramHandlerRegistrationMode.Reflection,
 11148            assembly);
 10149    }
 150
 151    public static void RegisterModuleType(
 152        IServiceCollection services,
 153        Type moduleType)
 154    {
 5155        ArgumentNullException.ThrowIfNull(services);
 5156        ArgumentNullException.ThrowIfNull(moduleType);
 157
 5158        EnsureValidModuleType(moduleType);
 5159        RegisterHandlerTypes(services, [moduleType]);
 4160    }
 161
 162    public static void RegisterHandlerTypes(
 163        IServiceCollection services,
 164        IReadOnlyList<Type> handlerTypes)
 165    {
 339166        RegisterHandlerTypes(
 339167            services,
 339168            handlerTypes,
 339169            TelegramHandlerRegistrationMode.Direct,
 339170            assembly: null);
 311171    }
 172
 173    private static void RegisterHandlerTypes(
 174        IServiceCollection services,
 175        IReadOnlyList<Type> handlerTypes,
 176        TelegramHandlerRegistrationMode registrationMode,
 177        Assembly? assembly)
 178    {
 350179        ArgumentNullException.ThrowIfNull(services);
 350180        ArgumentNullException.ThrowIfNull(handlerTypes);
 181
 350182        TelegramHandlerRegistrationValidator.EnsureHandlerTypesCanRegister(
 350183            services,
 350184            handlerTypes,
 350185            registrationMode);
 186
 348187        var nextOrder = GetNextHandlerRegistrationOrder(services);
 348188        var nextErrorOrder = GetNextErrorHandlerRegistrationOrder(services);
 348189        var newDescriptors = new List<TelegramHandlerDescriptor>();
 348190        var newErrorDescriptors = new List<TelegramErrorHandlerDescriptor>();
 191
 1383192        foreach (var handlerType in handlerTypes)
 193        {
 356194            var descriptors = TelegramHandlerDescriptorBuilder.Build(handlerType, nextOrder);
 332195            var errorDescriptors = TelegramErrorHandlerDescriptorBuilder.Build(handlerType, nextErrorOrder);
 196
 331197            if (descriptors.Count == 0 && errorDescriptors.Count == 0)
 198            {
 0199                throw new InvalidOperationException(
 0200                    $"Telegram handler type '{handlerType.FullName}' does not contain Telegram handler or error handler 
 201            }
 202
 331203            nextOrder += descriptors.Count;
 331204            nextErrorOrder += errorDescriptors.Count;
 331205            newDescriptors.AddRange(descriptors);
 331206            newErrorDescriptors.AddRange(errorDescriptors);
 207        }
 208
 323209        TelegramHandlerRegistrationValidator.EnsureNoDuplicateCommands(services, newDescriptors);
 321210        RegisterHandlerTypesAsNeeded(services, handlerTypes);
 321211        RegisterDescriptors(services, newDescriptors);
 321212        RegisterErrorDescriptors(services, newErrorDescriptors);
 321213        if (assembly is not null)
 214        {
 10215            RegisterAssemblyMarker(services, assembly, registrationMode);
 216        }
 217
 321218        RegisterTypeMarkers(services, handlerTypes, registrationMode);
 321219        EnsureDefaultDispatcherRegistered(services);
 321220    }
 221
 222    private static void EnsureValidModuleType(Type moduleType)
 223    {
 13224        if (moduleType.IsAbstract || moduleType.IsInterface)
 225        {
 0226            throw new InvalidOperationException(
 0227                $"Telegram module type '{moduleType.FullName}' must be a concrete class.");
 228        }
 229
 13230        if (moduleType.GetCustomAttributes(typeof(TeleFlow.Annotations.TelegramModuleAttribute), inherit: false).Length 
 231        {
 1232            throw new InvalidOperationException(
 1233                $"Telegram module type '{moduleType.FullName}' must be marked with {nameof(TeleFlow.Annotations.Telegram
 234        }
 12235    }
 236
 237    private static ITelegramGeneratedHandlerRegistrar CreateGeneratedRegistrar(Type registrarType)
 238    {
 36239        if (!typeof(ITelegramGeneratedHandlerRegistrar).IsAssignableFrom(registrarType))
 240        {
 0241            throw new InvalidOperationException(
 0242                $"Generated Telegram handler registrar '{registrarType.FullName}' must implement {nameof(ITelegramGenera
 243        }
 244
 36245        if (Activator.CreateInstance(registrarType) is not ITelegramGeneratedHandlerRegistrar registrar)
 246        {
 0247            throw new InvalidOperationException(
 0248                $"Generated Telegram handler registrar '{registrarType.FullName}' could not be created.");
 249        }
 250
 36251        return registrar;
 252    }
 253
 254    private static int GetNextHandlerRegistrationOrder(IServiceCollection services)
 255    {
 384256        return services
 11190257            .Where(static descriptor => descriptor.ServiceType == typeof(TelegramHandlerDescriptor))
 227258            .Select(static descriptor => descriptor.ImplementationInstance)
 384259            .OfType<TelegramHandlerDescriptor>()
 227260            .Select(static descriptor => descriptor.RegistrationOrder)
 384261            .DefaultIfEmpty(-1)
 384262            .Max() + 1;
 263    }
 264
 265    private static int GetNextErrorHandlerRegistrationOrder(IServiceCollection services)
 266    {
 384267        return services
 11190268            .Where(static descriptor => descriptor.ServiceType == typeof(TelegramErrorHandlerDescriptor))
 6269            .Select(static descriptor => descriptor.ImplementationInstance)
 384270            .OfType<TelegramErrorHandlerDescriptor>()
 6271            .Select(static descriptor => descriptor.RegistrationOrder)
 384272            .DefaultIfEmpty(-1)
 384273            .Max() + 1;
 274    }
 275
 276    private static void RegisterHandlerTypesAsNeeded(
 277        IServiceCollection services,
 278        IEnumerable<Type> handlerTypes)
 279    {
 3226280        foreach (var handlerType in handlerTypes.Distinct())
 281        {
 53329282            if (services.All(descriptor => descriptor.ServiceType != handlerType))
 283            {
 1261284                services.AddTransient(handlerType);
 285            }
 286        }
 352287    }
 288
 289    private static void RegisterDescriptors(
 290        IServiceCollection services,
 291        IEnumerable<TelegramHandlerDescriptor> descriptors)
 292    {
 3002293        foreach (var descriptor in descriptors)
 294        {
 1149295            services.AddSingleton(descriptor);
 296        }
 352297    }
 298
 299    private static void RegisterErrorDescriptors(
 300        IServiceCollection services,
 301        IEnumerable<TelegramErrorHandlerDescriptor> descriptors)
 302    {
 956303        foreach (var descriptor in descriptors)
 304        {
 126305            services.AddSingleton(descriptor);
 306        }
 352307    }
 308
 309    private static void RegisterAssemblyMarker(
 310        IServiceCollection services,
 311        Assembly assembly,
 312        TelegramHandlerRegistrationMode mode)
 313    {
 39314        services.AddSingleton(new TelegramHandlerAssemblyRegistrationMarker(assembly, mode));
 39315    }
 316
 317    private static void RegisterTypeMarkers(
 318        IServiceCollection services,
 319        IEnumerable<Type> handlerTypes,
 320        TelegramHandlerRegistrationMode mode)
 321    {
 3226322        foreach (var handlerType in handlerTypes.Distinct())
 323        {
 1261324            services.AddSingleton(new TelegramHandlerTypeRegistrationMarker(handlerType, mode));
 325        }
 352326    }
 327
 328    private static void EnsureDefaultDispatcherRegistered(IServiceCollection services)
 329    {
 13747330        if (services.All(static descriptor => descriptor.ServiceType != typeof(IUpdateDispatcher)))
 331        {
 216332            services.AddSingleton<IUpdateDispatcher, TelegramHandlerDispatcher>();
 333        }
 352334    }
 335}

Methods/Properties

TryRegisterGeneratedHandlerType(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Type,System.Collections.Generic.IReadOnlyList`1<TeleFlow.Telegram.TelegramGeneratedHandlersAttribute>)
RegisterGeneratedHandlers(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Reflection.Assembly,System.Collections.Generic.IReadOnlyList`1<TeleFlow.Telegram.TelegramGeneratedHandlersAttribute>)
RegisterReflectionHandlers(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Reflection.Assembly,System.Collections.Generic.IReadOnlyList`1<System.Type>)
RegisterModuleType(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Type)
RegisterHandlerTypes(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Collections.Generic.IReadOnlyList`1<System.Type>)
RegisterHandlerTypes(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Collections.Generic.IReadOnlyList`1<System.Type>,TeleFlow.Telegram.Internal.Handlers.TelegramHandlerRegistrationMode,System.Reflection.Assembly)
EnsureValidModuleType(System.Type)
CreateGeneratedRegistrar(System.Type)
GetNextHandlerRegistrationOrder(Microsoft.Extensions.DependencyInjection.IServiceCollection)
GetNextErrorHandlerRegistrationOrder(Microsoft.Extensions.DependencyInjection.IServiceCollection)
RegisterHandlerTypesAsNeeded(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Collections.Generic.IEnumerable`1<System.Type>)
RegisterDescriptors(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Collections.Generic.IEnumerable`1<TeleFlow.Telegram.Internal.Handlers.TelegramHandlerDescriptor>)
RegisterErrorDescriptors(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Collections.Generic.IEnumerable`1<TeleFlow.Telegram.Internal.Handlers.TelegramErrorHandlerDescriptor>)
RegisterAssemblyMarker(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Reflection.Assembly,TeleFlow.Telegram.Internal.Handlers.TelegramHandlerRegistrationMode)
RegisterTypeMarkers(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Collections.Generic.IEnumerable`1<System.Type>,TeleFlow.Telegram.Internal.Handlers.TelegramHandlerRegistrationMode)
EnsureDefaultDispatcherRegistered(Microsoft.Extensions.DependencyInjection.IServiceCollection)