< Summary

Information
Class: TeleFlow.Generators.TelegramBuiltInFilterFacts
Assembly: TeleFlow.Generators
File(s): /_/src/TeleFlow.Generators/TelegramHandlerMetadataFacts.cs
Line coverage
91%
Covered lines: 78
Uncovered lines: 7
Coverable lines: 85
Total lines: 319
Line coverage: 91.7%
Branch coverage
86%
Covered branches: 33
Total branches: 38
Branch coverage: 86.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
get_MarkerSpecs()100%11100%
GetAttributes()100%44100%
TryGetSpec(...)75%4471.42%
TryGetSpecByGeneratedKind(...)75%4471.42%
SupportsRouteKind(...)93.33%151593.75%
GetInvalidFilterMessage(...)81.81%111187.5%

File(s)

/_/src/TeleFlow.Generators/TelegramHandlerMetadataFacts.cs

#LineLine coverage
 1using System.Text;
 2using Microsoft.CodeAnalysis;
 3
 4namespace TeleFlow.Generators;
 5
 6internal enum TelegramHandlerMetadataRouteKind
 7{
 8    Command,
 9    Message,
 10    Callback,
 11    ChatMember
 12}
 13
 14internal enum TelegramBuiltInFilterTarget
 15{
 16    Message,
 17    Callback,
 18    Chat,
 19    MessageThread,
 20    SenderUser,
 21    SenderChat
 22}
 23
 24internal sealed record TelegramBuiltInFilterSpec(
 25    string AttributeMetadataName,
 26    string GeneratedKind,
 27    TelegramBuiltInFilterTarget Target,
 28    bool IsMarker);
 29
 30internal static class TelegramChatTypeFacts
 31{
 32    public static bool IsKnown(int value)
 33    {
 34        return value is >= 0 and <= 3;
 35    }
 36
 37    public static bool TryMapToTelegramValue(int value, out string telegramValue)
 38    {
 39        switch (value)
 40        {
 41            case 0:
 42                telegramValue = "private";
 43                return true;
 44            case 1:
 45                telegramValue = "group";
 46                return true;
 47            case 2:
 48                telegramValue = "supergroup";
 49                return true;
 50            case 3:
 51                telegramValue = "channel";
 52                return true;
 53            default:
 54                telegramValue = string.Empty;
 55                return false;
 56        }
 57    }
 58}
 59
 60internal static class TelegramMemberStatusFacts
 61{
 62    public const int Creator = 1 << 0;
 63    public const int Administrator = 1 << 1;
 64    public const int Member = 1 << 2;
 65    public const int RestrictedMember = 1 << 3;
 66    public const int RestrictedNotMember = 1 << 4;
 67    public const int Left = 1 << 5;
 68    public const int Banned = 1 << 6;
 69    public const int IsAdmin = Creator | Administrator;
 70    public const int IsMember = Creator | Administrator | Member | RestrictedMember;
 71    public const int IsNotMember = Left | Banned | RestrictedNotMember;
 72    public const int Any =
 73        Creator |
 74        Administrator |
 75        Member |
 76        RestrictedMember |
 77        RestrictedNotMember |
 78        Left |
 79        Banned;
 80
 81    private const int PromotedOldStatus =
 82        Member |
 83        RestrictedMember |
 84        RestrictedNotMember |
 85        Left |
 86        Banned;
 87
 88    public static bool IsValid(int value)
 89    {
 90        return value != 0 && (value & ~Any) == 0;
 91    }
 92
 93    public static bool TryMapTransition(
 94        int transition,
 95        out int oldStatus,
 96        out int newStatus)
 97    {
 98        switch (transition)
 99        {
 100            case 0:
 101                oldStatus = IsNotMember;
 102                newStatus = IsMember;
 103                return true;
 104            case 1:
 105                oldStatus = IsMember;
 106                newStatus = IsNotMember;
 107                return true;
 108            case 2:
 109                oldStatus = PromotedOldStatus;
 110                newStatus = IsAdmin;
 111                return true;
 112            case 3:
 113                oldStatus = IsAdmin;
 114                newStatus = PromotedOldStatus;
 115                return true;
 116            default:
 117                oldStatus = 0;
 118                newStatus = 0;
 119                return false;
 120        }
 121    }
 122
 123    public static bool TryGetRoleRequirementMask(
 124        AttributeData attribute,
 125        out int allowedStatuses)
 126    {
 127        allowedStatuses = 0;
 128
 129        if (attribute.ConstructorArguments.Length == 0)
 130        {
 131            return false;
 132        }
 133
 134        TypedConstant argument = attribute.ConstructorArguments[0];
 135
 136        if (argument.Kind == TypedConstantKind.Array)
 137        {
 138            foreach (TypedConstant value in argument.Values)
 139            {
 140                if (value.Value is not int status)
 141                {
 142                    return false;
 143                }
 144
 145                allowedStatuses |= status;
 146            }
 147
 148            return true;
 149        }
 150
 151        if (argument.Value is int singleStatus)
 152        {
 153            allowedStatuses = singleStatus;
 154            return true;
 155        }
 156
 157        return false;
 158    }
 159}
 160
 161internal static class TelegramCallbackDataFacts
 162{
 163    public const int MaxCallbackDataBytes = 64;
 164
 165    public static bool IsValidPayloadPrefix(string? prefix)
 166    {
 167        return prefix is not null &&
 168               !string.IsNullOrWhiteSpace(prefix) &&
 169               prefix.IndexOf(':') < 0 &&
 170               prefix.IndexOf('%') < 0 &&
 171               !prefix.Any(char.IsWhiteSpace) &&
 172               Encoding.UTF8.GetByteCount(prefix) <= MaxCallbackDataBytes;
 173    }
 174
 175    public static bool IsSupportedFieldType(ITypeSymbol type)
 176    {
 177        return type.SpecialType is SpecialType.System_String or
 178                   SpecialType.System_Int32 or
 179                   SpecialType.System_Int64 or
 180                   SpecialType.System_Boolean ||
 181               type.TypeKind == TypeKind.Enum;
 182    }
 183}
 184
 185internal static class TelegramBuiltInFilterFacts
 186{
 1187    public static IReadOnlyList<TelegramBuiltInFilterSpec> All { get; } = new TelegramBuiltInFilterSpec[]
 1188    {
 1189        new(TelegramHandlerSymbols.ChatTypeAttribute, "ChatType", TelegramBuiltInFilterTarget.Chat, IsMarker: false),
 1190        new(TelegramHandlerSymbols.SenderChatTypeAttribute, "SenderChatType", TelegramBuiltInFilterTarget.SenderChat, Is
 1191        new(TelegramHandlerSymbols.ChatIdAttribute, "ChatId", TelegramBuiltInFilterTarget.Chat, IsMarker: false),
 1192        new(TelegramHandlerSymbols.ChatUsernameAttribute, "ChatUsername", TelegramBuiltInFilterTarget.Chat, IsMarker: fa
 1193        new(TelegramHandlerSymbols.FromUserAttribute, "FromUser", TelegramBuiltInFilterTarget.SenderUser, IsMarker: fals
 1194        new(TelegramHandlerSymbols.HasTextAttribute, "HasText", TelegramBuiltInFilterTarget.Message, IsMarker: true),
 1195        new(TelegramHandlerSymbols.HasPhotoAttribute, "HasPhoto", TelegramBuiltInFilterTarget.Message, IsMarker: true),
 1196        new(TelegramHandlerSymbols.HasDocumentAttribute, "HasDocument", TelegramBuiltInFilterTarget.Message, IsMarker: t
 1197        new(TelegramHandlerSymbols.HasCaptionAttribute, "HasCaption", TelegramBuiltInFilterTarget.Message, IsMarker: tru
 1198        new(TelegramHandlerSymbols.HasVideoAttribute, "HasVideo", TelegramBuiltInFilterTarget.Message, IsMarker: true),
 1199        new(TelegramHandlerSymbols.HasAnimationAttribute, "HasAnimation", TelegramBuiltInFilterTarget.Message, IsMarker:
 1200        new(TelegramHandlerSymbols.HasAudioAttribute, "HasAudio", TelegramBuiltInFilterTarget.Message, IsMarker: true),
 1201        new(TelegramHandlerSymbols.HasVoiceAttribute, "HasVoice", TelegramBuiltInFilterTarget.Message, IsMarker: true),
 1202        new(TelegramHandlerSymbols.HasVideoNoteAttribute, "HasVideoNote", TelegramBuiltInFilterTarget.Message, IsMarker:
 1203        new(TelegramHandlerSymbols.HasStickerAttribute, "HasSticker", TelegramBuiltInFilterTarget.Message, IsMarker: tru
 1204        new(TelegramHandlerSymbols.HasContactAttribute, "HasContact", TelegramBuiltInFilterTarget.Message, IsMarker: tru
 1205        new(TelegramHandlerSymbols.HasLocationAttribute, "HasLocation", TelegramBuiltInFilterTarget.Message, IsMarker: t
 1206        new(TelegramHandlerSymbols.HasVenueAttribute, "HasVenue", TelegramBuiltInFilterTarget.Message, IsMarker: true),
 1207        new(TelegramHandlerSymbols.HasPollAttribute, "HasPoll", TelegramBuiltInFilterTarget.Message, IsMarker: true),
 1208        new(TelegramHandlerSymbols.HasDiceAttribute, "HasDice", TelegramBuiltInFilterTarget.Message, IsMarker: true),
 1209        new(TelegramHandlerSymbols.FromBotAttribute, "FromBot", TelegramBuiltInFilterTarget.SenderUser, IsMarker: false)
 1210        new(TelegramHandlerSymbols.FromPremiumUserAttribute, "FromPremiumUser", TelegramBuiltInFilterTarget.SenderUser, 
 1211        new(TelegramHandlerSymbols.IsReplyAttribute, "IsReply", TelegramBuiltInFilterTarget.Message, IsMarker: true),
 1212        new(TelegramHandlerSymbols.ReplyToBotAttribute, "ReplyToBot", TelegramBuiltInFilterTarget.Message, IsMarker: tru
 1213        new(TelegramHandlerSymbols.MessageThreadIdAttribute, "MessageThreadId", TelegramBuiltInFilterTarget.MessageThrea
 1214        new(TelegramHandlerSymbols.HasMessageThreadAttribute, "HasMessageThread", TelegramBuiltInFilterTarget.MessageThr
 1215        new(TelegramHandlerSymbols.HasCallbackDataAttribute, "HasCallbackData", TelegramBuiltInFilterTarget.Callback, Is
 1216        new(TelegramHandlerSymbols.CallbackDataPrefixAttribute, "CallbackDataPrefix", TelegramBuiltInFilterTarget.Callba
 1217    };
 218
 219    public static IEnumerable<TelegramBuiltInFilterSpec> MarkerSpecs
 220    {
 221        get
 222        {
 2610223            return All.Where(static spec => spec.IsMarker);
 224        }
 225    }
 226
 227    public static IEnumerable<AttributeData> GetAttributes(ISymbol symbol)
 228    {
 12252229        foreach (TelegramBuiltInFilterSpec spec in All)
 230        {
 11900231            foreach (AttributeData attribute in TelegramHandlerSymbols.GetAttributes(
 5915232                         symbol,
 5915233                         spec.AttributeMetadataName,
 5915234                         inherit: true))
 235            {
 36236                yield return attribute;
 237            }
 238        }
 210239    }
 240
 241    public static bool TryGetSpec(
 242        AttributeData attribute,
 243        out TelegramBuiltInFilterSpec spec)
 244    {
 778245        foreach (TelegramBuiltInFilterSpec candidate in All)
 246        {
 372247            if (TelegramHandlerSymbols.IsAttribute(attribute, candidate.AttributeMetadataName))
 248            {
 34249                spec = candidate;
 34250                return true;
 251            }
 252        }
 253
 0254        spec = null!;
 0255        return false;
 34256    }
 257
 258    public static bool TryGetSpecByGeneratedKind(
 259        string generatedKind,
 260        out TelegramBuiltInFilterSpec spec)
 261    {
 613262        foreach (TelegramBuiltInFilterSpec candidate in All)
 263        {
 295264            if (string.Equals(candidate.GeneratedKind, generatedKind, StringComparison.Ordinal))
 265            {
 23266                spec = candidate;
 23267                return true;
 268            }
 269        }
 270
 0271        spec = null!;
 0272        return false;
 23273    }
 274
 275    public static bool SupportsRouteKind(
 276        TelegramBuiltInFilterTarget target,
 277        TelegramHandlerMetadataRouteKind routeKind)
 278    {
 57279        return target switch
 57280        {
 16281            TelegramBuiltInFilterTarget.Chat => true,
 7282            TelegramBuiltInFilterTarget.MessageThread => routeKind is TelegramHandlerMetadataRouteKind.Command or
 7283                TelegramHandlerMetadataRouteKind.Message or
 7284                TelegramHandlerMetadataRouteKind.Callback,
 11285            TelegramBuiltInFilterTarget.Message => routeKind is TelegramHandlerMetadataRouteKind.Command or
 11286                TelegramHandlerMetadataRouteKind.Message,
 12287            TelegramBuiltInFilterTarget.SenderUser => routeKind is TelegramHandlerMetadataRouteKind.Command or
 12288                TelegramHandlerMetadataRouteKind.Message or
 12289                TelegramHandlerMetadataRouteKind.Callback,
 5290            TelegramBuiltInFilterTarget.SenderChat => routeKind is TelegramHandlerMetadataRouteKind.Command or
 5291                TelegramHandlerMetadataRouteKind.Message,
 6292            TelegramBuiltInFilterTarget.Callback => routeKind == TelegramHandlerMetadataRouteKind.Callback,
 0293            _ => false
 57294        };
 295    }
 296
 297    public static string GetInvalidFilterMessage(
 298        TelegramBuiltInFilterTarget target,
 299        TelegramHandlerMetadataRouteKind routeKind)
 300    {
 7301        string routeName = routeKind switch
 7302        {
 2303            TelegramHandlerMetadataRouteKind.Callback => "callback",
 4304            TelegramHandlerMetadataRouteKind.ChatMember => "chat member update",
 1305            _ => "message"
 7306        };
 307
 7308        return target switch
 7309        {
 0310            TelegramBuiltInFilterTarget.Chat => $"Chat filters cannot be used on {routeName} handlers.",
 2311            TelegramBuiltInFilterTarget.MessageThread => $"Message thread filters cannot be used on {routeName} handlers
 2312            TelegramBuiltInFilterTarget.Message => $"Message filters cannot be used on {routeName} handlers.",
 1313            TelegramBuiltInFilterTarget.SenderUser => $"Sender user filters cannot be used on {routeName} handlers.",
 1314            TelegramBuiltInFilterTarget.SenderChat => $"Sender chat filters cannot be used on {routeName} handlers.",
 1315            TelegramBuiltInFilterTarget.Callback => $"Callback filters cannot be used on {routeName} handlers.",
 0316            _ => $"Telegram filter cannot be used on {routeName} handlers."
 7317        };
 318    }
 319}