< Summary

Information
Class: TeleFlow.Telegram.Internal.Handlers.TelegramHandlerTable
Assembly: TeleFlow.Framework
File(s): /_/src/TeleFlow.Framework/Internal/Handlers/TelegramHandlerTable.cs
Line coverage
97%
Covered lines: 70
Uncovered lines: 2
Coverable lines: 72
Total lines: 133
Line coverage: 97.2%
Branch coverage
89%
Covered branches: 26
Total branches: 29
Branch coverage: 89.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%66100%
EnsureNoDuplicateCallbackPrefixes(...)100%44100%
TryGetCallbackPayloadPrefix(...)100%44100%
OrderForRouteSelection(...)100%11100%
GetRoutePriority(...)80%151587.5%

File(s)

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

#LineLine coverage
 1namespace TeleFlow.Telegram.Internal.Handlers;
 2
 3internal sealed class TelegramHandlerTable
 4{
 5    public TelegramHandlerTable(IEnumerable<TelegramHandlerDescriptor> descriptors)
 6    {
 2027        ArgumentNullException.ThrowIfNull(descriptors);
 8
 2029        var orderedDescriptors = descriptors
 100010            .OrderBy(static descriptor => descriptor.RegistrationOrder)
 20211            .ToArray();
 12
 20213        CommandHandlers = OrderForRouteSelection(orderedDescriptors
 109214            .Where(static descriptor => descriptor.Kind == TelegramHandlerKind.Command)
 20215            .ToArray());
 20216        MessageHandlers = OrderForRouteSelection(orderedDescriptors
 109217            .Where(static descriptor => descriptor.Kind == TelegramHandlerKind.Message)
 20218            .ToArray());
 20219        CallbackHandlers = orderedDescriptors
 109220            .Where(static descriptor => descriptor.Kind == TelegramHandlerKind.Callback)
 20221            .ToArray();
 20222        ChatMemberHandlers = OrderForRouteSelection(orderedDescriptors
 109223            .Where(static descriptor => descriptor.Kind == TelegramHandlerKind.ChatMember)
 20224            .ToArray());
 25
 20226        CommandHandlerCandidates = TelegramHandlerCandidateSet.Create(CommandHandlers);
 20227        MessageHandlerCandidates = TelegramHandlerCandidateSet.Create(MessageHandlers);
 20228        CallbackHandlerCandidates = TelegramCallbackHandlerCandidateSet.Create(CallbackHandlers);
 20229        ChatMemberHandlerCandidates = TelegramHandlerCandidateSet.Create(ChatMemberHandlers);
 20230        HasStatefulHandlers = CommandHandlerCandidates.HasStatefulCandidates ||
 20231                              MessageHandlerCandidates.HasStatefulCandidates ||
 20232                              CallbackHandlerCandidates.HasStatefulCandidates ||
 20233                              ChatMemberHandlerCandidates.HasStatefulCandidates;
 34
 20235        EnsureNoDuplicateCallbackPrefixes(CallbackHandlers);
 20136    }
 37
 38    public IReadOnlyList<TelegramHandlerDescriptor> CommandHandlers { get; }
 39
 40    public IReadOnlyList<TelegramHandlerDescriptor> MessageHandlers { get; }
 41
 42    public IReadOnlyList<TelegramHandlerDescriptor> CallbackHandlers { get; }
 43
 44    public IReadOnlyList<TelegramHandlerDescriptor> ChatMemberHandlers { get; }
 45
 46    public TelegramHandlerCandidateSet CommandHandlerCandidates { get; }
 47
 48    public TelegramHandlerCandidateSet MessageHandlerCandidates { get; }
 49
 50    public TelegramCallbackHandlerCandidateSet CallbackHandlerCandidates { get; }
 51
 52    public TelegramHandlerCandidateSet ChatMemberHandlerCandidates { get; }
 53
 54    public bool HasStatefulHandlers { get; }
 55
 56    private static void EnsureNoDuplicateCallbackPrefixes(IReadOnlyList<TelegramHandlerDescriptor> callbackHandlers)
 57    {
 20258        var duplicatePrefix = callbackHandlers
 25959            .Where(static handler => handler.CallbackPayloadType is not null)
 4360            .Select(static handler => TryGetCallbackPayloadPrefix(handler, out var prefix)
 4361                ? new CallbackPayloadPrefix(handler, prefix)
 4362                : null)
 4363            .Where(static item => item is not null)
 20264            .Cast<CallbackPayloadPrefix>()
 1065            .GroupBy(static item => item.Prefix, StringComparer.Ordinal)
 21166            .FirstOrDefault(static group => group.Count() > 1);
 67
 20268        if (duplicatePrefix is null)
 69        {
 20170            return;
 71        }
 72
 173        var handlers = string.Join(
 174            ", ",
 375            duplicatePrefix.Select(static item => TelegramHandlerDescriptorFormatter.GetDisplayName(item.Handler)));
 76
 177        throw new InvalidOperationException(
 178            $"Duplicate Telegram callback data prefix '{duplicatePrefix.Key}' is used by multiple handlers: {handlers}."
 79    }
 80
 81    private static bool TryGetCallbackPayloadPrefix(
 82        TelegramHandlerDescriptor handler,
 83        out string prefix)
 84    {
 4385        var payloadType = handler.CallbackPayloadType!;
 86
 4387        if (CallbackDataCodec.TryGetGenerated(payloadType, out var generatedCodec))
 88        {
 189            prefix = generatedCodec.Prefix;
 190            return true;
 91        }
 92
 4293        if (CallbackDataMetadata.TryCreate(payloadType, out var metadata))
 94        {
 995            prefix = metadata.Prefix;
 996            return true;
 97        }
 98
 3399        prefix = string.Empty;
 33100        return false;
 101    }
 102
 103    private static TelegramHandlerDescriptor[] OrderForRouteSelection(IEnumerable<TelegramHandlerDescriptor> handlers)
 104    {
 606105        return handlers
 713106            .OrderBy(static handler => GetRoutePriority(handler.Route))
 713107            .ThenByDescending(static handler => handler.Route.Specificity)
 713108            .ThenBy(static handler => handler.RegistrationOrder)
 606109            .ToArray();
 110    }
 111
 112    private static int GetRoutePriority(TelegramRouteDescriptor route)
 113    {
 713114        return route.RouteKind switch
 713115        {
 256116            TelegramRouteKind.CommandExact => 0,
 50117            TelegramRouteKind.CommandTemplate => 1,
 0118            TelegramRouteKind.CommandRegex => 2,
 126119            TelegramRouteKind.TextExact => 10,
 259120            TelegramRouteKind.MessageAny when route.TextFilters.Count > 0 => 10,
 33121            TelegramRouteKind.TextTemplate => 11,
 3122            TelegramRouteKind.TextRegex => 12,
 209123            TelegramRouteKind.MessageAny => 13,
 713124            TelegramRouteKind.ChatMemberUpdated or
 17125                TelegramRouteKind.MyChatMemberUpdated when route.ChatMemberTransitions.Count > 0 => 0,
 713126            TelegramRouteKind.ChatMemberUpdated or
 5127                TelegramRouteKind.MyChatMemberUpdated => 1,
 0128            _ => 100
 713129        };
 130    }
 131
 132    private sealed record CallbackPayloadPrefix(TelegramHandlerDescriptor Handler, string Prefix);
 133}