< Summary

Line coverage
88%
Covered lines: 1599
Uncovered lines: 217
Coverable lines: 1816
Total lines: 3289
Line coverage: 88%
Branch coverage
68%
Covered branches: 757
Total branches: 1098
Branch coverage: 68.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: .cctor()100%11100%
File 1: Initialize(...)100%121295.65%
File 1: GetHandlerMethod(...)87.5%88100%
File 1: GetCallbackDataPayload(...)60%101090%
File 1: GetClassBasedHandlerMethod(...)58.33%131281.81%
File 1: GetErrorHandlerMethod(...)66.66%6683.33%
File 1: TryBuildHandler(...)59.09%444495%
File 1: TryBuildErrorHandler(...)46.66%303096.29%
File 1: TryBuildErrorExceptionTypes(...)70%111077.77%
File 1: TryBuildErrorParameters(...)65%222082.14%
File 1: TryBuildAutoAnswerCallback(...)85.71%141491.66%
File 1: BuildRoutes(...)76.72%13211689.33%
File 1: TryBuildFilters(...)50%4471.42%
File 1: AppendFilters(...)61.29%646291.74%
File 1: AppendChatTypeFilters(...)66.66%131281.25%
File 1: AppendSenderIdFilters(...)60%111081.25%
File 1: AppendMarkerFilter(...)100%22100%
File 1: SupportsGeneratedFilter(...)50%22100%
File 1: TryPrepareCustomFilters(...)83.33%6681.81%
File 1: TryResolveCustomFilterContextType(...)62.5%9871.42%
File 1: ToFullyQualifiedTypeName(...)100%11100%
File 1: CanonicalizeChatUsername(...)50%5466.66%
File 1: HasChatMemberTransitionAttributes(...)50%66100%
File 1: TryBuildRoleRequirements(...)66.66%7672.72%
File 1: TryBuildChatMemberTransitions(...)60%252077.27%
File 1: ToMetadataRouteKind(...)40%6562.5%
File 1: IsInvalidCustomFilterType(...)100%11100%
File 1: TryGetTelegramFilterContextTypes(...)64.28%1414100%
File 1: GetModuleName(...)100%88100%
File 1: TryGetExpectedContextType(...)100%44100%
File 1: HasExactlyOneExpectedContext(...)87.5%88100%
File 1: HasValidCallbackPayloadParameter(...)75%4483.33%
File 1: IsInvalidCallbackPayloadType(...)100%11100%
File 1: IsAccessibleFromGeneratedCode(...)62.5%9875%
File 1: TryGetCallbackDataFields(...)75%4490.9%
File 1: TryGetConstructorCallbackDataFields(...)60%101095%
File 1: TryGetPropertyCallbackDataFields(...)0%156120%
File 1: TryGetCallbackDataFieldKind(...)87.5%252488.23%
File 1: IsInvalidConcreteNamedType(...)50%1212100%
File 1: IsSupportedReturnType(...)100%22100%
File 1: TryGetErrorReturnKind(...)62.5%171685.71%
File 1: IsValidCommand(...)50%88100%
File 1: GetConstructorString(...)50%4475%
File 1: TryGetPrefixes(...)78.57%141494.11%
File 1: GetCommandPrefixMode(...)100%66100%
File 1: HasInvalidNoPrefixConfiguration(...)100%22100%
File 1: IsSupportedCommandPrefixMode(...)50%22100%
File 1: HasNamedArgument(...)100%11100%
File 1: CommandPatternStartsWithPrefix(...)100%11100%
File 1: GetNamedBool(...)100%66100%
File 1: TryParseTemplateRouteValues(...)75%262486.2%
File 1: TryParseRegexRouteValues(...)83.33%7671.42%
File 1: ContainsBrace(...)100%22100%
File 1: TryResolveRouteValueNames(...)72.72%262280%
File 1: IsSupportedRouteValueParameterType(...)100%11100%
File 1: IsNullableRouteValueParameter(...)50%6683.33%
File 1: GetRouteValueParameterSpecialType(...)100%88100%
File 1: RouteConstraintMatchesParameter(...)66.66%7675%
File 1: GetRouteValueTypeName(...)75%4483.33%
File 1: BuildTextFilters(...)71.42%1414100%
File 1: GetRouteAttributes(...)100%44100%
File 1: GetGenericRouteAttributes(...)100%44100%
File 1: IsClassBasedHandlerType(...)100%44100%
File 1: IsClassBasedRouteCompatible(...)61.11%191884.61%
File 1: GetDeclaredHandleAsyncMethods(...)50%66100%
File 1: TryGetCallbackHandlerPayloadType(...)100%66100%
File 1: IsAssignableTo(...)100%66100%
File 1: IsDirectlyAssignableTo(...)50%22100%
File 1: TryBuildStates(...)88.88%1818100%
File 1: GetGenericStateAttributes(...)100%11100%
File 1: TryResolveTypedState(...)50%201882.35%
File 1: TryResolveSceneStep(...)66.66%121293.75%
File 1: TryGetStateContainerPrefix(...)100%22100%
File 1: TryGetStateGroupPrefix(...)100%11100%
File 1: TryGetScenePrefix(...)100%11100%
File 1: TryGetAttributeStringPrefix(...)100%88100%
File 1: GetStateSegment(...)100%88100%
File 1: IsUsableStateProperty(...)100%44100%
File 1: IsPartialStateProperty(...)100%11100%
File 1: ToCamelCase(...)50%22100%
File 1: BuildParameters(...)100%22100%
File 1: GetParameterKind(...)100%1212100%
File 1: BuildErrorParameters(...)100%22100%
File 1: GetErrorParameterKind(...)100%1010100%
File 1: IsExceptionType(...)50%22100%
File 1: IsTelegramContextType(...)50%22100%
File 1: IsAssignableFrom(...)50%7666.66%
File 1: IsErrorRouteValueParameterType(...)100%11100%
File 1: IsRouteValueParameterType(...)85%2020100%
File 2: GenerateSource(...)100%3232100%
File 2: AppendCallbackDataCodecRegistration(...)100%22100%
File 2: GenerateStateGroupsSource(...)90%101096%
File 2: AppendRegistration(...)100%2020100%
File 2: AppendAutoAnswerCallback(...)100%22100%
File 2: AppendFilter(...)100%88100%
File 2: AppendErrorRegistration(...)100%22100%
File 2: AppendCallbackDataCodec(...)70%101084.21%
File 2: GetCallbackDataPackExpression(...)83.33%6690%
File 2: GetCallbackDataUnpackExpression(...)66.66%6680%
File 2: AppendCallbackDataHelpers(...)100%11100%
File 2: AppendInvoker(...)100%44100%
File 2: AppendErrorInvoker(...)100%44100%
File 2: AppendErrorInvocationArguments(...)100%44100%
File 2: ToNullableLiteral(...)100%22100%
File 2: ToTypeOfNullable(...)100%22100%
File 2: ToLiteral(...)66.66%151271.42%
File 2: ToBoolLiteral(...)100%22100%
File 2: ToCommandPrefixModeLiteral(...)25%5457.14%
File 2: ToAttributeCreationExpression(...)66.66%121286.36%
File 2: ToAttributeArgumentExpression(...)22.22%25941.66%
File 2: ToPrimitiveLiteral(...)11.76%4703427.77%
File 2: ToCharLiteral(...)0%110100%
File 2: ToEnumLiteral(...)0%156120%
File 2: ToArrayLiteral(...)0%4260%
File 3: GetStateGroup(...)66.66%121296.15%
File 3: GetAccessibility(...)50%5466.66%

File(s)

/_/src/TeleFlow.Generators/TelegramHandlerSourceGenerator.cs

#LineLine coverage
 1using System.Collections.Immutable;
 2using System.Text;
 3using System.Text.RegularExpressions;
 4using Microsoft.CodeAnalysis;
 5using Microsoft.CodeAnalysis.CSharp.Syntax;
 6using Microsoft.CodeAnalysis.Text;
 7
 8namespace TeleFlow.Generators;
 9
 10[Generator]
 11public sealed partial class TelegramHandlerSourceGenerator : IIncrementalGenerator
 12{
 113    private static readonly SymbolDisplayFormat FullyQualifiedFormat = SymbolDisplayFormat.FullyQualifiedFormat;
 14
 15    public void Initialize(IncrementalGeneratorInitializationContext context)
 16    {
 2817        IncrementalValueProvider<ImmutableArray<GeneratedHandlerMethod?>> methodHandlers = context.SyntaxProvider
 2818            .CreateSyntaxProvider(
 211719                static (node, _) => node is MethodDeclarationSyntax,
 5220                static (syntaxContext, _) => GetHandlerMethod(syntaxContext))
 5221            .Where(static method => method is not null)
 2822            .Collect();
 23
 2824        IncrementalValueProvider<ImmutableArray<GeneratedHandlerMethod?>> classBasedHandlers = context.SyntaxProvider
 2825            .CreateSyntaxProvider(
 211726                static (node, _) => node is TypeDeclarationSyntax,
 5127                static (syntaxContext, _) => GetClassBasedHandlerMethod(syntaxContext))
 5128            .Where(static method => method is not null)
 2829            .Collect();
 30
 2831        IncrementalValueProvider<ImmutableArray<GeneratedErrorHandlerMethod?>> errorHandlers = context.SyntaxProvider
 2832            .CreateSyntaxProvider(
 211733                static (node, _) => node is MethodDeclarationSyntax,
 5234                static (syntaxContext, _) => GetErrorHandlerMethod(syntaxContext))
 5235            .Where(static method => method is not null)
 2836            .Collect();
 37
 2838        IncrementalValueProvider<ImmutableArray<GeneratedStateGroup?>> stateGroups = context.SyntaxProvider
 2839            .CreateSyntaxProvider(
 211740                static (node, _) => node is TypeDeclarationSyntax { AttributeLists.Count: > 0 },
 1941                static (syntaxContext, _) => GetStateGroup(syntaxContext))
 1942            .Where(static group => group is not null)
 2843            .Collect();
 44
 2845        IncrementalValueProvider<ImmutableArray<GeneratedCallbackDataPayload?>> callbackDataPayloads = context.SyntaxPro
 2846            .CreateSyntaxProvider(
 211747                static (node, _) => node is TypeDeclarationSyntax { AttributeLists.Count: > 0 },
 1948                static (syntaxContext, _) => GetCallbackDataPayload(syntaxContext))
 1949            .Where(static payload => payload is not null)
 2850            .Collect();
 51
 2852        context.RegisterSourceOutput(
 2853            methodHandlers.Combine(classBasedHandlers).Combine(errorHandlers).Combine(callbackDataPayloads),
 2854            static (sourceContext, collectedHandlers) =>
 2855        {
 2856            GeneratedHandlerMethod[] handlers = collectedHandlers.Left.Left.Left
 2857                .Concat(collectedHandlers.Left.Left.Right)
 3958                .Where(static handler => handler is not null)
 2859                .Cast<GeneratedHandlerMethod>()
 3960                .GroupBy(static handler => handler.SignatureKey, StringComparer.Ordinal)
 3961                .Select(static group => group.First())
 2562                .OrderBy(static handler => handler.HandlerTypeMetadataName, StringComparer.Ordinal)
 2563                .ThenBy(static handler => handler.SourcePath, StringComparer.Ordinal)
 2564                .ThenBy(static handler => handler.SourceSpanStart)
 2865                .ToArray();
 2866            GeneratedErrorHandlerMethod[] errors = collectedHandlers.Left.Right
 367                .Where(static handler => handler is not null)
 2868                .Cast<GeneratedErrorHandlerMethod>()
 369                .GroupBy(static handler => handler.SignatureKey, StringComparer.Ordinal)
 370                .Select(static group => group.First())
 271                .OrderBy(static handler => handler.HandlerTypeMetadataName, StringComparer.Ordinal)
 272                .ThenBy(static handler => handler.SourcePath, StringComparer.Ordinal)
 273                .ThenBy(static handler => handler.SourceSpanStart)
 2874                .ToArray();
 2875            GeneratedCallbackDataPayload[] callbackPayloads = collectedHandlers.Right
 276                .Where(static payload => payload is not null)
 2877                .Cast<GeneratedCallbackDataPayload>()
 278                .GroupBy(static payload => payload.TypeMetadataName, StringComparer.Ordinal)
 279                .Select(static group => group.First())
 080                .OrderBy(static payload => payload.TypeMetadataName, StringComparer.Ordinal)
 081                .ThenBy(static payload => payload.SourcePath, StringComparer.Ordinal)
 082                .ThenBy(static payload => payload.SourceSpanStart)
 2883                .ToArray();
 2884
 2885            if (handlers.Length == 0 && errors.Length == 0 && callbackPayloads.Length == 0)
 2886            {
 487                return;
 2888            }
 2889
 2490            sourceContext.AddSource(
 2491                "TeleFlow.Telegram.GeneratedHandlers.g.cs",
 2492                SourceText.From(GenerateSource(handlers, errors, callbackPayloads), Encoding.UTF8));
 5293        });
 94
 2895        context.RegisterSourceOutput(stateGroups, static (sourceContext, collectedGroups) =>
 2896        {
 2897            GeneratedStateGroup[] groups = collectedGroups
 498                .Where(static group => group is not null)
 2899                .Cast<GeneratedStateGroup>()
 4100                .GroupBy(static group => group.TypeMetadataName, StringComparer.Ordinal)
 4101                .Select(static group => group.First())
 0102                .OrderBy(static group => group.TypeMetadataName, StringComparer.Ordinal)
 28103                .ToArray();
 28104
 28105            if (groups.Length == 0)
 28106            {
 24107                return;
 28108            }
 28109
 4110            sourceContext.AddSource(
 4111                "TeleFlow.StateGroups.g.cs",
 4112                SourceText.From(GenerateStateGroupsSource(groups), Encoding.UTF8));
 32113        });
 28114    }
 115
 116    private static GeneratedHandlerMethod? GetHandlerMethod(GeneratorSyntaxContext context)
 117    {
 52118        if (context.SemanticModel.GetDeclaredSymbol(context.Node) is not IMethodSymbol method ||
 52119            IsClassBasedHandlerType(method.ContainingType) ||
 52120            !TelegramHandlerSymbols.HasAnyRouteAttribute(method))
 121        {
 11122            return null;
 123        }
 124
 41125        if (!TryBuildHandler(method, includeClassRouteAttributes: false, out GeneratedHandlerMethod handler))
 126        {
 6127            return null;
 128        }
 129
 35130        return handler;
 131    }
 132
 133    private static GeneratedCallbackDataPayload? GetCallbackDataPayload(GeneratorSyntaxContext context)
 134    {
 19135        if (context.SemanticModel.GetDeclaredSymbol(context.Node) is not INamedTypeSymbol type)
 136        {
 0137            return null;
 138        }
 139
 19140        AttributeData? attribute = TelegramHandlerSymbols
 19141            .GetAttributes(type, TelegramHandlerSymbols.CallbackDataAttribute, inherit: false)
 19142            .FirstOrDefault();
 143
 19144        if (attribute is null)
 145        {
 17146            return null;
 147        }
 148
 2149        string? prefix = GetConstructorString(attribute);
 150
 2151        if (!TelegramCallbackDataFacts.IsValidPayloadPrefix(prefix) ||
 2152            !IsAccessibleFromGeneratedCode(type) ||
 2153            !TryGetCallbackDataFields(type, out bool usesConstructor, out ImmutableArray<GeneratedCallbackDataField> fie
 154        {
 0155            return null;
 156        }
 157
 2158        return new GeneratedCallbackDataPayload(
 2159            type.ToDisplayString(FullyQualifiedFormat),
 2160            type.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat),
 2161            prefix!,
 2162            usesConstructor,
 2163            fields,
 2164            context.Node.SyntaxTree.FilePath,
 2165            context.Node.SpanStart);
 166    }
 167
 168    private static GeneratedHandlerMethod? GetClassBasedHandlerMethod(GeneratorSyntaxContext context)
 169    {
 51170        if (context.SemanticModel.GetDeclaredSymbol(context.Node) is not INamedTypeSymbol type ||
 51171            !IsClassBasedHandlerType(type))
 172        {
 47173            return null;
 174        }
 175
 4176        ImmutableArray<IMethodSymbol> handleMethods = GetDeclaredHandleAsyncMethods(type);
 177
 4178        if (handleMethods.Length != 1 ||
 4179            (!TelegramHandlerSymbols.HasAnyRouteAttribute(type) &&
 4180             !TelegramHandlerSymbols.HasAnyRouteAttribute(handleMethods[0])))
 181        {
 0182            return null;
 183        }
 184
 4185        if (!TryBuildHandler(handleMethods[0], includeClassRouteAttributes: true, out GeneratedHandlerMethod handler))
 186        {
 0187            return null;
 188        }
 189
 4190        return handler;
 191    }
 192
 193    private static GeneratedErrorHandlerMethod? GetErrorHandlerMethod(GeneratorSyntaxContext context)
 194    {
 52195        if (context.SemanticModel.GetDeclaredSymbol(context.Node) is not IMethodSymbol method ||
 52196            !TelegramHandlerSymbols.HasAnyErrorAttribute(method))
 197        {
 49198            return null;
 199        }
 200
 3201        if (!TryBuildErrorHandler(method, out GeneratedErrorHandlerMethod handler))
 202        {
 0203            return null;
 204        }
 205
 3206        return handler;
 207    }
 208
 209    private static bool TryBuildHandler(
 210        IMethodSymbol method,
 211        bool includeClassRouteAttributes,
 212        out GeneratedHandlerMethod handler)
 213    {
 45214        handler = null!;
 215
 45216        ImmutableArray<GeneratedRoute> routes = BuildRoutes(method, includeClassRouteAttributes, out ITypeSymbol? callba
 217
 45218        if (routes.Length == 0 ||
 45219            method.DeclaredAccessibility != Accessibility.Public ||
 45220            method.IsStatic ||
 45221            method.IsGenericMethod ||
 45222            method.ContainingType.TypeKind is TypeKind.Interface ||
 45223            method.ContainingType.IsAbstract ||
 45224            !IsClassBasedRouteCompatible(method.ContainingType, routes[0].Kind, callbackPayloadType) ||
 45225            !IsSupportedReturnType(method.ReturnType) ||
 45226            !TryGetExpectedContextType(routes[0].Kind, out string expectedContextType) ||
 45227            !HasExactlyOneExpectedContext(method, expectedContextType) ||
 45228            !HasValidCallbackPayloadParameter(method, callbackPayloadType) ||
 45229            !TryResolveRouteValueNames(method, routes, out ImmutableHashSet<string> routeValueNames))
 230        {
 5231            return false;
 232        }
 233
 40234        if (!TryBuildStates(method, out ImmutableArray<string> states, out string? sceneName))
 235        {
 1236            return false;
 237        }
 238
 39239        MethodDeclarationSyntax? syntax = method.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() as MethodDeclar
 78240        Location? location = method.Locations.FirstOrDefault(location => location.IsInSource);
 39241        ImmutableArray<GeneratedParameter> parameters = BuildParameters(method, routes[0].Kind, expectedContextType, cal
 80242        bool hasCallbackRoute = routes.Any(static route => route.Kind == GeneratedHandlerKind.Callback);
 243
 39244        if (parameters.Length == 0)
 245        {
 0246            return false;
 247        }
 248
 39249        if (!TryBuildAutoAnswerCallback(method, hasCallbackRoute, out GeneratedAutoAnswerCallback? autoAnswerCallback))
 250        {
 0251            return false;
 252        }
 253
 39254        handler = new GeneratedHandlerMethod(
 39255            SignatureKey: method.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat),
 39256            HandlerTypeName: method.ContainingType.ToDisplayString(FullyQualifiedFormat),
 39257            HandlerTypeMetadataName: method.ContainingType.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat)
 39258            MethodName: method.Name,
 39259            ModuleName: GetModuleName(method.ContainingType),
 39260            SceneName: sceneName,
 39261            CallbackPayloadType: callbackPayloadType?.ToDisplayString(FullyQualifiedFormat),
 39262            AutoAnswerCallback: autoAnswerCallback,
 39263            Routes: routes,
 39264            States: states,
 39265            Parameters: parameters,
 39266            SourcePath: location?.SourceTree?.FilePath ?? string.Empty,
 39267            SourceSpanStart: location?.SourceSpan.Start ?? syntax?.SpanStart ?? 0);
 268
 39269        return true;
 270    }
 271
 272    private static bool TryBuildErrorHandler(
 273        IMethodSymbol method,
 274        out GeneratedErrorHandlerMethod handler)
 275    {
 3276        handler = null!;
 277
 3278        IReadOnlyList<AttributeData> errorAttributes = TelegramHandlerSymbols.GetErrorAttributes(method, inherit: true);
 279
 3280        if (errorAttributes.Count == 0 ||
 3281            method.DeclaredAccessibility != Accessibility.Public ||
 3282            method.IsStatic ||
 3283            method.IsGenericMethod ||
 3284            method.ContainingType.TypeKind is TypeKind.Interface ||
 3285            method.ContainingType.IsAbstract ||
 3286            !TryGetErrorReturnKind(method.ReturnType, out GeneratedErrorReturnKind returnKind) ||
 3287            !TryBuildErrorExceptionTypes(errorAttributes, out ImmutableArray<ITypeSymbol?> exceptionTypes, out Immutable
 3288            !TryBuildErrorParameters(method, exceptionTypes, out ImmutableArray<GeneratedErrorParameter> parameters, out
 289        {
 0290            return false;
 291        }
 292
 3293        MethodDeclarationSyntax? syntax = method.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() as MethodDeclar
 6294        Location? location = method.Locations.FirstOrDefault(static location => location.IsInSource);
 295
 3296        handler = new GeneratedErrorHandlerMethod(
 3297            SignatureKey: method.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat),
 3298            HandlerTypeName: method.ContainingType.ToDisplayString(FullyQualifiedFormat),
 3299            HandlerTypeMetadataName: method.ContainingType.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat)
 3300            MethodName: method.Name,
 3301            ModuleName: GetModuleName(method.ContainingType),
 3302            ExceptionTypes: exceptionTypeNames,
 3303            TelegramContextType: telegramContextType,
 3304            ReturnKind: returnKind,
 3305            Parameters: parameters,
 3306            SourcePath: location?.SourceTree?.FilePath ?? string.Empty,
 3307            SourceSpanStart: location?.SourceSpan.Start ?? syntax?.SpanStart ?? 0);
 308
 3309        return true;
 310    }
 311
 312    private static bool TryBuildErrorExceptionTypes(
 313        IReadOnlyList<AttributeData> attributes,
 314        out ImmutableArray<ITypeSymbol?> exceptionTypes,
 315        out ImmutableArray<string?> exceptionTypeNames)
 316    {
 3317        ImmutableArray<ITypeSymbol?>.Builder symbols = ImmutableArray.CreateBuilder<ITypeSymbol?>();
 3318        ImmutableArray<string?>.Builder names = ImmutableArray.CreateBuilder<string?>();
 319
 12320        foreach (AttributeData attribute in attributes)
 321        {
 3322            if (TelegramHandlerSymbols.IsGenericAttribute(attribute, TelegramHandlerSymbols.GenericErrorAttribute))
 323            {
 2324                if (attribute.AttributeClass is not { TypeArguments.Length: 1 } genericAttribute ||
 2325                    !IsExceptionType(genericAttribute.TypeArguments[0]))
 326                {
 0327                    exceptionTypes = [];
 0328                    exceptionTypeNames = [];
 0329                    return false;
 330                }
 331
 2332                symbols.Add(genericAttribute.TypeArguments[0]);
 2333                names.Add(genericAttribute.TypeArguments[0].ToDisplayString(FullyQualifiedFormat));
 2334                continue;
 335            }
 336
 1337            symbols.Add(null);
 1338            names.Add(null);
 339        }
 340
 3341        exceptionTypes = symbols.ToImmutable();
 3342        exceptionTypeNames = names.ToImmutable();
 3343        return exceptionTypes.Length > 0;
 0344    }
 345
 346    private static bool TryBuildErrorParameters(
 347        IMethodSymbol method,
 348        ImmutableArray<ITypeSymbol?> exceptionTypes,
 349        out ImmutableArray<GeneratedErrorParameter> parameters,
 350        out string? telegramContextType)
 351    {
 3352        parameters = [];
 3353        telegramContextType = null;
 354
 3355        if (method.Parameters.Count(static parameter =>
 11356                TelegramHandlerSymbols.IsType(parameter.Type, TelegramHandlerSymbols.TelegramErrorContext)) > 1 ||
 3357            method.Parameters.Count(static parameter =>
 14358                TelegramHandlerSymbols.IsType(parameter.Type, TelegramHandlerSymbols.CancellationToken)) > 1)
 359        {
 0360            return false;
 361        }
 362
 3363        IParameterSymbol[] exceptionParameters = method.Parameters
 11364            .Where(static parameter => IsExceptionType(parameter.Type))
 3365            .ToArray();
 366
 3367        if (exceptionParameters.Length > 1)
 368        {
 0369            return false;
 370        }
 371
 3372        IParameterSymbol[] telegramContextParameters = method.Parameters
 11373            .Where(static parameter => IsTelegramContextType(parameter.Type))
 3374            .ToArray();
 375
 3376        if (telegramContextParameters.Length > 1)
 377        {
 0378            return false;
 379        }
 380
 3381        IParameterSymbol? exceptionParameter = exceptionParameters.FirstOrDefault();
 382
 3383        if (exceptionParameter is not null)
 384        {
 12385            foreach (ITypeSymbol? exceptionType in exceptionTypes)
 386            {
 3387                if (exceptionType is null)
 388                {
 1389                    if (!TelegramHandlerSymbols.IsType(exceptionParameter.Type, TelegramHandlerSymbols.Exception))
 390                    {
 0391                        return false;
 392                    }
 393
 394                    continue;
 395                }
 396
 2397                if (!IsAssignableFrom(exceptionParameter.Type, exceptionType))
 398                {
 0399                    return false;
 400                }
 401            }
 402        }
 403
 3404        telegramContextType = telegramContextParameters.FirstOrDefault()?.Type.ToDisplayString(FullyQualifiedFormat);
 3405        parameters = BuildErrorParameters(method);
 3406        return true;
 407    }
 408
 409    private static bool TryBuildAutoAnswerCallback(
 410        IMethodSymbol method,
 411        bool hasCallbackRoute,
 412        out GeneratedAutoAnswerCallback? autoAnswerCallback)
 413    {
 39414        autoAnswerCallback = null;
 415
 39416        AttributeData? methodAttribute = TelegramHandlerSymbols.GetFirstAttribute(
 39417            method,
 39418            TelegramHandlerSymbols.AutoAnswerCallbackAttribute,
 39419            inherit: true);
 420
 39421        if (methodAttribute is not null && !hasCallbackRoute)
 422        {
 0423            return false;
 424        }
 425
 39426        if (!hasCallbackRoute)
 427        {
 33428            return true;
 429        }
 430
 6431        AttributeData? attribute = methodAttribute ??
 6432            TelegramHandlerSymbols.GetFirstAttribute(
 6433                method.ContainingType,
 6434                TelegramHandlerSymbols.AutoAnswerCallbackAttribute,
 6435                inherit: true);
 436
 6437        if (attribute is null)
 438        {
 5439            return true;
 440        }
 441
 1442        string? text = GetConstructorString(attribute);
 1443        if (text is not null && string.IsNullOrWhiteSpace(text))
 444        {
 0445            return false;
 446        }
 447
 1448        autoAnswerCallback = new GeneratedAutoAnswerCallback(
 1449            GetNamedBool(attribute, "Enabled", defaultValue: true),
 1450            text,
 1451            GetNamedBool(attribute, "ShowAlert", defaultValue: false));
 1452        return true;
 453    }
 454
 455    private static ImmutableArray<GeneratedRoute> BuildRoutes(
 456        IMethodSymbol method,
 457        bool includeClassRouteAttributes,
 458        out ITypeSymbol? callbackPayloadType)
 459    {
 45460        callbackPayloadType = null;
 45461        bool hasRawCallback = GetRouteAttributes(
 45462            method,
 45463            TelegramHandlerSymbols.CallbackAttribute,
 45464            includeClassRouteAttributes)
 45465            .Count > 0;
 45466        IReadOnlyList<AttributeData> genericCallbackAttributes = GetGenericRouteAttributes(
 45467            method,
 45468            TelegramHandlerSymbols.GenericCallbackAttribute,
 45469            includeClassRouteAttributes);
 45470        AttributeData? genericCallback = genericCallbackAttributes.Count > 0
 45471            ? genericCallbackAttributes[0]
 45472            : null;
 473
 45474        if (genericCallback?.AttributeClass is { TypeArguments.Length: 1 } genericCallbackType)
 475        {
 3476            callbackPayloadType = genericCallbackType.TypeArguments[0];
 477        }
 478
 45479        int messageRouteAttributeCount =
 45480            GetRouteAttributes(method, TelegramHandlerSymbols.CommandAttribute, includeClassRouteAttributes).Count +
 45481            GetRouteAttributes(method, TelegramHandlerSymbols.MessageAttribute, includeClassRouteAttributes).Count +
 45482            GetRouteAttributes(method, TelegramHandlerSymbols.TextAttribute, includeClassRouteAttributes).Count +
 45483            GetRouteAttributes(method, TelegramHandlerSymbols.TextTemplateAttribute, includeClassRouteAttributes).Count 
 45484            GetRouteAttributes(method, TelegramHandlerSymbols.CommandTemplateAttribute, includeClassRouteAttributes).Cou
 45485            GetRouteAttributes(method, TelegramHandlerSymbols.TextRegexAttribute, includeClassRouteAttributes).Count +
 45486            GetRouteAttributes(method, TelegramHandlerSymbols.CommandRegexAttribute, includeClassRouteAttributes).Count;
 45487        bool hasChatMemberUpdated = GetRouteAttributes(
 45488            method,
 45489            TelegramHandlerSymbols.ChatMemberUpdatedAttribute,
 45490            includeClassRouteAttributes).Count > 0;
 45491        bool hasMyChatMemberUpdated = GetRouteAttributes(
 45492            method,
 45493            TelegramHandlerSymbols.MyChatMemberUpdatedAttribute,
 45494            includeClassRouteAttributes).Count > 0;
 45495        bool hasChatMemberRoute = hasChatMemberUpdated || hasMyChatMemberUpdated;
 496
 45497        if ((hasRawCallback || callbackPayloadType is not null) &&
 45498            (messageRouteAttributeCount > 0 || hasChatMemberRoute || (hasRawCallback && callbackPayloadType is not null)
 499        {
 0500            return [];
 501        }
 502
 45503        if (hasChatMemberRoute && messageRouteAttributeCount > 0)
 504        {
 0505            return [];
 506        }
 507
 45508        if (!hasChatMemberRoute && HasChatMemberTransitionAttributes(method))
 509        {
 0510            return [];
 511        }
 512
 45513        if (!TryBuildFilters(method, out ImmutableArray<GeneratedFilter> filters))
 514        {
 0515            return [];
 516        }
 517
 45518        if (!TryBuildRoleRequirements(method, out ImmutableArray<int> roleRequirements))
 519        {
 0520            return [];
 521        }
 522
 45523        if (hasChatMemberRoute)
 524        {
 7525            if (!TryPrepareCustomFilters(filters, GeneratedHandlerKind.ChatMember, out ImmutableArray<GeneratedFilter> c
 0526                chatMemberFilters.Any(static filter => filter.CustomTypeName is null &&
 0527                                             !SupportsGeneratedFilter(filter, GeneratedHandlerKind.ChatMember)) ||
 7528                !TryBuildChatMemberTransitions(method, out ImmutableArray<GeneratedChatMemberTransition> transitions))
 529            {
 0530                return [];
 531            }
 532
 7533            filters = chatMemberFilters;
 7534            ImmutableArray<GeneratedRoute>.Builder chatMemberRoutes = ImmutableArray.CreateBuilder<GeneratedRoute>();
 535
 7536            if (hasChatMemberUpdated)
 537            {
 6538                chatMemberRoutes.Add(new GeneratedRoute(
 6539                    GeneratedHandlerKind.ChatMember,
 6540                    GeneratedRouteKind.ChatMemberUpdated,
 6541                    Command: null,
 6542                    Pattern: null,
 6543                    CommandPrefixes: ["/"],
 6544                    AllowSpaceAfterPrefix: false,
 6545                    PrefixMode: CommandPrefixModeRequired,
 6546                    IgnoreCase: true,
 6547                    TextFilters: [],
 6548                    filters,
 6549                    transitions,
 6550                    roleRequirements,
 6551                    RouteValues: ImmutableDictionary<string, GeneratedRouteValue>.Empty));
 552            }
 553
 7554            if (hasMyChatMemberUpdated)
 555            {
 1556                chatMemberRoutes.Add(new GeneratedRoute(
 1557                    GeneratedHandlerKind.ChatMember,
 1558                    GeneratedRouteKind.MyChatMemberUpdated,
 1559                    Command: null,
 1560                    Pattern: null,
 1561                    CommandPrefixes: ["/"],
 1562                    AllowSpaceAfterPrefix: false,
 1563                    PrefixMode: CommandPrefixModeRequired,
 1564                    IgnoreCase: true,
 1565                    TextFilters: [],
 1566                    filters,
 1567                    transitions,
 1568                    roleRequirements,
 1569                    RouteValues: ImmutableDictionary<string, GeneratedRouteValue>.Empty));
 570            }
 571
 7572            return chatMemberRoutes.ToImmutable();
 573        }
 574
 38575        if (hasRawCallback || callbackPayloadType is not null)
 576        {
 6577            if (!TryPrepareCustomFilters(filters, GeneratedHandlerKind.Callback, out ImmutableArray<GeneratedFilter> cal
 10578                callbackFilters.Any(static filter => filter.CustomTypeName is null &&
 10579                                             !SupportsGeneratedFilter(filter, GeneratedHandlerKind.Callback)))
 580            {
 0581                return [];
 582            }
 583
 6584            filters = callbackFilters;
 6585            return
 6586            [
 6587                new GeneratedRoute(
 6588                    GeneratedHandlerKind.Callback,
 6589                    GeneratedRouteKind.Callback,
 6590                    Command: null,
 6591                    Pattern: null,
 6592                    CommandPrefixes: ["/"],
 6593                    AllowSpaceAfterPrefix: false,
 6594                    PrefixMode: CommandPrefixModeRequired,
 6595                    IgnoreCase: true,
 6596                    TextFilters: [],
 6597                    filters,
 6598                    ChatMemberTransitions: [],
 6599                    RoleRequirements: roleRequirements,
 6600                    RouteValues: ImmutableDictionary<string, GeneratedRouteValue>.Empty)
 6601            ];
 602        }
 603
 32604        if (!TryPrepareCustomFilters(filters, GeneratedHandlerKind.Message, out ImmutableArray<GeneratedFilter> messageF
 54605            messageFilters.Any(static filter => filter.CustomTypeName is null &&
 54606                                         !SupportsGeneratedFilter(filter, GeneratedHandlerKind.Message)))
 607        {
 0608            return [];
 609        }
 610
 32611        filters = messageFilters;
 32612        ImmutableArray<GeneratedRoute>.Builder routes = ImmutableArray.CreateBuilder<GeneratedRoute>();
 613
 87614        foreach (AttributeData attribute in GetRouteAttributes(
 32615                     method,
 32616                     TelegramHandlerSymbols.CommandAttribute,
 32617                     includeClassRouteAttributes))
 618        {
 12619            string? command = GetConstructorString(attribute);
 12620            int prefixMode = GetCommandPrefixMode(attribute);
 621
 12622            if (!IsValidCommand(command) ||
 12623                !TryGetPrefixes(attribute, out ImmutableArray<string> prefixes) ||
 12624                !IsSupportedCommandPrefixMode(prefixMode) ||
 12625                HasInvalidNoPrefixConfiguration(attribute, prefixMode))
 626            {
 1627                return [];
 628            }
 629
 11630            routes.Add(new GeneratedRoute(
 11631                GeneratedHandlerKind.Command,
 11632                GeneratedRouteKind.CommandExact,
 11633                command,
 11634                command,
 11635                prefixes,
 11636                GetNamedBool(attribute, "AllowSpaceAfterPrefix", defaultValue: false),
 11637                prefixMode,
 11638                GetNamedBool(attribute, "IgnoreCase", defaultValue: true),
 11639                TextFilters: [],
 11640                filters,
 11641                ChatMemberTransitions: [],
 11642                RoleRequirements: roleRequirements,
 11643                RouteValues: ImmutableDictionary<string, GeneratedRouteValue>.Empty));
 644        }
 645
 66646        foreach (AttributeData attribute in GetRouteAttributes(
 31647                     method,
 31648                     TelegramHandlerSymbols.CommandTemplateAttribute,
 31649                     includeClassRouteAttributes))
 650        {
 3651            string? template = GetConstructorString(attribute);
 3652            int prefixMode = GetCommandPrefixMode(attribute);
 653
 3654            if (template is null ||
 3655                string.IsNullOrWhiteSpace(template) ||
 3656                !TryParseTemplateRouteValues(template, out ImmutableDictionary<string, GeneratedRouteValue> routeValues)
 3657                !TryGetPrefixes(attribute, out ImmutableArray<string> prefixes) ||
 3658                !IsSupportedCommandPrefixMode(prefixMode) ||
 3659                HasInvalidNoPrefixConfiguration(attribute, prefixMode) ||
 3660                CommandPatternStartsWithPrefix(template, prefixes))
 661            {
 2662                return [];
 663            }
 664
 1665            routes.Add(new GeneratedRoute(
 1666                GeneratedHandlerKind.Command,
 1667                GeneratedRouteKind.CommandTemplate,
 1668                Command: null,
 1669                Pattern: template.Trim(),
 1670                prefixes,
 1671                GetNamedBool(attribute, "AllowSpaceAfterPrefix", defaultValue: false),
 1672                prefixMode,
 1673                GetNamedBool(attribute, "IgnoreCase", defaultValue: true),
 1674                TextFilters: [],
 1675                filters,
 1676                ChatMemberTransitions: [],
 1677                RoleRequirements: roleRequirements,
 1678                routeValues));
 679        }
 680
 60681        foreach (AttributeData attribute in GetRouteAttributes(
 29682                     method,
 29683                     TelegramHandlerSymbols.CommandRegexAttribute,
 29684                     includeClassRouteAttributes))
 685        {
 1686            string? pattern = GetConstructorString(attribute);
 1687            int prefixMode = GetCommandPrefixMode(attribute);
 688
 1689            if (string.IsNullOrWhiteSpace(pattern) ||
 1690                !TryParseRegexRouteValues(pattern, out ImmutableDictionary<string, GeneratedRouteValue> routeValues) ||
 1691                !TryGetPrefixes(attribute, out ImmutableArray<string> prefixes) ||
 1692                !IsSupportedCommandPrefixMode(prefixMode) ||
 1693                HasInvalidNoPrefixConfiguration(attribute, prefixMode))
 694            {
 0695                return [];
 696            }
 697
 1698            routes.Add(new GeneratedRoute(
 1699                GeneratedHandlerKind.Command,
 1700                GeneratedRouteKind.CommandRegex,
 1701                Command: null,
 1702                Pattern: pattern,
 1703                prefixes,
 1704                GetNamedBool(attribute, "AllowSpaceAfterPrefix", defaultValue: false),
 1705                prefixMode,
 1706                GetNamedBool(attribute, "IgnoreCase", defaultValue: true),
 1707                TextFilters: [],
 1708                filters,
 1709                ChatMemberTransitions: [],
 1710                RoleRequirements: roleRequirements,
 1711                routeValues));
 712        }
 713
 29714        ImmutableArray<GeneratedTextFilter> textFilters = BuildTextFilters(method, includeClassRouteAttributes);
 29715        bool hasMessageAttribute = GetRouteAttributes(
 29716            method,
 29717            TelegramHandlerSymbols.MessageAttribute,
 29718            includeClassRouteAttributes).Count > 0;
 719
 29720        if (hasMessageAttribute)
 721        {
 13722            routes.Add(new GeneratedRoute(
 13723                GeneratedHandlerKind.Message,
 13724                GeneratedRouteKind.MessageAny,
 13725                Command: null,
 13726                Pattern: null,
 13727                CommandPrefixes: ["/"],
 13728                AllowSpaceAfterPrefix: false,
 13729                PrefixMode: CommandPrefixModeRequired,
 13730                IgnoreCase: true,
 13731                textFilters,
 13732                filters,
 13733                ChatMemberTransitions: [],
 13734                RoleRequirements: roleRequirements,
 13735                RouteValues: ImmutableDictionary<string, GeneratedRouteValue>.Empty));
 736        }
 737        else
 738        {
 34739            foreach (GeneratedTextFilter filter in textFilters)
 740            {
 1741                routes.Add(new GeneratedRoute(
 1742                    GeneratedHandlerKind.Message,
 1743                    GeneratedRouteKind.TextExact,
 1744                    Command: null,
 1745                    Pattern: filter.Value,
 1746                    CommandPrefixes: ["/"],
 1747                    AllowSpaceAfterPrefix: false,
 1748                    PrefixMode: CommandPrefixModeRequired,
 1749                    IgnoreCase: filter.IgnoreCase,
 1750                    TextFilters: [filter],
 1751                    filters,
 1752                    ChatMemberTransitions: [],
 1753                    RoleRequirements: roleRequirements,
 1754                    RouteValues: ImmutableDictionary<string, GeneratedRouteValue>.Empty));
 755            }
 756        }
 757
 65758        foreach (AttributeData attribute in GetRouteAttributes(
 29759                     method,
 29760                     TelegramHandlerSymbols.TextTemplateAttribute,
 29761                     includeClassRouteAttributes))
 762        {
 4763            string? template = GetConstructorString(attribute);
 764
 4765            if (template is null ||
 4766                string.IsNullOrWhiteSpace(template) ||
 4767                !TryParseTemplateRouteValues(template, out ImmutableDictionary<string, GeneratedRouteValue> routeValues)
 768            {
 1769                return [];
 770            }
 771
 3772            routes.Add(new GeneratedRoute(
 3773                GeneratedHandlerKind.Message,
 3774                GeneratedRouteKind.TextTemplate,
 3775                Command: null,
 3776                Pattern: template.Trim(),
 3777                    CommandPrefixes: ["/"],
 3778                    AllowSpaceAfterPrefix: false,
 3779                    PrefixMode: CommandPrefixModeRequired,
 3780                    IgnoreCase: GetNamedBool(attribute, "IgnoreCase", defaultValue: true),
 3781                    TextFilters: [],
 3782                filters,
 3783                ChatMemberTransitions: [],
 3784                RoleRequirements: roleRequirements,
 3785                routeValues));
 786        }
 787
 56788        foreach (AttributeData attribute in GetRouteAttributes(
 28789                     method,
 28790                     TelegramHandlerSymbols.TextRegexAttribute,
 28791                     includeClassRouteAttributes))
 792        {
 0793            string? pattern = GetConstructorString(attribute);
 794
 0795            if (string.IsNullOrWhiteSpace(pattern) ||
 0796                !TryParseRegexRouteValues(pattern, out ImmutableDictionary<string, GeneratedRouteValue> routeValues))
 797            {
 0798                return [];
 799            }
 800
 0801            routes.Add(new GeneratedRoute(
 0802                GeneratedHandlerKind.Message,
 0803                GeneratedRouteKind.TextRegex,
 0804                Command: null,
 0805                Pattern: pattern,
 0806                    CommandPrefixes: ["/"],
 0807                    AllowSpaceAfterPrefix: false,
 0808                    PrefixMode: CommandPrefixModeRequired,
 0809                    IgnoreCase: GetNamedBool(attribute, "IgnoreCase", defaultValue: true),
 0810                    TextFilters: [],
 0811                filters,
 0812                ChatMemberTransitions: [],
 0813                RoleRequirements: roleRequirements,
 0814                routeValues));
 815        }
 816
 28817        return routes.ToImmutable();
 4818    }
 819
 820    private static bool TryBuildFilters(
 821        IMethodSymbol method,
 822        out ImmutableArray<GeneratedFilter> filters)
 823    {
 45824        ImmutableArray<GeneratedFilter>.Builder builder = ImmutableArray.CreateBuilder<GeneratedFilter>();
 825
 45826        if (!AppendFilters(builder, method.ContainingType) ||
 45827            !AppendFilters(builder, method))
 828        {
 0829            filters = [];
 0830            return false;
 831        }
 832
 45833        filters = builder.ToImmutable();
 45834        return true;
 835    }
 836
 837    private static bool AppendFilters(
 838        ImmutableArray<GeneratedFilter>.Builder builder,
 839        ISymbol symbol)
 840    {
 90841        if (!AppendChatTypeFilters(
 90842                builder,
 90843                symbol,
 90844                TelegramHandlerSymbols.ChatTypeAttribute,
 90845                "ChatType") ||
 90846            !AppendChatTypeFilters(
 90847                builder,
 90848                symbol,
 90849                TelegramHandlerSymbols.SenderChatTypeAttribute,
 90850                "SenderChatType"))
 851        {
 0852            return false;
 853        }
 854
 184855        foreach (AttributeData attribute in TelegramHandlerSymbols.GetAttributes(
 90856                     symbol,
 90857                     TelegramHandlerSymbols.ChatIdAttribute,
 90858                     inherit: true))
 859        {
 2860            ImmutableArray<long> values = attribute.ConstructorArguments.Length > 0
 2861                ? attribute.ConstructorArguments[0].Values
 2862                    .Select(static value => value.Value)
 2863                    .OfType<long>()
 2864                    .ToImmutableArray()
 2865                : [];
 866
 2867            if (values.Length == 0 ||
 4868                values.Any(static value => value == 0))
 869            {
 0870                return false;
 871            }
 872
 2873            builder.Add(new GeneratedFilter("ChatId", StringValues: [], values));
 874        }
 875
 182876        foreach (AttributeData attribute in TelegramHandlerSymbols.GetAttributes(
 90877                     symbol,
 90878                     TelegramHandlerSymbols.ChatUsernameAttribute,
 90879                     inherit: true))
 880        {
 1881            ImmutableArray<string> values = attribute.ConstructorArguments.Length > 0
 1882                ? attribute.ConstructorArguments[0].Values
 1883                    .Select(static value => value.Value as string)
 1884                    .Select(CanonicalizeChatUsername)
 1885                    .ToImmutableArray()
 1886                : [];
 887
 1888            if (values.Length == 0 ||
 2889                values.Any(static value => value.Length == 0))
 890            {
 0891                return false;
 892            }
 893
 1894            builder.Add(new GeneratedFilter("ChatUsername", values, LongValues: []));
 895        }
 896
 90897        if (!AppendSenderIdFilters(
 90898                builder,
 90899                symbol,
 90900                TelegramHandlerSymbols.FromUserAttribute,
 90901                "FromUser") ||
 90902            !AppendSenderIdFilters(
 90903                builder,
 90904                symbol,
 90905                TelegramHandlerSymbols.FromBotAttribute,
 90906                "FromBot"))
 907        {
 0908            return false;
 909        }
 910
 3780911        foreach (TelegramBuiltInFilterSpec spec in TelegramBuiltInFilterFacts.MarkerSpecs)
 912        {
 1800913            AppendMarkerFilter(builder, symbol, spec);
 914        }
 915
 182916        foreach (AttributeData attribute in TelegramHandlerSymbols.GetAttributes(
 90917                     symbol,
 90918                     TelegramHandlerSymbols.MessageThreadIdAttribute,
 90919                     inherit: true))
 920        {
 1921            ImmutableArray<long> values = attribute.ConstructorArguments.Length > 0
 1922                ? attribute.ConstructorArguments[0].Values
 1923                    .Select(static value => value.Value)
 1924                    .OfType<long>()
 1925                    .ToImmutableArray()
 1926                : [];
 927
 1928            if (values.Length == 0 ||
 2929                values.Any(static value => value <= 0))
 930            {
 0931                return false;
 932            }
 933
 1934            builder.Add(new GeneratedFilter("MessageThreadId", StringValues: [], values));
 935        }
 936
 184937        foreach (AttributeData attribute in TelegramHandlerSymbols.GetAttributes(
 90938                     symbol,
 90939                     TelegramHandlerSymbols.CallbackDataPrefixAttribute,
 90940                     inherit: true))
 941        {
 2942            if (GetConstructorString(attribute) is not { } prefix ||
 2943                string.IsNullOrWhiteSpace(prefix))
 944            {
 0945                return false;
 946            }
 947
 2948            builder.Add(new GeneratedFilter("CallbackDataPrefix", [prefix], LongValues: []));
 949        }
 950
 184951        foreach (AttributeData attribute in TelegramHandlerSymbols.GetGenericAttributes(
 90952                     symbol,
 90953                     TelegramHandlerSymbols.GenericUseFilterAttribute,
 90954                     inherit: true))
 955        {
 2956            if (attribute.AttributeClass is not { TypeArguments.Length: 1 } filterAttributeType ||
 2957                IsInvalidCustomFilterType(filterAttributeType.TypeArguments[0]) ||
 2958                !TryGetTelegramFilterContextTypes(
 2959                    filterAttributeType.TypeArguments[0],
 2960                    attributeType: null,
 2961                    out ImmutableArray<string> contextMetadataNames))
 962            {
 0963                return false;
 964            }
 965
 2966            builder.Add(new GeneratedFilter(
 2967                "Custom",
 2968                StringValues: [],
 2969                LongValues: [],
 2970                filterAttributeType.TypeArguments[0].ToDisplayString(FullyQualifiedFormat),
 2971                CustomContextMetadataNames: contextMetadataNames));
 972        }
 973
 182974        foreach (AttributeData attribute in TelegramHandlerSymbols.GetTelegramFilterAttributes(symbol, inherit: true))
 975        {
 1976            if (attribute.AttributeClass is not { IsGenericType: false } attributeType ||
 1977                !TelegramHandlerSymbols.TryGetTelegramFilterAttributeFilterType(attribute, out ITypeSymbol filterType) |
 1978                IsInvalidCustomFilterType(filterType) ||
 1979                !TryGetTelegramFilterContextTypes(
 1980                    filterType,
 1981                    attributeType,
 1982                    out ImmutableArray<string> contextMetadataNames))
 983            {
 0984                return false;
 985            }
 986
 1987            builder.Add(new GeneratedFilter(
 1988                "Custom",
 1989                StringValues: [],
 1990                LongValues: [],
 1991                filterType.ToDisplayString(FullyQualifiedFormat),
 1992                ToAttributeCreationExpression(attribute),
 1993                CustomContextMetadataNames: contextMetadataNames));
 994        }
 995
 90996        return true;
 0997    }
 998
 999    private static bool AppendChatTypeFilters(
 1000        ImmutableArray<GeneratedFilter>.Builder builder,
 1001        ISymbol symbol,
 1002        string attributeMetadataName,
 1003        string generatedKind)
 1004    {
 3681005        foreach (AttributeData attribute in TelegramHandlerSymbols.GetAttributes(
 1801006                     symbol,
 1801007                     attributeMetadataName,
 1801008                     inherit: true))
 1009        {
 41010            if (attribute.ConstructorArguments.Length == 0 ||
 41011                attribute.ConstructorArguments[0].Values.IsDefaultOrEmpty)
 1012            {
 01013                return false;
 1014            }
 1015
 41016            ImmutableArray<string>.Builder values = ImmutableArray.CreateBuilder<string>();
 1017
 221018            foreach (TypedConstant value in attribute.ConstructorArguments[0].Values)
 1019            {
 71020                if (value.Value is not int chatType ||
 71021                    !TelegramChatTypeFacts.TryMapToTelegramValue(chatType, out string mappedValue))
 1022                {
 01023                    return false;
 1024                }
 1025
 71026                values.Add(mappedValue);
 1027            }
 1028
 41029            builder.Add(new GeneratedFilter(generatedKind, values.ToImmutable(), LongValues: []));
 1030        }
 1031
 1801032        return true;
 01033    }
 1034
 1035    private static bool AppendSenderIdFilters(
 1036        ImmutableArray<GeneratedFilter>.Builder builder,
 1037        ISymbol symbol,
 1038        string attributeMetadataName,
 1039        string generatedKind)
 1040    {
 3661041        foreach (AttributeData attribute in TelegramHandlerSymbols.GetAttributes(
 1801042                     symbol,
 1801043                     attributeMetadataName,
 1801044                     inherit: true))
 1045        {
 31046            if (attribute.ConstructorArguments.Length != 1)
 1047            {
 01048                return false;
 1049            }
 1050
 31051            ImmutableArray<TypedConstant> arguments = attribute.ConstructorArguments[0].Values;
 1052
 31053            if (arguments.IsDefault ||
 51054                arguments.Any(static value => value.Value is not long id || id <= 0))
 1055            {
 01056                return false;
 1057            }
 1058
 31059            builder.Add(new GeneratedFilter(
 31060                generatedKind,
 31061                StringValues: [],
 51062                arguments.Select(static value => (long)value.Value!).ToImmutableArray()));
 1063        }
 1064
 1801065        return true;
 01066    }
 1067
 1068    private static void AppendMarkerFilter(
 1069        ImmutableArray<GeneratedFilter>.Builder builder,
 1070        ISymbol symbol,
 1071        TelegramBuiltInFilterSpec spec)
 1072    {
 18001073        if (TelegramHandlerSymbols.HasAttribute(symbol, spec.AttributeMetadataName, inherit: true))
 1074        {
 101075            builder.Add(new GeneratedFilter(spec.GeneratedKind, StringValues: [], LongValues: []));
 1076        }
 18001077    }
 1078
 1079    private static bool SupportsGeneratedFilter(
 1080        GeneratedFilter filter,
 1081        GeneratedHandlerKind handlerKind)
 1082    {
 231083        return TelegramBuiltInFilterFacts.TryGetSpecByGeneratedKind(filter.Kind, out TelegramBuiltInFilterSpec spec) &&
 231084               TelegramBuiltInFilterFacts.SupportsRouteKind(spec.Target, ToMetadataRouteKind(handlerKind));
 1085    }
 1086
 1087    private static bool TryPrepareCustomFilters(
 1088        ImmutableArray<GeneratedFilter> filters,
 1089        GeneratedHandlerKind handlerKind,
 1090        out ImmutableArray<GeneratedFilter> preparedFilters)
 1091    {
 451092        ImmutableArray<GeneratedFilter>.Builder builder = ImmutableArray.CreateBuilder<GeneratedFilter>(filters.Length);
 1093
 1421094        foreach (GeneratedFilter filter in filters)
 1095        {
 261096            if (filter.CustomTypeName is null)
 1097            {
 231098                builder.Add(filter);
 231099                continue;
 1100            }
 1101
 31102            if (!TryResolveCustomFilterContextType(filter, handlerKind, out string contextTypeName))
 1103            {
 01104                preparedFilters = [];
 01105                return false;
 1106            }
 1107
 31108            builder.Add(filter with { CustomContextTypeName = contextTypeName });
 1109        }
 1110
 451111        preparedFilters = builder.ToImmutable();
 451112        return true;
 1113    }
 1114
 1115    private static bool TryResolveCustomFilterContextType(
 1116        GeneratedFilter filter,
 1117        GeneratedHandlerKind handlerKind,
 1118        out string contextTypeName)
 1119    {
 31120        string expectedContext = handlerKind switch
 31121        {
 01122            GeneratedHandlerKind.Callback => TelegramHandlerSymbols.CallbackQueryContext,
 01123            GeneratedHandlerKind.ChatMember => TelegramHandlerSymbols.ChatMemberUpdatedContext,
 31124            _ => TelegramHandlerSymbols.MessageContext
 31125        };
 1126
 31127        if (filter.CustomContextMetadataNames.Contains(expectedContext, StringComparer.Ordinal))
 1128        {
 21129            contextTypeName = ToFullyQualifiedTypeName(expectedContext);
 21130            return true;
 1131        }
 1132
 11133        if (filter.CustomContextMetadataNames.Contains(TelegramHandlerSymbols.TelegramUpdateContext, StringComparer.Ordi
 1134        {
 11135            contextTypeName = ToFullyQualifiedTypeName(TelegramHandlerSymbols.TelegramUpdateContext);
 11136            return true;
 1137        }
 1138
 01139        contextTypeName = string.Empty;
 01140        return false;
 1141    }
 1142
 1143    private static string ToFullyQualifiedTypeName(string metadataName)
 1144    {
 31145        return $"global::{metadataName}";
 1146    }
 1147
 1148    private static string CanonicalizeChatUsername(string? username)
 1149    {
 11150        if (username is null)
 1151        {
 01152            return string.Empty;
 1153        }
 1154
 11155        string value = username.Trim();
 1156
 11157        if (value.StartsWith("@", StringComparison.Ordinal))
 1158        {
 11159            return value.Substring(1);
 1160        }
 1161
 01162        return value;
 1163    }
 1164
 1165    private static bool HasChatMemberTransitionAttributes(IMethodSymbol method)
 1166    {
 381167        return TelegramHandlerSymbols.HasAttribute(method.ContainingType, TelegramHandlerSymbols.ChatMemberTransitionAtt
 381168               TelegramHandlerSymbols.HasAttribute(method, TelegramHandlerSymbols.ChatMemberTransitionAttribute, inherit
 381169               TelegramHandlerSymbols.HasAttribute(method.ContainingType, TelegramHandlerSymbols.ChatMemberChangedAttrib
 381170               TelegramHandlerSymbols.HasAttribute(method, TelegramHandlerSymbols.ChatMemberChangedAttribute, inherit: t
 1171    }
 1172
 1173    private static bool TryBuildRoleRequirements(
 1174        IMethodSymbol method,
 1175        out ImmutableArray<int> roleRequirements)
 1176    {
 451177        ImmutableArray<int>.Builder builder = ImmutableArray.CreateBuilder<int>();
 1178
 941179        foreach (AttributeData attribute in TelegramHandlerSymbols.GetAttributes(method.ContainingType, TelegramHandlerS
 451180                     .Concat(TelegramHandlerSymbols.GetAttributes(method, TelegramHandlerSymbols.RequireTelegramRoleAttr
 1181        {
 21182            if (!TelegramMemberStatusFacts.TryGetRoleRequirementMask(attribute, out int allowedStatuses) ||
 21183                !TelegramMemberStatusFacts.IsValid(allowedStatuses))
 1184            {
 01185                roleRequirements = [];
 01186                return false;
 1187            }
 1188
 21189            builder.Add(allowedStatuses);
 1190        }
 1191
 451192        roleRequirements = builder.ToImmutable();
 451193        return true;
 01194    }
 1195
 1196    private static bool TryBuildChatMemberTransitions(
 1197        IMethodSymbol method,
 1198        out ImmutableArray<GeneratedChatMemberTransition> transitions)
 1199    {
 71200        ImmutableArray<GeneratedChatMemberTransition>.Builder builder = ImmutableArray.CreateBuilder<GeneratedChatMember
 1201
 261202        foreach (AttributeData attribute in TelegramHandlerSymbols.GetAttributes(method.ContainingType, TelegramHandlerS
 71203                     .Concat(TelegramHandlerSymbols.GetAttributes(method, TelegramHandlerSymbols.ChatMemberTransitionAtt
 1204        {
 61205            if (attribute.ConstructorArguments.Length == 0 ||
 61206                attribute.ConstructorArguments[0].Value is not int transition ||
 61207                !TelegramMemberStatusFacts.TryMapTransition(transition, out int oldStatus, out int newStatus))
 1208            {
 01209                transitions = [];
 01210                return false;
 1211            }
 1212
 61213            builder.Add(new GeneratedChatMemberTransition(oldStatus, newStatus));
 1214        }
 1215
 161216        foreach (AttributeData attribute in TelegramHandlerSymbols.GetAttributes(method.ContainingType, TelegramHandlerS
 71217                     .Concat(TelegramHandlerSymbols.GetAttributes(method, TelegramHandlerSymbols.ChatMemberChangedAttrib
 1218        {
 11219            if (attribute.ConstructorArguments.Length < 2 ||
 11220                attribute.ConstructorArguments[0].Value is not int oldStatus ||
 11221                attribute.ConstructorArguments[1].Value is not int newStatus ||
 11222                !TelegramMemberStatusFacts.IsValid(oldStatus) ||
 11223                !TelegramMemberStatusFacts.IsValid(newStatus))
 1224            {
 01225                transitions = [];
 01226                return false;
 1227            }
 1228
 11229            builder.Add(new GeneratedChatMemberTransition(oldStatus, newStatus));
 1230        }
 1231
 71232        transitions = builder.ToImmutable();
 71233        return true;
 01234    }
 1235
 1236    private static TelegramHandlerMetadataRouteKind ToMetadataRouteKind(GeneratedHandlerKind handlerKind)
 1237    {
 231238        return handlerKind switch
 231239        {
 01240            GeneratedHandlerKind.Command => TelegramHandlerMetadataRouteKind.Command,
 191241            GeneratedHandlerKind.Message => TelegramHandlerMetadataRouteKind.Message,
 41242            GeneratedHandlerKind.Callback => TelegramHandlerMetadataRouteKind.Callback,
 01243            GeneratedHandlerKind.ChatMember => TelegramHandlerMetadataRouteKind.ChatMember,
 01244            _ => TelegramHandlerMetadataRouteKind.Message
 231245        };
 1246    }
 1247
 1248    private static bool IsInvalidCustomFilterType(ITypeSymbol type)
 1249    {
 31250        return IsInvalidConcreteNamedType(type);
 1251    }
 1252
 1253    private static bool TryGetTelegramFilterContextTypes(
 1254        ITypeSymbol type,
 1255        ITypeSymbol? attributeType,
 1256        out ImmutableArray<string> contextTypes)
 1257    {
 31258        ImmutableArray<string>.Builder builder = ImmutableArray.CreateBuilder<string>();
 1259
 121260        foreach (INamedTypeSymbol candidate in type.AllInterfaces)
 1261        {
 31262            if (attributeType is null)
 1263            {
 21264                if (string.Equals(
 21265                        candidate.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat),
 21266                        TelegramHandlerSymbols.GenericTelegramFilter,
 21267                        StringComparison.Ordinal) &&
 21268                    candidate.TypeArguments.Length == 1)
 1269                {
 21270                    builder.Add(candidate.TypeArguments[0].ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat)
 1271                }
 1272
 21273                continue;
 1274            }
 1275
 11276            if (string.Equals(
 11277                    candidate.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat),
 11278                    TelegramHandlerSymbols.GenericParameterizedTelegramFilter,
 11279                    StringComparison.Ordinal) &&
 11280                candidate.TypeArguments.Length == 2 &&
 11281                SymbolEqualityComparer.Default.Equals(candidate.TypeArguments[1], attributeType))
 1282            {
 11283                builder.Add(candidate.TypeArguments[0].ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat));
 1284            }
 1285        }
 1286
 31287        contextTypes = builder.ToImmutable();
 31288        return contextTypes.Length > 0;
 1289    }
 1290
 1291    private static string? GetModuleName(INamedTypeSymbol handlerType)
 1292    {
 421293        AttributeData? attribute = TelegramHandlerSymbols.GetFirstAttribute(
 421294            handlerType,
 421295            TelegramHandlerSymbols.TelegramModuleAttribute);
 1296
 421297        if (attribute?.ConstructorArguments.Length > 0 &&
 421298            attribute.ConstructorArguments[0].Value is string name &&
 421299            !string.IsNullOrWhiteSpace(name))
 1300        {
 51301            return name.Trim();
 1302        }
 1303
 371304        return null;
 1305    }
 1306
 1307    private static bool TryGetExpectedContextType(GeneratedHandlerKind kind, out string contextType)
 1308    {
 401309        contextType = kind switch
 401310        {
 61311            GeneratedHandlerKind.Callback => TelegramHandlerSymbols.CallbackQueryContext,
 71312            GeneratedHandlerKind.ChatMember => TelegramHandlerSymbols.ChatMemberUpdatedContext,
 271313            _ => TelegramHandlerSymbols.MessageContext
 401314        };
 401315        return true;
 1316    }
 1317
 1318    private static bool HasExactlyOneExpectedContext(IMethodSymbol method, string expectedContextType)
 1319    {
 401320        IParameterSymbol[] contextParameters = method.Parameters
 401321            .Where(static parameter =>
 561322                TelegramHandlerSymbols.IsType(parameter.Type, TelegramHandlerSymbols.MessageContext) ||
 561323                TelegramHandlerSymbols.IsType(parameter.Type, TelegramHandlerSymbols.CallbackQueryContext) ||
 561324                TelegramHandlerSymbols.IsType(parameter.Type, TelegramHandlerSymbols.ChatMemberUpdatedContext) ||
 561325                TelegramHandlerSymbols.IsType(parameter.Type, TelegramHandlerSymbols.TelegramUpdateContext))
 401326            .ToArray();
 1327
 401328        return contextParameters.Length == 1 &&
 401329               TelegramHandlerSymbols.IsType(contextParameters[0].Type, expectedContextType);
 1330    }
 1331
 1332    private static bool HasValidCallbackPayloadParameter(
 1333        IMethodSymbol method,
 1334        ITypeSymbol? callbackPayloadType)
 1335    {
 401336        if (callbackPayloadType is null)
 1337        {
 371338            return true;
 1339        }
 1340
 31341        if (IsInvalidCallbackPayloadType(callbackPayloadType))
 1342        {
 01343            return false;
 1344        }
 1345
 31346        return method.Parameters.Count(parameter =>
 111347            SymbolEqualityComparer.Default.Equals(parameter.Type, callbackPayloadType)) == 1;
 1348    }
 1349
 1350    private static bool IsInvalidCallbackPayloadType(ITypeSymbol type)
 1351    {
 31352        return IsInvalidConcreteNamedType(type);
 1353    }
 1354
 1355    private static bool IsAccessibleFromGeneratedCode(INamedTypeSymbol type)
 1356    {
 81357        for (INamedTypeSymbol? current = type; current is not null; current = current.ContainingType)
 1358        {
 21359            if (current.DeclaredAccessibility is not (Accessibility.Public or Accessibility.Internal))
 1360            {
 01361                return false;
 1362            }
 1363        }
 1364
 21365        return true;
 1366    }
 1367
 1368    private static bool TryGetCallbackDataFields(
 1369        INamedTypeSymbol type,
 1370        out bool usesConstructor,
 1371        out ImmutableArray<GeneratedCallbackDataField> fields)
 1372    {
 21373        usesConstructor = false;
 21374        fields = ImmutableArray<GeneratedCallbackDataField>.Empty;
 1375
 21376        IMethodSymbol? constructor = type.InstanceConstructors
 21377            .Where(static candidate =>
 41378                candidate.DeclaredAccessibility == Accessibility.Public &&
 41379                candidate.Parameters.Length > 0)
 21380            .OrderByDescending(static candidate => candidate.Parameters.Length)
 21381            .FirstOrDefault();
 1382
 21383        if (constructor is not null)
 1384        {
 21385            return TryGetConstructorCallbackDataFields(type, constructor, out usesConstructor, out fields);
 1386        }
 1387
 01388        return TryGetPropertyCallbackDataFields(type, out fields);
 1389    }
 1390
 1391    private static bool TryGetConstructorCallbackDataFields(
 1392        INamedTypeSymbol type,
 1393        IMethodSymbol constructor,
 1394        out bool usesConstructor,
 1395        out ImmutableArray<GeneratedCallbackDataField> fields)
 1396    {
 21397        usesConstructor = true;
 21398        fields = ImmutableArray<GeneratedCallbackDataField>.Empty;
 1399
 21400        Dictionary<string, IPropertySymbol> properties = type
 21401            .GetMembers()
 21402            .OfType<IPropertySymbol>()
 21403            .Where(static property =>
 101404                !property.IsStatic &&
 101405                property.GetMethod is { DeclaredAccessibility: Accessibility.Public })
 101406            .ToDictionary(static property => property.Name, StringComparer.OrdinalIgnoreCase);
 21407        ImmutableArray<GeneratedCallbackDataField>.Builder builder = ImmutableArray.CreateBuilder<GeneratedCallbackDataF
 1408
 201409        foreach (IParameterSymbol parameter in constructor.Parameters)
 1410        {
 81411            if (!properties.TryGetValue(parameter.Name, out IPropertySymbol? property) ||
 81412                !TryGetCallbackDataFieldKind(property.Type, out GeneratedCallbackDataFieldKind kind))
 1413            {
 01414                return false;
 1415            }
 1416
 81417            builder.Add(new GeneratedCallbackDataField(
 81418                property.Name,
 81419                property.Type.ToDisplayString(FullyQualifiedFormat),
 81420                kind));
 1421        }
 1422
 21423        fields = builder.ToImmutable();
 21424        return true;
 1425    }
 1426
 1427    private static bool TryGetPropertyCallbackDataFields(
 1428        INamedTypeSymbol type,
 1429        out ImmutableArray<GeneratedCallbackDataField> fields)
 1430    {
 01431        ImmutableArray<GeneratedCallbackDataField>.Builder builder = ImmutableArray.CreateBuilder<GeneratedCallbackDataF
 1432
 01433        foreach (IPropertySymbol property in type
 01434                     .GetMembers()
 01435                     .OfType<IPropertySymbol>()
 01436                     .Where(static property =>
 01437                         !property.IsStatic &&
 01438                         property.GetMethod is { DeclaredAccessibility: Accessibility.Public }))
 1439        {
 01440            if (property.SetMethod is not { DeclaredAccessibility: Accessibility.Public } ||
 01441                !TryGetCallbackDataFieldKind(property.Type, out GeneratedCallbackDataFieldKind kind))
 1442            {
 01443                return false;
 1444            }
 1445
 01446            builder.Add(new GeneratedCallbackDataField(
 01447                property.Name,
 01448                property.Type.ToDisplayString(FullyQualifiedFormat),
 01449                kind));
 1450        }
 1451
 01452        fields = builder.ToImmutable();
 01453        return true;
 01454    }
 1455
 1456    private static bool TryGetCallbackDataFieldKind(
 1457        ITypeSymbol type,
 1458        out GeneratedCallbackDataFieldKind kind)
 1459    {
 81460        kind = type.SpecialType switch
 81461        {
 21462            SpecialType.System_String => GeneratedCallbackDataFieldKind.String,
 01463            SpecialType.System_Int32 => GeneratedCallbackDataFieldKind.Int32,
 21464            SpecialType.System_Int64 => GeneratedCallbackDataFieldKind.Int64,
 21465            SpecialType.System_Boolean => GeneratedCallbackDataFieldKind.Boolean,
 21466            _ => default
 81467        };
 1468
 81469        if (type.SpecialType is SpecialType.System_String or
 81470            SpecialType.System_Int32 or
 81471            SpecialType.System_Int64 or
 81472            SpecialType.System_Boolean)
 1473        {
 61474            return true;
 1475        }
 1476
 21477        if (type.TypeKind == TypeKind.Enum)
 1478        {
 21479            kind = GeneratedCallbackDataFieldKind.Enum;
 21480            return true;
 1481        }
 1482
 01483        return false;
 1484    }
 1485
 1486    private static bool IsInvalidConcreteNamedType(ITypeSymbol type)
 1487    {
 61488        return type.TypeKind is TypeKind.Interface or TypeKind.TypeParameter ||
 61489               type is not INamedTypeSymbol namedType ||
 61490               namedType.IsAbstract ||
 61491               namedType.IsUnboundGenericType ||
 61492               namedType.TypeArguments.Any(static argument => argument.TypeKind == TypeKind.TypeParameter);
 1493    }
 1494
 1495    private static bool IsSupportedReturnType(ITypeSymbol returnType)
 1496    {
 401497        return TelegramHandlerSymbols.IsType(returnType, TelegramHandlerSymbols.Task) ||
 401498               TelegramHandlerSymbols.IsType(returnType, TelegramHandlerSymbols.ValueTask);
 1499    }
 1500
 1501    private static bool TryGetErrorReturnKind(
 1502        ITypeSymbol returnType,
 1503        out GeneratedErrorReturnKind returnKind)
 1504    {
 31505        if (TelegramHandlerSymbols.IsType(returnType, TelegramHandlerSymbols.TelegramErrorHandlingResult))
 1506        {
 11507            returnKind = GeneratedErrorReturnKind.Sync;
 11508            return true;
 1509        }
 1510
 21511        if (returnType is INamedTypeSymbol
 21512            {
 21513                IsGenericType: true,
 21514                TypeArguments.Length: 1
 21515            } namedType &&
 21516            TelegramHandlerSymbols.IsType(namedType.TypeArguments[0], TelegramHandlerSymbols.TelegramErrorHandlingResult
 1517        {
 21518            GeneratedErrorReturnKind? matchedReturnKind = namedType.ConstructedFrom
 21519                .ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat) switch
 21520            {
 11521                "System.Threading.Tasks.Task<TResult>" => GeneratedErrorReturnKind.Task,
 11522                "System.Threading.Tasks.ValueTask<TResult>" => GeneratedErrorReturnKind.ValueTask,
 01523                _ => null
 21524            };
 1525
 21526            if (matchedReturnKind is not null)
 1527            {
 21528                returnKind = matchedReturnKind.Value;
 21529                return true;
 1530            }
 1531        }
 1532
 01533        returnKind = default;
 01534        return false;
 1535    }
 1536
 1537    private static bool IsValidCommand(string? command)
 1538    {
 121539        return command is not null &&
 121540               !string.IsNullOrWhiteSpace(command) &&
 121541               !command.StartsWith("/", StringComparison.Ordinal) &&
 121542               !command.Contains("@", StringComparison.Ordinal) &&
 121543               !command.Any(char.IsWhiteSpace);
 1544    }
 1545
 1546    private static string? GetConstructorString(AttributeData attribute)
 1547    {
 251548        if (attribute.ConstructorArguments.Length > 0 &&
 251549            attribute.ConstructorArguments[0].Value is string value)
 1550        {
 251551            return value.Trim();
 1552        }
 1553
 01554        return null;
 1555    }
 1556
 1557    private static bool TryGetPrefixes(
 1558        AttributeData attribute,
 1559        out ImmutableArray<string> prefixes)
 1560    {
 161561        prefixes = ["/"];
 1562
 401563        foreach (KeyValuePair<string, TypedConstant> argument in attribute.NamedArguments)
 1564        {
 61565            if (!string.Equals(argument.Key, "Prefixes", StringComparison.Ordinal))
 1566            {
 1567                continue;
 1568            }
 1569
 41570            if (argument.Value.Values.IsDefaultOrEmpty)
 1571            {
 01572                return false;
 1573            }
 1574
 41575            ImmutableArray<string>.Builder builder = ImmutableArray.CreateBuilder<string>();
 41576            HashSet<string> seen = new HashSet<string>(StringComparer.Ordinal);
 1577
 191578            foreach (TypedConstant value in argument.Value.Values)
 1579            {
 61580                if (value.Value is not string prefix ||
 61581                    string.IsNullOrWhiteSpace(prefix))
 1582                {
 11583                    return false;
 1584                }
 1585
 51586                string normalized = prefix.Trim();
 1587
 51588                if (seen.Add(normalized))
 1589                {
 51590                    builder.Add(normalized);
 1591                }
 1592            }
 1593
 31594            prefixes = builder.ToImmutable();
 31595            return true;
 1596        }
 1597
 121598        return true;
 1599    }
 1600
 1601    private static int GetCommandPrefixMode(AttributeData attribute)
 1602    {
 381603        foreach (KeyValuePair<string, TypedConstant> argument in attribute.NamedArguments)
 1604        {
 41605            if (string.Equals(argument.Key, "PrefixMode", StringComparison.Ordinal) &&
 41606                argument.Value.Value is int value)
 1607            {
 21608                return value;
 1609            }
 1610        }
 1611
 141612        return CommandPrefixModeRequired;
 1613    }
 1614
 1615    private static bool HasInvalidNoPrefixConfiguration(
 1616        AttributeData attribute,
 1617        int prefixMode)
 1618    {
 151619        return prefixMode == CommandPrefixModeNoPrefix && HasNamedArgument(attribute, "Prefixes");
 1620    }
 1621
 1622    private static bool IsSupportedCommandPrefixMode(int value)
 1623    {
 151624        return value is CommandPrefixModeRequired or
 151625            CommandPrefixModeOptional or
 151626            CommandPrefixModeNoPrefix;
 1627    }
 1628
 1629    private static bool HasNamedArgument(
 1630        AttributeData attribute,
 1631        string name)
 1632    {
 31633        return attribute.NamedArguments.Any(argument => string.Equals(argument.Key, name, StringComparison.Ordinal));
 1634    }
 1635
 1636    private static bool CommandPatternStartsWithPrefix(
 1637        string pattern,
 1638        ImmutableArray<string> prefixes)
 1639    {
 21640        string normalized = pattern.Trim();
 1641
 51642        return prefixes.Any(prefix => normalized.StartsWith(prefix, StringComparison.Ordinal));
 1643    }
 1644
 1645    private static bool GetNamedBool(
 1646        AttributeData attribute,
 1647        string name,
 1648        bool defaultValue)
 1649    {
 771650        foreach (KeyValuePair<string, TypedConstant> argument in attribute.NamedArguments)
 1651        {
 81652            if (string.Equals(argument.Key, name, StringComparison.Ordinal) &&
 81653                argument.Value.Value is bool value)
 1654            {
 11655                return value;
 1656            }
 1657        }
 1658
 301659        return defaultValue;
 1660    }
 1661
 1662    private static bool TryParseTemplateRouteValues(
 1663        string? template,
 1664        out ImmutableDictionary<string, GeneratedRouteValue> routeValues)
 1665    {
 71666        routeValues = ImmutableDictionary<string, GeneratedRouteValue>.Empty;
 1667
 71668        if (template is null ||
 71669            string.IsNullOrWhiteSpace(template))
 1670        {
 01671            return false;
 1672        }
 1673
 71674        ImmutableDictionary<string, GeneratedRouteValue>.Builder values = ImmutableDictionary.CreateBuilder<string, Gene
 71675        Regex placeholderRegex = new Regex(
 71676            @"\{(?<name>[A-Za-z_][A-Za-z0-9_]*)(?:(?<nameOptional>\?)|:(?<constraint>[A-Za-z][A-Za-z0-9_]*)(?<constraint
 71677            RegexOptions.CultureInvariant);
 71678        int position = 0;
 1679
 261680        foreach (Match match in placeholderRegex.Matches(template))
 1681        {
 61682            if (match.Index != position &&
 61683                ContainsBrace(template.Substring(position, match.Index - position)))
 1684            {
 01685                return false;
 1686            }
 1687
 61688            string name = match.Groups["name"].Value;
 61689            bool hasNameOptional = match.Groups["nameOptional"].Success;
 61690            bool hasConstraintOptional = match.Groups["constraintOptional"].Success;
 61691            string constraint = match.Groups["constraint"].Success
 61692                ? match.Groups["constraint"].Value
 61693                : "string";
 1694
 61695            if (values.ContainsKey(name) ||
 61696                constraint is not ("string" or "int" or "long"))
 1697            {
 01698                return false;
 1699            }
 1700
 61701            values.Add(name, new GeneratedRouteValue(constraint, hasNameOptional || hasConstraintOptional));
 61702            position = match.Index + match.Length;
 1703        }
 1704
 71705        if (ContainsBrace(template.Substring(position)))
 1706        {
 11707            return false;
 1708        }
 1709
 61710        routeValues = values.ToImmutable();
 61711        return true;
 01712    }
 1713
 1714    private static bool TryParseRegexRouteValues(
 1715        string? pattern,
 1716        out ImmutableDictionary<string, GeneratedRouteValue> routeValues)
 1717    {
 11718        routeValues = ImmutableDictionary<string, GeneratedRouteValue>.Empty;
 1719
 11720        if (string.IsNullOrWhiteSpace(pattern))
 1721        {
 01722            return false;
 1723        }
 1724
 1725        Regex regex;
 1726
 1727        try
 1728        {
 11729            regex = new Regex(pattern, RegexOptions.CultureInvariant);
 11730        }
 01731        catch (ArgumentException)
 1732        {
 01733            return false;
 1734        }
 1735
 11736        ImmutableDictionary<string, GeneratedRouteValue>.Builder values = ImmutableDictionary.CreateBuilder<string, Gene
 1737
 61738        foreach (string name in regex.GetGroupNames())
 1739        {
 21740            if (!int.TryParse(name, out _))
 1741            {
 11742                values[name] = new GeneratedRouteValue(Constraint: null, IsOptional: false);
 1743            }
 1744        }
 1745
 11746        routeValues = values.ToImmutable();
 11747        return true;
 01748    }
 1749
 1750    private static bool ContainsBrace(string value)
 1751    {
 131752        return value.IndexOf("{", StringComparison.Ordinal) >= 0 ||
 131753               value.IndexOf("}", StringComparison.Ordinal) >= 0;
 1754    }
 1755
 1756    private static bool TryResolveRouteValueNames(
 1757        IMethodSymbol method,
 1758        ImmutableArray<GeneratedRoute> routes,
 1759        out ImmutableHashSet<string> routeValueNames)
 1760    {
 401761        routeValueNames = ImmutableHashSet<string>.Empty.WithComparer(StringComparer.Ordinal);
 1762
 401763        GeneratedRoute[] routeValueRoutes = routes
 421764            .Where(static route => route.RouteValues.Count > 0)
 401765            .ToArray();
 1766
 401767        if (routeValueRoutes.Length == 0)
 1768        {
 361769            return true;
 1770        }
 1771
 91772        if (routes.Any(static route => route.RouteValues.Count == 0))
 1773        {
 01774            return false;
 1775        }
 1776
 41777        string[] firstNames = routeValueRoutes[0].RouteValues.Keys.OrderBy(static name => name, StringComparer.Ordinal).
 1778
 101779        foreach (GeneratedRoute route in routeValueRoutes.Skip(1))
 1780        {
 11781            string[] names = route.RouteValues.Keys.OrderBy(static name => name, StringComparer.Ordinal).ToArray();
 1782
 11783            if (!firstNames.SequenceEqual(names, StringComparer.Ordinal))
 1784            {
 01785                return false;
 1786            }
 1787        }
 1788
 41789        ImmutableHashSet<string>.Builder builder = ImmutableHashSet.CreateBuilder<string>(StringComparer.Ordinal);
 1790
 161791        foreach (string name in firstNames)
 1792        {
 121793            IParameterSymbol? parameter = method.Parameters.FirstOrDefault(parameter => string.Equals(parameter.Name, na
 1794
 41795            if (parameter is null ||
 41796                !IsSupportedRouteValueParameterType(parameter.Type))
 1797            {
 01798                return false;
 1799            }
 1800
 181801            foreach (GeneratedRoute route in routeValueRoutes)
 1802            {
 51803                GeneratedRouteValue routeValue = route.RouteValues[name];
 1804
 51805                if (routeValue.IsOptional != IsNullableRouteValueParameter(parameter.Type))
 1806                {
 01807                    return false;
 1808                }
 1809
 51810                if (routeValue.Constraint is not null &&
 51811                    !RouteConstraintMatchesParameter(routeValue.Constraint, parameter.Type))
 1812                {
 01813                    return false;
 1814                }
 1815            }
 1816
 41817            builder.Add(name);
 1818        }
 1819
 41820        routeValueNames = builder.ToImmutable();
 41821        return true;
 01822    }
 1823
 1824    private static bool IsSupportedRouteValueParameterType(ITypeSymbol type)
 1825    {
 41826        return IsRouteValueParameterType(type);
 1827    }
 1828
 1829    private static bool IsNullableRouteValueParameter(ITypeSymbol type)
 1830    {
 51831        if (type.SpecialType == SpecialType.System_String)
 1832        {
 01833            return type.NullableAnnotation == NullableAnnotation.Annotated;
 1834        }
 1835
 51836        return type is INamedTypeSymbol
 51837        {
 51838            OriginalDefinition.SpecialType: SpecialType.System_Nullable_T
 51839        };
 1840    }
 1841
 1842    private static SpecialType GetRouteValueParameterSpecialType(ITypeSymbol type)
 1843    {
 41844        if (type is INamedTypeSymbol
 41845            {
 41846                OriginalDefinition.SpecialType: SpecialType.System_Nullable_T,
 41847                TypeArguments.Length: 1
 41848            } nullable)
 1849        {
 11850            return nullable.TypeArguments[0].SpecialType;
 1851        }
 1852
 31853        return type.SpecialType;
 1854    }
 1855
 1856    private static bool RouteConstraintMatchesParameter(
 1857        string constraint,
 1858        ITypeSymbol parameterType)
 1859    {
 41860        SpecialType specialType = GetRouteValueParameterSpecialType(parameterType);
 1861
 41862        return constraint switch
 41863        {
 01864            "string" => specialType == SpecialType.System_String,
 11865            "int" => specialType == SpecialType.System_Int32,
 31866            "long" => specialType == SpecialType.System_Int64,
 01867            _ => false
 41868        };
 1869    }
 1870
 1871    private static string GetRouteValueTypeName(GeneratedRouteValue routeValue)
 1872    {
 41873        return routeValue.Constraint switch
 41874        {
 11875            "int" => "int",
 31876            "long" => "long",
 01877            _ => "string"
 41878        };
 1879    }
 1880
 1881    private static ImmutableArray<GeneratedTextFilter> BuildTextFilters(
 1882        IMethodSymbol method,
 1883        bool includeClassRouteAttributes)
 1884    {
 291885        ImmutableArray<GeneratedTextFilter>.Builder builder = ImmutableArray.CreateBuilder<GeneratedTextFilter>();
 1886
 621887        foreach (AttributeData attribute in GetRouteAttributes(
 291888                     method,
 291889                     TelegramHandlerSymbols.TextAttribute,
 291890                     includeClassRouteAttributes))
 1891        {
 21892            string? value = attribute.ConstructorArguments.Length > 0
 21893                ? attribute.ConstructorArguments[0].Value as string
 21894                : null;
 1895
 21896            if (value is null ||
 21897                string.IsNullOrWhiteSpace(value))
 1898            {
 1899                continue;
 1900            }
 1901
 21902            int mode = attribute.ConstructorArguments.Length > 1 &&
 21903                       attribute.ConstructorArguments[1].Value is int modeValue
 21904                ? modeValue
 21905                : 0;
 21906            bool ignoreCase = attribute.ConstructorArguments.Length > 2 &&
 21907                             attribute.ConstructorArguments[2].Value is bool ignoreCaseValue
 21908                ? ignoreCaseValue
 21909                : true;
 1910
 21911            builder.Add(new GeneratedTextFilter(value, mode, ignoreCase));
 1912        }
 1913
 291914        return builder.ToImmutable();
 1915    }
 1916
 1917    private static IReadOnlyList<AttributeData> GetRouteAttributes(
 1918        IMethodSymbol method,
 1919        string metadataName,
 1920        bool includeClassRouteAttributes)
 1921    {
 6571922        IReadOnlyList<AttributeData> methodAttributes = TelegramHandlerSymbols.GetAttributes(method, metadataName, inher
 1923
 6571924        if (!includeClassRouteAttributes)
 1925        {
 6031926            return methodAttributes;
 1927        }
 1928
 541929        IReadOnlyList<AttributeData> typeAttributes = TelegramHandlerSymbols.GetAttributes(method.ContainingType, metada
 1930
 541931        return typeAttributes.Count == 0
 541932            ? methodAttributes
 541933            : typeAttributes.Concat(methodAttributes).ToArray();
 1934    }
 1935
 1936    private static IReadOnlyList<AttributeData> GetGenericRouteAttributes(
 1937        IMethodSymbol method,
 1938        string metadataName,
 1939        bool includeClassRouteAttributes)
 1940    {
 451941        IReadOnlyList<AttributeData> methodAttributes = TelegramHandlerSymbols.GetGenericAttributes(method, metadataName
 1942
 451943        if (!includeClassRouteAttributes)
 1944        {
 411945            return methodAttributes;
 1946        }
 1947
 41948        IReadOnlyList<AttributeData> typeAttributes = TelegramHandlerSymbols.GetGenericAttributes(method.ContainingType,
 1949
 41950        return typeAttributes.Count == 0
 41951            ? methodAttributes
 41952            : typeAttributes.Concat(methodAttributes).ToArray();
 1953    }
 1954
 1955    private static bool IsClassBasedHandlerType(INamedTypeSymbol type)
 1956    {
 1431957        return IsAssignableTo(type, TelegramHandlerSymbols.MessageHandler) ||
 1431958               IsAssignableTo(type, TelegramHandlerSymbols.CallbackHandler) ||
 1431959               IsAssignableTo(type, TelegramHandlerSymbols.ChatMemberUpdateHandler);
 1960    }
 1961
 1962    private static bool IsClassBasedRouteCompatible(
 1963        INamedTypeSymbol type,
 1964        GeneratedHandlerKind kind,
 1965        ITypeSymbol? callbackPayloadType)
 1966    {
 401967        if (!IsClassBasedHandlerType(type))
 1968        {
 361969            return true;
 1970        }
 1971
 41972        if (IsAssignableTo(type, TelegramHandlerSymbols.MessageHandler))
 1973        {
 21974            return kind is GeneratedHandlerKind.Command or GeneratedHandlerKind.Message;
 1975        }
 1976
 21977        if (TryGetCallbackHandlerPayloadType(type, out ITypeSymbol handlerPayloadType))
 1978        {
 11979            return kind == GeneratedHandlerKind.Callback &&
 11980                   callbackPayloadType is not null &&
 11981                   SymbolEqualityComparer.Default.Equals(handlerPayloadType, callbackPayloadType);
 1982        }
 1983
 11984        if (IsDirectlyAssignableTo(type, TelegramHandlerSymbols.CallbackHandler))
 1985        {
 01986            return kind == GeneratedHandlerKind.Callback &&
 01987                   callbackPayloadType is null;
 1988        }
 1989
 11990        return IsAssignableTo(type, TelegramHandlerSymbols.ChatMemberUpdateHandler) &&
 11991               kind == GeneratedHandlerKind.ChatMember;
 1992    }
 1993
 1994    private static ImmutableArray<IMethodSymbol> GetDeclaredHandleAsyncMethods(INamedTypeSymbol type)
 1995    {
 41996        return type.GetMembers("HandleAsync")
 41997            .OfType<IMethodSymbol>()
 41998            .Where(static method =>
 41999                method.DeclaredAccessibility == Accessibility.Public &&
 42000                !method.IsStatic &&
 42001                method.MethodKind == MethodKind.Ordinary &&
 42002                !method.IsGenericMethod)
 42003            .ToImmutableArray();
 2004    }
 2005
 2006    private static bool TryGetCallbackHandlerPayloadType(
 2007        INamedTypeSymbol type,
 2008        out ITypeSymbol payloadType)
 2009    {
 122010        for (INamedTypeSymbol? current = type; current is not null; current = current.BaseType)
 2011        {
 52012            if (current.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat) ==
 52013                TelegramHandlerSymbols.GenericCallbackHandler &&
 52014                current.TypeArguments.Length == 1)
 2015            {
 12016                payloadType = current.TypeArguments[0];
 12017                return true;
 2018            }
 2019        }
 2020
 12021        payloadType = null!;
 12022        return false;
 2023    }
 2024
 2025    private static bool IsAssignableTo(INamedTypeSymbol type, string metadataName)
 2026    {
 28302027        for (INamedTypeSymbol? current = type; current is not null; current = current.BaseType)
 2028        {
 9872029            if (current.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat) == metadataName
 9872030                current.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat) == metadataName)
 2031            {
 272032                return true;
 2033            }
 2034        }
 2035
 4282036        return false;
 2037    }
 2038
 2039    private static bool IsDirectlyAssignableTo(INamedTypeSymbol type, string metadataName)
 2040    {
 12041        return IsAssignableTo(type, metadataName) &&
 12042               !TryGetCallbackHandlerPayloadType(type, out _);
 2043    }
 2044
 2045    private static bool TryBuildStates(
 2046        IMethodSymbol method,
 2047        out ImmutableArray<string> states,
 2048        out string? sceneName)
 2049    {
 402050        ImmutableArray<string>.Builder builder = ImmutableArray.CreateBuilder<string>();
 402051        states = [];
 402052        sceneName = null;
 2053
 882054        foreach (AttributeData attribute in TelegramHandlerSymbols.GetAttributes(method.ContainingType, TelegramHandlerS
 402055                     .Concat(TelegramHandlerSymbols.GetAttributes(method, TelegramHandlerSymbols.StateAttribute, inherit
 2056        {
 42057            if (attribute.ConstructorArguments.Length > 0 &&
 42058                attribute.ConstructorArguments[0].Value is string state &&
 42059                !string.IsNullOrWhiteSpace(state))
 2060            {
 42061                builder.Add(state);
 2062            }
 2063        }
 2064
 402065        AttributeData[] hasGenericStateAttributes = GetGenericStateAttributes(method.ContainingType)
 402066            .Concat(GetGenericStateAttributes(method))
 402067            .ToArray();
 2068
 822069        foreach (AttributeData attribute in hasGenericStateAttributes)
 2070        {
 12071            if (TryResolveTypedState(attribute, out string state))
 2072            {
 12073                builder.Add(state);
 2074            }
 2075        }
 2076
 402077        AttributeData? sceneStep = TelegramHandlerSymbols.GetFirstAttribute(
 402078            method,
 402079            TelegramHandlerSymbols.SceneStepAttribute,
 402080            inherit: true);
 2081
 402082        if (sceneStep is not null)
 2083        {
 22084            if (builder.Count > 0 ||
 22085                !TryResolveSceneStep(method, sceneStep, out string sceneState, out sceneName))
 2086            {
 12087                return false;
 2088            }
 2089
 12090            builder.Add(sceneState);
 2091        }
 2092
 392093        states = builder.ToImmutable();
 392094        return true;
 2095    }
 2096
 2097    private static IEnumerable<AttributeData> GetGenericStateAttributes(ISymbol symbol)
 2098    {
 802099        return TelegramHandlerSymbols.GetGenericAttributes(
 802100            symbol,
 802101            TelegramHandlerSymbols.GenericStateAttribute,
 802102            inherit: true);
 2103    }
 2104
 2105    private static bool TryResolveTypedState(AttributeData attribute, out string state)
 2106    {
 12107        state = string.Empty;
 2108
 12109        if (attribute.AttributeClass is not { TypeArguments.Length: 1 } attributeType ||
 12110            attribute.ConstructorArguments.Length == 0 ||
 12111            attribute.ConstructorArguments[0].Value is not string stateName ||
 12112            string.IsNullOrWhiteSpace(stateName) ||
 12113            attributeType.TypeArguments[0] is not INamedTypeSymbol groupType)
 2114        {
 02115            return false;
 2116        }
 2117
 12118        if (!TryGetStateContainerPrefix(groupType, out string prefix))
 2119        {
 02120            return false;
 2121        }
 2122
 12123        IPropertySymbol? property = groupType
 12124            .GetMembers(stateName)
 12125            .OfType<IPropertySymbol>()
 12126            .FirstOrDefault(IsUsableStateProperty);
 2127
 12128        if (property is null || !IsPartialStateProperty(property))
 2129        {
 02130            return false;
 2131        }
 2132
 12133        state = $"{prefix}:{GetStateSegment(property)}";
 12134        return true;
 2135    }
 2136
 2137    private static bool TryResolveSceneStep(
 2138        IMethodSymbol method,
 2139        AttributeData attribute,
 2140        out string state,
 2141        out string? sceneName)
 2142    {
 22143        state = string.Empty;
 22144        sceneName = null;
 2145
 22146        if (!TryGetScenePrefix(method.ContainingType, out string prefix) ||
 22147            attribute.ConstructorArguments.Length == 0 ||
 22148            attribute.ConstructorArguments[0].Value is not string stateName ||
 22149            string.IsNullOrWhiteSpace(stateName))
 2150        {
 02151            return false;
 2152        }
 2153
 22154        IPropertySymbol? property = method.ContainingType
 22155            .GetMembers(stateName)
 22156            .OfType<IPropertySymbol>()
 22157            .FirstOrDefault(IsUsableStateProperty);
 2158
 22159        if (property is null || !IsPartialStateProperty(property))
 2160        {
 12161            return false;
 2162        }
 2163
 12164        sceneName = prefix;
 12165        state = $"{prefix}:{GetStateSegment(property)}";
 12166        return true;
 2167    }
 2168
 2169    private static bool TryGetStateContainerPrefix(INamedTypeSymbol type, out string prefix)
 2170    {
 202171        return TryGetStateGroupPrefix(type, out prefix) ||
 202172               TryGetScenePrefix(type, out prefix);
 2173    }
 2174
 2175    private static bool TryGetStateGroupPrefix(INamedTypeSymbol type, out string prefix)
 2176    {
 202177        return TryGetAttributeStringPrefix(type, TelegramHandlerSymbols.StateGroupAttribute, out prefix);
 2178    }
 2179
 2180    private static bool TryGetScenePrefix(INamedTypeSymbol type, out string prefix)
 2181    {
 202182        return TryGetAttributeStringPrefix(type, TelegramHandlerSymbols.SceneAttribute, out prefix);
 2183    }
 2184
 2185    private static bool TryGetAttributeStringPrefix(
 2186        INamedTypeSymbol type,
 2187        string metadataName,
 2188        out string prefix)
 2189    {
 402190        AttributeData? attribute = TelegramHandlerSymbols.GetFirstAttribute(type, metadataName);
 2191
 402192        if (attribute?.ConstructorArguments.Length > 0 &&
 402193            attribute.ConstructorArguments[0].Value is string value &&
 402194            !string.IsNullOrWhiteSpace(value))
 2195        {
 72196            prefix = value;
 72197            return true;
 2198        }
 2199
 332200        prefix = string.Empty;
 332201        return false;
 2202    }
 2203
 2204    private static string GetStateSegment(IPropertySymbol property)
 2205    {
 82206        AttributeData? attribute = TelegramHandlerSymbols.GetFirstAttribute(
 82207            property,
 82208            TelegramHandlerSymbols.StateValueAttribute);
 2209
 82210        if (attribute?.ConstructorArguments.Length > 0 &&
 82211            attribute.ConstructorArguments[0].Value is string value &&
 82212            !string.IsNullOrWhiteSpace(value))
 2213        {
 22214            return value;
 2215        }
 2216
 62217        return ToCamelCase(property.Name);
 2218    }
 2219
 2220    private static bool IsUsableStateProperty(IPropertySymbol property)
 2221    {
 112222        return property.DeclaredAccessibility == Accessibility.Public &&
 112223               property.IsStatic &&
 112224               TelegramHandlerSymbols.IsType(property.Type, TelegramHandlerSymbols.State);
 2225    }
 2226
 2227    private static bool IsPartialStateProperty(IPropertySymbol property)
 2228    {
 92229        return property
 92230            .DeclaringSyntaxReferences
 92231            .Select(static reference => reference.GetSyntax())
 92232            .OfType<PropertyDeclarationSyntax>()
 442233            .Any(static declaration => declaration.Modifiers.Any(modifier => modifier.Text == "partial"));
 2234    }
 2235
 2236    private static string ToCamelCase(string value)
 2237    {
 62238        return value.Length == 0
 62239            ? value
 62240            : char.ToLowerInvariant(value[0]) + value.Substring(1);
 2241    }
 2242
 2243    private static ImmutableArray<GeneratedParameter> BuildParameters(
 2244        IMethodSymbol method,
 2245        GeneratedHandlerKind kind,
 2246        string expectedContextType,
 2247        ITypeSymbol? callbackPayloadType,
 2248        ImmutableHashSet<string> routeValueNames)
 2249    {
 392250        ImmutableArray<GeneratedParameter>.Builder builder = ImmutableArray.CreateBuilder<GeneratedParameter>();
 2251
 1882252        foreach (IParameterSymbol parameter in method.Parameters)
 2253        {
 552254            GeneratedParameterKind parameterKind = GetParameterKind(parameter, kind, expectedContextType, callbackPayloa
 2255
 552256            builder.Add(new GeneratedParameter(
 552257                parameter.Type.ToDisplayString(FullyQualifiedFormat),
 552258                parameterKind,
 552259                parameter.Name));
 2260        }
 2261
 392262        return builder.ToImmutable();
 2263    }
 2264
 2265    private static GeneratedParameterKind GetParameterKind(
 2266        IParameterSymbol parameter,
 2267        GeneratedHandlerKind kind,
 2268        string expectedContextType,
 2269        ITypeSymbol? callbackPayloadType,
 2270        ImmutableHashSet<string> routeValueNames)
 2271    {
 552272        if (TelegramHandlerSymbols.IsType(parameter.Type, expectedContextType))
 2273        {
 392274            return GeneratedParameterKind.Context;
 2275        }
 2276
 162277        if (TelegramHandlerSymbols.IsType(parameter.Type, TelegramHandlerSymbols.CancellationToken))
 2278        {
 12279            return GeneratedParameterKind.CancellationToken;
 2280        }
 2281
 152282        if (routeValueNames.Contains(parameter.Name))
 2283        {
 42284            return GeneratedParameterKind.RouteValue;
 2285        }
 2286
 112287        if (kind == GeneratedHandlerKind.Callback &&
 112288            callbackPayloadType is not null &&
 112289            SymbolEqualityComparer.Default.Equals(parameter.Type, callbackPayloadType))
 2290        {
 32291            return GeneratedParameterKind.CallbackPayload;
 2292        }
 2293
 82294        return GeneratedParameterKind.Service;
 2295    }
 2296
 2297    private static ImmutableArray<GeneratedErrorParameter> BuildErrorParameters(IMethodSymbol method)
 2298    {
 32299        ImmutableArray<GeneratedErrorParameter>.Builder builder = ImmutableArray.CreateBuilder<GeneratedErrorParameter>(
 2300
 282301        foreach (IParameterSymbol parameter in method.Parameters)
 2302        {
 112303            builder.Add(new GeneratedErrorParameter(
 112304                parameter.Type.ToDisplayString(FullyQualifiedFormat),
 112305                GetErrorParameterKind(parameter),
 112306                parameter.Name));
 2307        }
 2308
 32309        return builder.ToImmutable();
 2310    }
 2311
 2312    private static GeneratedErrorParameterKind GetErrorParameterKind(IParameterSymbol parameter)
 2313    {
 112314        if (TelegramHandlerSymbols.IsType(parameter.Type, TelegramHandlerSymbols.TelegramErrorContext))
 2315        {
 22316            return GeneratedErrorParameterKind.ErrorContext;
 2317        }
 2318
 92319        if (TelegramHandlerSymbols.IsType(parameter.Type, TelegramHandlerSymbols.CancellationToken))
 2320        {
 22321            return GeneratedErrorParameterKind.CancellationToken;
 2322        }
 2323
 72324        if (IsExceptionType(parameter.Type))
 2325        {
 32326            return GeneratedErrorParameterKind.Exception;
 2327        }
 2328
 42329        if (IsTelegramContextType(parameter.Type))
 2330        {
 22331            return GeneratedErrorParameterKind.TelegramContext;
 2332        }
 2333
 22334        if (IsErrorRouteValueParameterType(parameter.Type))
 2335        {
 12336            return GeneratedErrorParameterKind.RouteValue;
 2337        }
 2338
 12339        return GeneratedErrorParameterKind.Service;
 2340    }
 2341
 2342    private static bool IsExceptionType(ITypeSymbol type)
 2343    {
 202344        return type is INamedTypeSymbol namedType &&
 202345               IsAssignableTo(namedType, TelegramHandlerSymbols.Exception);
 2346    }
 2347
 2348    private static bool IsTelegramContextType(ITypeSymbol type)
 2349    {
 152350        return type is INamedTypeSymbol namedType &&
 152351               IsAssignableTo(namedType, TelegramHandlerSymbols.TelegramUpdateContext);
 2352    }
 2353
 2354    private static bool IsAssignableFrom(ITypeSymbol targetType, ITypeSymbol sourceType)
 2355    {
 22356        if (sourceType is not INamedTypeSymbol source)
 2357        {
 02358            return false;
 2359        }
 2360
 42361        for (INamedTypeSymbol? current = source; current is not null; current = current.BaseType)
 2362        {
 22363            if (SymbolEqualityComparer.Default.Equals(current, targetType))
 2364            {
 22365                return true;
 2366            }
 2367        }
 2368
 02369        return false;
 2370    }
 2371
 2372    private static bool IsErrorRouteValueParameterType(ITypeSymbol type)
 2373    {
 22374        return IsRouteValueParameterType(type);
 2375    }
 2376
 2377    private static bool IsRouteValueParameterType(ITypeSymbol type)
 2378    {
 62379        return type.SpecialType is
 62380                   SpecialType.System_String or
 62381                   SpecialType.System_Int32 or
 62382                   SpecialType.System_Int64 ||
 62383               type is INamedTypeSymbol
 62384               {
 62385                   OriginalDefinition.SpecialType: SpecialType.System_Nullable_T,
 62386                   TypeArguments.Length: 1
 62387               } nullable &&
 62388               nullable.TypeArguments[0].SpecialType is
 62389                   SpecialType.System_Int32 or
 62390                   SpecialType.System_Int64;
 2391    }
 2392}

/_/src/TeleFlow.Generators/TelegramHandlerSourceGenerator.Emission.cs

#LineLine coverage
 1using System.Text;
 2using Microsoft.CodeAnalysis;
 3
 4namespace TeleFlow.Generators;
 5
 6public sealed partial class TelegramHandlerSourceGenerator
 7{
 8    private static string GenerateSource(
 9        IReadOnlyList<GeneratedHandlerMethod> handlers,
 10        IReadOnlyList<GeneratedErrorHandlerMethod> errorHandlers,
 11        IReadOnlyList<GeneratedCallbackDataPayload> callbackDataPayloads)
 12    {
 2413        StringBuilder builder = new StringBuilder();
 2414        bool hasHandlers = handlers.Count > 0 || errorHandlers.Count > 0;
 2415        bool hasCallbackDataPayloads = callbackDataPayloads.Count > 0;
 16
 2417        builder.AppendLine("// <auto-generated />");
 2418        builder.AppendLine("#nullable enable");
 19
 2420        if (hasHandlers)
 21        {
 2322            builder.AppendLine("[assembly: global::TeleFlow.Telegram.TelegramGeneratedHandlersAttribute(typeof(global::T
 23        }
 24
 2425        if (hasCallbackDataPayloads)
 26        {
 227            builder.AppendLine("[assembly: global::TeleFlow.Telegram.TelegramGeneratedCallbackDataCodecsAttribute(typeof
 28        }
 29
 2430        builder.AppendLine();
 2431        builder.AppendLine("namespace TeleFlow.Generated;");
 2432        builder.AppendLine();
 2433        builder.Append("internal sealed class TelegramGeneratedHandlersRegistrar : ");
 2434        if (hasHandlers)
 35        {
 2336            builder.Append("global::TeleFlow.Telegram.ITelegramGeneratedHandlerRegistrar");
 37        }
 38
 2439        if (hasHandlers && hasCallbackDataPayloads)
 40        {
 141            builder.Append(", ");
 42        }
 43
 2444        if (hasCallbackDataPayloads)
 45        {
 246            builder.Append("global::TeleFlow.Telegram.ITelegramGeneratedCallbackDataCodecRegistrar");
 47        }
 48
 2449        builder.AppendLine();
 2450        builder.AppendLine("{");
 51
 2452        if (hasHandlers)
 53        {
 2354            builder.AppendLine("    public void Register(global::TeleFlow.Telegram.ITelegramGeneratedHandlerRegistry reg
 2355            builder.AppendLine("    {");
 56
 2357            int descriptorIndex = 0;
 58
 12459            for (int handlerIndex = 0; handlerIndex < handlers.Count; handlerIndex++)
 60            {
 16061                foreach (GeneratedRoute route in handlers[handlerIndex].Routes)
 62                {
 4163                    AppendRegistration(builder, handlers[handlerIndex], route, handlerIndex, descriptorIndex);
 4164                    descriptorIndex++;
 65                }
 66            }
 67
 2368            int errorDescriptorIndex = 0;
 69
 5270            for (int handlerIndex = 0; handlerIndex < errorHandlers.Count; handlerIndex++)
 71            {
 1272                foreach (string? exceptionType in errorHandlers[handlerIndex].ExceptionTypes)
 73                {
 374                    AppendErrorRegistration(
 375                        builder,
 376                        errorHandlers[handlerIndex],
 377                        exceptionType,
 378                        handlerIndex,
 379                        errorDescriptorIndex);
 380                    errorDescriptorIndex++;
 81                }
 82            }
 83
 2384            builder.AppendLine("    }");
 2385            builder.AppendLine();
 86        }
 87
 2488        if (hasCallbackDataPayloads)
 89        {
 290            AppendCallbackDataCodecRegistration(builder, callbackDataPayloads);
 91        }
 92
 12693        for (int index = 0; index < handlers.Count; index++)
 94        {
 3995            AppendInvoker(builder, handlers[index], index);
 96        }
 97
 5498        for (int index = 0; index < errorHandlers.Count; index++)
 99        {
 3100            AppendErrorInvoker(builder, errorHandlers[index], index);
 101        }
 102
 52103        for (int index = 0; index < callbackDataPayloads.Count; index++)
 104        {
 2105            AppendCallbackDataCodec(builder, callbackDataPayloads[index], index);
 106        }
 107
 24108        if (hasCallbackDataPayloads)
 109        {
 2110            AppendCallbackDataHelpers(builder);
 111        }
 112
 24113        builder.AppendLine("}");
 114
 24115        return builder.ToString();
 116    }
 117
 118    private static void AppendCallbackDataCodecRegistration(
 119        StringBuilder builder,
 120        IReadOnlyList<GeneratedCallbackDataPayload> callbackDataPayloads)
 121    {
 2122        builder.AppendLine("    public void RegisterCallbackDataCodecs(global::TeleFlow.Telegram.ITelegramGeneratedCallb
 2123        builder.AppendLine("    {");
 124
 8125        for (int index = 0; index < callbackDataPayloads.Count; index++)
 126        {
 2127            GeneratedCallbackDataPayload payload = callbackDataPayloads[index];
 128
 2129            builder.AppendLine("        registry.RegisterCallbackDataCodec(new global::TeleFlow.Telegram.TelegramGenerat
 2130            builder.AppendLine($"            typeof({payload.TypeName}),");
 2131            builder.AppendLine($"            {ToLiteral(payload.Prefix)},");
 2132            builder.AppendLine($"            PackCallbackData_{index},");
 2133            builder.AppendLine($"            MatchesCallbackData_{index},");
 2134            builder.AppendLine($"            UnpackCallbackData_{index}));");
 135        }
 136
 2137        builder.AppendLine("    }");
 2138        builder.AppendLine();
 2139    }
 140
 141    private static string GenerateStateGroupsSource(IReadOnlyList<GeneratedStateGroup> groups)
 142    {
 4143        StringBuilder builder = new StringBuilder();
 144
 4145        builder.AppendLine("// <auto-generated />");
 4146        builder.AppendLine("#nullable enable");
 4147        builder.AppendLine();
 148
 20149        foreach (IGrouping<string?, GeneratedStateGroup> namespaceGroup in groups.GroupBy(static group => group.Namespac
 150        {
 4151            if (!string.IsNullOrWhiteSpace(namespaceGroup.Key))
 152            {
 4153                builder.AppendLine($"namespace {namespaceGroup.Key};");
 4154                builder.AppendLine();
 155            }
 156
 16157            foreach (GeneratedStateGroup group in namespaceGroup)
 158            {
 4159                builder.Append(group.Accessibility);
 4160                builder.Append(' ');
 161
 4162                if (group.IsStatic)
 163                {
 0164                    builder.Append("static ");
 165                }
 166
 4167                builder.Append("partial class ");
 4168                builder.AppendLine(group.TypeName);
 4169                builder.AppendLine("{");
 170
 20171                foreach (GeneratedStateProperty state in group.States)
 172                {
 6173                    builder.Append("    public static partial global::TeleFlow.Framework.States.State ");
 6174                    builder.Append(state.Name);
 6175                    builder.Append(" => global::TeleFlow.Framework.States.State.Create(");
 6176                    builder.Append(ToLiteral($"{group.Prefix}:{state.Segment}"));
 6177                    builder.AppendLine(");");
 178                }
 179
 4180                builder.AppendLine("}");
 4181                builder.AppendLine();
 182            }
 183        }
 184
 4185        return builder.ToString();
 186    }
 187
 188    private static void AppendRegistration(
 189        StringBuilder builder,
 190        GeneratedHandlerMethod handler,
 191        GeneratedRoute route,
 192        int invokerIndex,
 193        int descriptorIndex)
 194    {
 41195        builder.AppendLine("        registry.RegisterHandler(new global::TeleFlow.Telegram.TelegramGeneratedHandlerDescr
 41196        builder.AppendLine($"            typeof({handler.HandlerTypeName}),");
 41197        builder.AppendLine($"            {ToLiteral(handler.MethodName)},");
 41198        builder.AppendLine($"            global::TeleFlow.Telegram.TelegramGeneratedHandlerKind.{route.Kind},");
 41199        builder.AppendLine($"            global::TeleFlow.Telegram.TelegramGeneratedRouteKind.{route.RouteKind},");
 41200        builder.AppendLine($"            {ToNullableLiteral(route.Pattern)},");
 41201        builder.AppendLine("            new string[]");
 41202        builder.AppendLine("            {");
 203
 168204        foreach (string prefix in route.CommandPrefixes)
 205        {
 43206            builder.AppendLine($"                {ToLiteral(prefix)},");
 207        }
 208
 41209        builder.AppendLine("            },");
 41210        builder.AppendLine($"            {ToBoolLiteral(route.AllowSpaceAfterPrefix)},");
 41211        builder.AppendLine($"            {ToBoolLiteral(route.IgnoreCase)},");
 41212        builder.AppendLine($"            {descriptorIndex},");
 41213        builder.AppendLine($"            {ToNullableLiteral(handler.ModuleName)},");
 41214        builder.AppendLine($"            {ToNullableLiteral(route.Command)},");
 41215        builder.AppendLine($"            {ToTypeOfNullable(handler.CallbackPayloadType)},");
 41216        builder.AppendLine("            new global::TeleFlow.Telegram.TelegramGeneratedTextFilterDescriptor[]");
 41217        builder.AppendLine("            {");
 218
 86219        foreach (GeneratedTextFilter filter in route.TextFilters)
 220        {
 2221            builder.AppendLine(
 2222                $"                new global::TeleFlow.Telegram.TelegramGeneratedTextFilterDescriptor({ToLiteral(filter.
 223        }
 224
 41225        builder.AppendLine("            },");
 41226        builder.AppendLine("            new global::TeleFlow.Telegram.TelegramGeneratedFilterDescriptor[]");
 41227        builder.AppendLine("            {");
 228
 134229        foreach (GeneratedFilter filter in route.Filters)
 230        {
 26231            AppendFilter(builder, filter);
 232        }
 233
 41234        builder.AppendLine("            },");
 41235        builder.AppendLine("            new global::TeleFlow.Telegram.TelegramGeneratedChatMemberTransitionDescriptor[]"
 41236        builder.AppendLine("            {");
 237
 96238        foreach (GeneratedChatMemberTransition transition in route.ChatMemberTransitions)
 239        {
 7240            builder.AppendLine(
 7241                $"                new global::TeleFlow.Telegram.TelegramGeneratedChatMemberTransitionDescriptor((global:
 242        }
 243
 41244        builder.AppendLine("            },");
 41245        builder.AppendLine("            new global::TeleFlow.Telegram.TelegramGeneratedRoleRequirementDescriptor[]");
 41246        builder.AppendLine("            {");
 247
 86248        foreach (int requirement in route.RoleRequirements)
 249        {
 2250            builder.AppendLine(
 2251                $"                new global::TeleFlow.Telegram.TelegramGeneratedRoleRequirementDescriptor((global::Tele
 252        }
 253
 41254        builder.AppendLine("            },");
 41255        builder.AppendLine("            new string[]");
 41256        builder.AppendLine("            {");
 257
 94258        foreach (string state in handler.States)
 259        {
 6260            builder.AppendLine($"                {ToLiteral(state)},");
 261        }
 262
 41263        builder.AppendLine("            },");
 41264        builder.AppendLine("            new global::TeleFlow.Telegram.TelegramGeneratedHandlerParameterDescriptor[]");
 41265        builder.AppendLine("            {");
 266
 198267        foreach (GeneratedParameter parameter in handler.Parameters)
 268        {
 58269            builder.AppendLine(
 58270                $"                new global::TeleFlow.Telegram.TelegramGeneratedHandlerParameterDescriptor(typeof({para
 271        }
 272
 41273        builder.AppendLine("            },");
 41274        builder.AppendLine($"            Invoke_{invokerIndex},");
 41275        builder.AppendLine($"            {ToNullableLiteral(handler.SceneName)},");
 41276        builder.AppendLine("            new global::TeleFlow.Telegram.TelegramGeneratedRouteValueDescriptor[]");
 41277        builder.AppendLine("            {");
 278
 46279        if (route.RouteValues.Values.Any(static value => value.Constraint is not null))
 280        {
 20281            foreach (KeyValuePair<string, GeneratedRouteValue> routeValue in route.RouteValues.OrderBy(static value => v
 282            {
 4283                builder.AppendLine(
 4284                    $"                new global::TeleFlow.Telegram.TelegramGeneratedRouteValueDescriptor({ToLiteral(rou
 285            }
 286        }
 287
 41288        builder.AppendLine("            },");
 41289        AppendAutoAnswerCallback(builder, handler.AutoAnswerCallback);
 290
 41291        if (route.PrefixMode == CommandPrefixModeRequired)
 292        {
 40293            builder.AppendLine("            ));");
 294        }
 295        else
 296        {
 1297            builder.AppendLine(",");
 1298            builder.AppendLine($"            prefixMode: {ToCommandPrefixModeLiteral(route.PrefixMode)}));");
 299        }
 1300    }
 301
 302    private static void AppendAutoAnswerCallback(
 303        StringBuilder builder,
 304        GeneratedAutoAnswerCallback? autoAnswerCallback)
 305    {
 41306        if (autoAnswerCallback is null)
 307        {
 40308            builder.AppendLine("            null");
 40309            return;
 310        }
 311
 1312        builder.AppendLine("            new global::TeleFlow.Telegram.TelegramGeneratedAutoAnswerCallbackDescriptor(");
 1313        builder.AppendLine($"                enabled: {ToBoolLiteral(autoAnswerCallback.Enabled)},");
 1314        builder.AppendLine($"                text: {ToNullableLiteral(autoAnswerCallback.Text)},");
 1315        builder.AppendLine($"                showAlert: {ToBoolLiteral(autoAnswerCallback.ShowAlert)})");
 1316    }
 317
 318    private static void AppendFilter(
 319        StringBuilder builder,
 320        GeneratedFilter filter)
 321    {
 26322        if (filter.CustomTypeName is not null)
 323        {
 3324            if (filter.CustomAttributeExpression is null)
 325            {
 2326                builder.AppendLine(
 2327                    $"                new global::TeleFlow.Telegram.TelegramGeneratedFilterDescriptor(typeof({filter.Cus
 328            }
 329            else
 330            {
 1331                builder.AppendLine(
 1332                    $"                new global::TeleFlow.Telegram.TelegramGeneratedFilterDescriptor(typeof({filter.Cus
 333            }
 334
 1335            return;
 336        }
 337
 23338        builder.AppendLine("                new global::TeleFlow.Telegram.TelegramGeneratedFilterDescriptor(");
 23339        builder.AppendLine($"                    global::TeleFlow.Telegram.TelegramGeneratedFilterKind.{filter.Kind},");
 23340        builder.AppendLine("                    new string[]");
 23341        builder.AppendLine("                    {");
 342
 66343        foreach (string value in filter.StringValues)
 344        {
 10345            builder.AppendLine($"                        {ToLiteral(value)},");
 346        }
 347
 23348        builder.AppendLine("                    },");
 23349        builder.AppendLine("                    new long[]");
 23350        builder.AppendLine("                    {");
 351
 56352        foreach (long value in filter.LongValues)
 353        {
 5354            builder.AppendLine($"                        {value}L,");
 355        }
 356
 23357        builder.AppendLine("                    }),");
 23358    }
 359
 360    private static void AppendErrorRegistration(
 361        StringBuilder builder,
 362        GeneratedErrorHandlerMethod handler,
 363        string? exceptionType,
 364        int invokerIndex,
 365        int descriptorIndex)
 366    {
 3367        builder.AppendLine("        registry.RegisterErrorHandler(new global::TeleFlow.Telegram.TelegramGeneratedErrorHa
 3368        builder.AppendLine($"            typeof({handler.HandlerTypeName}),");
 3369        builder.AppendLine($"            {ToLiteral(handler.MethodName)},");
 3370        builder.AppendLine($"            exceptionType: {ToTypeOfNullable(exceptionType)},");
 3371        builder.AppendLine($"            telegramContextType: {ToTypeOfNullable(handler.TelegramContextType)},");
 3372        builder.AppendLine($"            registrationOrder: {descriptorIndex},");
 3373        builder.AppendLine($"            moduleName: {ToNullableLiteral(handler.ModuleName)},");
 3374        builder.AppendLine("            parameters: new global::TeleFlow.Telegram.TelegramGeneratedErrorHandlerParameter
 3375        builder.AppendLine("            {");
 376
 28377        foreach (GeneratedErrorParameter parameter in handler.Parameters)
 378        {
 11379            builder.AppendLine(
 11380                $"                new global::TeleFlow.Telegram.TelegramGeneratedErrorHandlerParameterDescriptor(typeof(
 381        }
 382
 3383        builder.AppendLine("            },");
 3384        builder.AppendLine($"            invoker: InvokeError_{invokerIndex}));");
 3385    }
 386
 387    private static void AppendCallbackDataCodec(
 388        StringBuilder builder,
 389        GeneratedCallbackDataPayload payload,
 390        int index)
 391    {
 2392        builder.AppendLine($"    private static string PackCallbackData_{index}(object payload)");
 2393        builder.AppendLine("    {");
 2394        builder.AppendLine($"        var value = ({payload.TypeName})payload;");
 2395        builder.AppendLine("        return string.Concat(");
 2396        builder.AppendLine($"            {ToLiteral(payload.Prefix)}");
 397
 20398        foreach (GeneratedCallbackDataField field in payload.Fields)
 399        {
 8400            builder.AppendLine("            ,");
 8401            builder.Append("            \":\", ");
 8402            builder.Append(GetCallbackDataPackExpression(field));
 8403            builder.AppendLine();
 404        }
 405
 2406        builder.AppendLine("        );");
 2407        builder.AppendLine("    }");
 2408        builder.AppendLine();
 409
 2410        builder.AppendLine($"    private static bool MatchesCallbackData_{index}(string serializedPayload)");
 2411        builder.AppendLine("    {");
 2412        builder.AppendLine($"        return MatchesCallbackData(serializedPayload, {ToLiteral(payload.Prefix)}, {payload
 2413        builder.AppendLine("    }");
 2414        builder.AppendLine();
 415
 2416        builder.AppendLine($"    private static object UnpackCallbackData_{index}(string serializedPayload)");
 2417        builder.AppendLine("    {");
 2418        builder.AppendLine(
 2419            $"        var parts = SplitCallbackData(serializedPayload, {ToLiteral(payload.Prefix)}, {payload.Fields.Leng
 420
 2421        if (payload.UsesConstructor)
 422        {
 2423            builder.Append($"        return new {payload.TypeName}(");
 424
 20425            for (int fieldIndex = 0; fieldIndex < payload.Fields.Length; fieldIndex++)
 426            {
 8427                if (fieldIndex > 0)
 428                {
 6429                    builder.Append(", ");
 430                }
 431
 8432                builder.Append(GetCallbackDataUnpackExpression(payload, payload.Fields[fieldIndex], fieldIndex + 1));
 433            }
 434
 2435            builder.AppendLine(");");
 436        }
 437        else
 438        {
 0439            builder.AppendLine($"        return new {payload.TypeName}");
 0440            builder.AppendLine("        {");
 441
 0442            for (int fieldIndex = 0; fieldIndex < payload.Fields.Length; fieldIndex++)
 443            {
 0444                GeneratedCallbackDataField field = payload.Fields[fieldIndex];
 0445                builder.AppendLine($"            {field.PropertyName} = {GetCallbackDataUnpackExpression(payload, field,
 446            }
 447
 0448            builder.AppendLine("        };");
 449        }
 450
 2451        builder.AppendLine("    }");
 2452        builder.AppendLine();
 2453    }
 454
 455    private static string GetCallbackDataPackExpression(GeneratedCallbackDataField field)
 456    {
 8457        string property = $"value.{field.PropertyName}";
 458
 8459        return field.Kind switch
 8460        {
 2461            GeneratedCallbackDataFieldKind.String => $"EscapeCallbackDataField({property})",
 8462            GeneratedCallbackDataFieldKind.Int32 or
 2463                GeneratedCallbackDataFieldKind.Int64 => $"{property}.ToString(global::System.Globalization.CultureInfo.I
 2464            GeneratedCallbackDataFieldKind.Boolean => $"{property} ? \"true\" : \"false\"",
 2465            GeneratedCallbackDataFieldKind.Enum => $"EscapeCallbackDataField({property}.ToString())",
 0466            _ => throw new InvalidOperationException($"Unsupported generated callback data field kind '{field.Kind}'.")
 8467        };
 468    }
 469
 470    private static string GetCallbackDataUnpackExpression(
 471        GeneratedCallbackDataPayload payload,
 472        GeneratedCallbackDataField field,
 473        int partIndex)
 474    {
 8475        string value = $"UnescapeCallbackDataField(parts[{partIndex}])";
 476
 8477        return field.Kind switch
 8478        {
 2479            GeneratedCallbackDataFieldKind.String => value,
 0480            GeneratedCallbackDataFieldKind.Int32 => $"global::System.Int32.Parse({value}, global::System.Globalization.C
 2481            GeneratedCallbackDataFieldKind.Int64 => $"global::System.Int64.Parse({value}, global::System.Globalization.C
 2482            GeneratedCallbackDataFieldKind.Boolean => $"global::System.Boolean.Parse({value})",
 2483            GeneratedCallbackDataFieldKind.Enum => $"ParseCallbackDataEnum<{field.TypeName}>({value}, {ToLiteral(field.P
 0484            _ => throw new InvalidOperationException($"Unsupported generated callback data field kind '{field.Kind}'.")
 8485        };
 486    }
 487
 488    private static void AppendCallbackDataHelpers(StringBuilder builder)
 489    {
 2490        builder.AppendLine("    private static bool MatchesCallbackData(string serializedPayload, string prefix, int fie
 2491        builder.AppendLine("    {");
 2492        builder.AppendLine("        if (!serializedPayload.StartsWith(prefix, global::System.StringComparison.Ordinal))"
 2493        builder.AppendLine("        {");
 2494        builder.AppendLine("            return false;");
 2495        builder.AppendLine("        }");
 2496        builder.AppendLine();
 2497        builder.AppendLine("        if (fieldCount == 0)");
 2498        builder.AppendLine("        {");
 2499        builder.AppendLine("            return serializedPayload.Length == prefix.Length;");
 2500        builder.AppendLine("        }");
 2501        builder.AppendLine();
 2502        builder.AppendLine("        if (serializedPayload.Length <= prefix.Length || serializedPayload[prefix.Length] !=
 2503        builder.AppendLine("        {");
 2504        builder.AppendLine("            return false;");
 2505        builder.AppendLine("        }");
 2506        builder.AppendLine();
 2507        builder.AppendLine("        var separatorCount = 0;");
 2508        builder.AppendLine();
 2509        builder.AppendLine("        for (var index = prefix.Length; index < serializedPayload.Length; index++)");
 2510        builder.AppendLine("        {");
 2511        builder.AppendLine("            if (serializedPayload[index] == ':')");
 2512        builder.AppendLine("            {");
 2513        builder.AppendLine("                separatorCount++;");
 2514        builder.AppendLine("            }");
 2515        builder.AppendLine("        }");
 2516        builder.AppendLine();
 2517        builder.AppendLine("        return separatorCount == fieldCount;");
 2518        builder.AppendLine("    }");
 2519        builder.AppendLine();
 520
 2521        builder.AppendLine("    private static string[] SplitCallbackData(string serializedPayload, string prefix, int f
 2522        builder.AppendLine("    {");
 2523        builder.AppendLine("        if (!MatchesCallbackData(serializedPayload, prefix, fieldCount))");
 2524        builder.AppendLine("        {");
 2525        builder.AppendLine("            throw new global::System.Text.Json.JsonException($\"Telegram callback data does 
 2526        builder.AppendLine("        }");
 2527        builder.AppendLine();
 2528        builder.AppendLine("        return serializedPayload.Split(':');");
 2529        builder.AppendLine("    }");
 2530        builder.AppendLine();
 531
 2532        builder.AppendLine("    private static string EscapeCallbackDataField(string? value)");
 2533        builder.AppendLine("    {");
 2534        builder.AppendLine("        if (value is null)");
 2535        builder.AppendLine("        {");
 2536        builder.AppendLine("            throw new global::System.InvalidOperationException(\"Compact callback data field
 2537        builder.AppendLine("        }");
 2538        builder.AppendLine();
 2539        builder.AppendLine("        return value");
 2540        builder.AppendLine("            .Replace(\"%\", \"%25\", global::System.StringComparison.Ordinal)");
 2541        builder.AppendLine("            .Replace(\":\", \"%3A\", global::System.StringComparison.Ordinal);");
 2542        builder.AppendLine("    }");
 2543        builder.AppendLine();
 544
 2545        builder.AppendLine("    private static string UnescapeCallbackDataField(string value)");
 2546        builder.AppendLine("    {");
 2547        builder.AppendLine("        return value");
 2548        builder.AppendLine("            .Replace(\"%3A\", \":\", global::System.StringComparison.Ordinal)");
 2549        builder.AppendLine("            .Replace(\"%25\", \"%\", global::System.StringComparison.Ordinal);");
 2550        builder.AppendLine("    }");
 2551        builder.AppendLine();
 552
 2553        builder.AppendLine("    private static TEnum ParseCallbackDataEnum<TEnum>(string value, string fieldName, string
 2554        builder.AppendLine("        where TEnum : struct, global::System.Enum");
 2555        builder.AppendLine("    {");
 2556        builder.AppendLine("        if (global::System.Enum.TryParse<TEnum>(value, ignoreCase: false, out var result))")
 2557        builder.AppendLine("        {");
 2558        builder.AppendLine("            return result;");
 2559        builder.AppendLine("        }");
 2560        builder.AppendLine();
 2561        builder.AppendLine("        throw new global::System.Text.Json.JsonException($\"Telegram callback data field '{f
 2562        builder.AppendLine("    }");
 2563        builder.AppendLine();
 2564    }
 565
 566    private static void AppendInvoker(StringBuilder builder, GeneratedHandlerMethod handler, int index)
 567    {
 39568        builder.AppendLine($"    private static async global::System.Threading.Tasks.ValueTask Invoke_{index}(");
 39569        builder.AppendLine("        global::System.IServiceProvider services,");
 39570        builder.AppendLine("        object?[] arguments,");
 39571        builder.AppendLine("        global::System.Threading.CancellationToken cancellationToken)");
 39572        builder.AppendLine("    {");
 39573        builder.AppendLine(
 39574            $"        var handler = ({handler.HandlerTypeName})(services.GetService(typeof({handler.HandlerTypeName})) ?
 39575        builder.Append("        await handler.");
 39576        builder.Append(handler.MethodName);
 39577        builder.Append('(');
 578
 188579        for (int parameterIndex = 0; parameterIndex < handler.Parameters.Length; parameterIndex++)
 580        {
 55581            if (parameterIndex > 0)
 582            {
 16583                builder.Append(", ");
 584            }
 585
 55586            GeneratedParameter parameter = handler.Parameters[parameterIndex];
 55587            builder.Append('(');
 55588            builder.Append(parameter.TypeName);
 55589            builder.Append($")arguments[{parameterIndex}]!");
 590        }
 591
 39592        builder.AppendLine(").ConfigureAwait(false);");
 39593        builder.AppendLine("    }");
 39594        builder.AppendLine();
 39595    }
 596
 597    private static void AppendErrorInvoker(StringBuilder builder, GeneratedErrorHandlerMethod handler, int index)
 598    {
 3599        string asyncKeyword = handler.ReturnKind == GeneratedErrorReturnKind.Sync ? string.Empty : "async ";
 600
 3601        builder.AppendLine($"    private static {asyncKeyword}global::System.Threading.Tasks.ValueTask<global::TeleFlow.
 3602        builder.AppendLine("        global::System.IServiceProvider services,");
 3603        builder.AppendLine("        object?[] arguments,");
 3604        builder.AppendLine("        global::System.Threading.CancellationToken cancellationToken)");
 3605        builder.AppendLine("    {");
 3606        builder.AppendLine(
 3607            $"        var handler = ({handler.HandlerTypeName})(services.GetService(typeof({handler.HandlerTypeName})) ?
 608
 3609        if (handler.ReturnKind == GeneratedErrorReturnKind.Sync)
 610        {
 1611            builder.Append("        return new global::System.Threading.Tasks.ValueTask<global::TeleFlow.Telegram.Telegr
 1612            AppendErrorInvocationArguments(builder, handler);
 1613            builder.AppendLine(");");
 614        }
 615        else
 616        {
 2617            builder.Append("        return await handler.");
 2618            AppendErrorInvocationArguments(builder, handler);
 2619            builder.AppendLine(".ConfigureAwait(false);");
 620        }
 621
 3622        builder.AppendLine("    }");
 3623        builder.AppendLine();
 3624    }
 625
 626    private static void AppendErrorInvocationArguments(
 627        StringBuilder builder,
 628        GeneratedErrorHandlerMethod handler)
 629    {
 3630        builder.Append(handler.MethodName);
 3631        builder.Append('(');
 632
 28633        for (int parameterIndex = 0; parameterIndex < handler.Parameters.Length; parameterIndex++)
 634        {
 11635            if (parameterIndex > 0)
 636            {
 8637                builder.Append(", ");
 638            }
 639
 11640            GeneratedErrorParameter parameter = handler.Parameters[parameterIndex];
 11641            builder.Append('(');
 11642            builder.Append(parameter.TypeName);
 11643            builder.Append($")arguments[{parameterIndex}]!");
 644        }
 645
 3646        builder.Append(')');
 3647    }
 648
 649    private static string ToNullableLiteral(string? value)
 650    {
 168651        return value is null ? "null" : ToLiteral(value);
 652    }
 653
 654    private static string ToTypeOfNullable(string? typeName)
 655    {
 47656        return typeName is null ? "null" : $"typeof({typeName})";
 657    }
 658
 659    private static string ToLiteral(string value)
 660    {
 276661        StringBuilder builder = new StringBuilder(value.Length + 2);
 276662        builder.Append('"');
 663
 11594664        foreach (char character in value)
 665        {
 5521666            builder.Append(character switch
 5521667            {
 1668                '\\' => "\\\\",
 0669                '"' => "\\\"",
 0670                '\r' => "\\r",
 0671                '\n' => "\\n",
 0672                '\t' => "\\t",
 5520673                _ => character.ToString()
 5521674            });
 675        }
 676
 276677        builder.Append('"');
 276678        return builder.ToString();
 679    }
 680
 681    private static string ToBoolLiteral(bool value)
 682    {
 91683        return value ? "true" : "false";
 684    }
 685
 686    private static string ToCommandPrefixModeLiteral(int value)
 687    {
 1688        return value switch
 1689        {
 0690            CommandPrefixModeRequired => "global::TeleFlow.Annotations.CommandPrefixMode.Required",
 1691            CommandPrefixModeOptional => "global::TeleFlow.Annotations.CommandPrefixMode.Optional",
 0692            CommandPrefixModeNoPrefix => "global::TeleFlow.Annotations.CommandPrefixMode.NoPrefix",
 0693            _ => $"(global::TeleFlow.Annotations.CommandPrefixMode){value}"
 1694        };
 695    }
 696
 697    private static string ToAttributeCreationExpression(AttributeData attribute)
 698    {
 1699        if (attribute.AttributeClass is null)
 700        {
 0701            return "null!";
 702        }
 703
 1704        StringBuilder builder = new StringBuilder();
 1705        builder.Append("new ");
 1706        builder.Append(attribute.AttributeClass.ToDisplayString(FullyQualifiedFormat));
 1707        builder.Append('(');
 708
 4709        for (int index = 0; index < attribute.ConstructorArguments.Length; index++)
 710        {
 1711            if (index > 0)
 712            {
 0713                builder.Append(", ");
 714            }
 715
 1716            builder.Append(ToAttributeArgumentExpression(attribute.ConstructorArguments[index]));
 717        }
 718
 1719        builder.Append(')');
 720
 1721        if (attribute.NamedArguments.Length > 0)
 722        {
 1723            builder.Append(" { ");
 724
 4725            for (int index = 0; index < attribute.NamedArguments.Length; index++)
 726            {
 1727                if (index > 0)
 728                {
 0729                    builder.Append(", ");
 730                }
 731
 1732                KeyValuePair<string, TypedConstant> argument = attribute.NamedArguments[index];
 1733                builder.Append(argument.Key);
 1734                builder.Append(" = ");
 1735                builder.Append(ToAttributeArgumentExpression(argument.Value));
 736            }
 737
 1738            builder.Append(" }");
 739        }
 740
 1741        return builder.ToString();
 742    }
 743
 744    private static string ToAttributeArgumentExpression(TypedConstant constant)
 745    {
 2746        if (constant.IsNull)
 747        {
 0748            return "null";
 749        }
 750
 2751        return constant.Kind switch
 2752        {
 2753            TypedConstantKind.Primitive => ToPrimitiveLiteral(constant.Value, constant.Type),
 0754            TypedConstantKind.Enum => ToEnumLiteral(constant),
 0755            TypedConstantKind.Type => constant.Value is ITypeSymbol type
 0756                ? $"typeof({type.ToDisplayString(FullyQualifiedFormat)})"
 0757                : "null",
 0758            TypedConstantKind.Array => ToArrayLiteral(constant),
 0759            _ => "null"
 2760        };
 761    }
 762
 763    private static string ToPrimitiveLiteral(
 764        object? value,
 765        ITypeSymbol? type)
 766    {
 2767        return value switch
 2768        {
 1769            string stringValue => ToLiteral(stringValue),
 0770            char charValue => ToCharLiteral(charValue),
 1771            bool boolValue => ToBoolLiteral(boolValue),
 0772            byte byteValue => byteValue.ToString(System.Globalization.CultureInfo.InvariantCulture),
 0773            sbyte sbyteValue => sbyteValue.ToString(System.Globalization.CultureInfo.InvariantCulture),
 0774            short shortValue => shortValue.ToString(System.Globalization.CultureInfo.InvariantCulture),
 0775            ushort ushortValue => ushortValue.ToString(System.Globalization.CultureInfo.InvariantCulture),
 0776            int intValue => intValue.ToString(System.Globalization.CultureInfo.InvariantCulture),
 0777            uint uintValue => uintValue.ToString(System.Globalization.CultureInfo.InvariantCulture) + "U",
 0778            long longValue => longValue.ToString(System.Globalization.CultureInfo.InvariantCulture) + "L",
 0779            ulong ulongValue => ulongValue.ToString(System.Globalization.CultureInfo.InvariantCulture) + "UL",
 0780            float floatValue => floatValue.ToString("R", System.Globalization.CultureInfo.InvariantCulture) + "F",
 0781            double doubleValue => doubleValue.ToString("R", System.Globalization.CultureInfo.InvariantCulture) + "D",
 0782            _ when type?.SpecialType == SpecialType.System_Object => "null",
 0783            _ => value?.ToString() ?? "null"
 2784        };
 785    }
 786
 787    private static string ToCharLiteral(char value)
 788    {
 0789        string escaped = value switch
 0790        {
 0791            '\'' => "\\'",
 0792            '\\' => "\\\\",
 0793            '\r' => "\\r",
 0794            '\n' => "\\n",
 0795            '\t' => "\\t",
 0796            _ => value.ToString()
 0797        };
 798
 0799        return $"'{escaped}'";
 800    }
 801
 802    private static string ToEnumLiteral(TypedConstant constant)
 803    {
 0804        var enumType = constant.Type?.ToDisplayString(FullyQualifiedFormat) ?? "int";
 0805        var value = constant.Value switch
 0806        {
 0807            ulong ulongValue => ulongValue.ToString(System.Globalization.CultureInfo.InvariantCulture) + "UL",
 0808            long longValue => longValue.ToString(System.Globalization.CultureInfo.InvariantCulture) + "L",
 0809            uint uintValue => uintValue.ToString(System.Globalization.CultureInfo.InvariantCulture) + "U",
 0810            _ => Convert.ToString(constant.Value, System.Globalization.CultureInfo.InvariantCulture) ?? "0"
 0811        };
 812
 0813        return $"({enumType}){value}";
 814    }
 815
 816    private static string ToArrayLiteral(TypedConstant constant)
 817    {
 0818        var elementType = constant.Type is IArrayTypeSymbol arrayType
 0819            ? arrayType.ElementType.ToDisplayString(FullyQualifiedFormat)
 0820            : "object";
 0821        StringBuilder builder = new StringBuilder();
 822
 0823        builder.Append("new ");
 0824        builder.Append(elementType);
 0825        builder.Append("[] { ");
 826
 0827        for (int index = 0; index < constant.Values.Length; index++)
 828        {
 0829            if (index > 0)
 830            {
 0831                builder.Append(", ");
 832            }
 833
 0834            builder.Append(ToAttributeArgumentExpression(constant.Values[index]));
 835        }
 836
 0837        builder.Append(" }");
 838
 0839        return builder.ToString();
 840    }
 841}

/_/src/TeleFlow.Generators/TelegramHandlerSourceGenerator.StateGroups.cs

#LineLine coverage
 1using System.Collections.Immutable;
 2using Microsoft.CodeAnalysis;
 3using Microsoft.CodeAnalysis.CSharp.Syntax;
 4
 5namespace TeleFlow.Generators;
 6
 7public sealed partial class TelegramHandlerSourceGenerator
 8{
 9    private static GeneratedStateGroup? GetStateGroup(GeneratorSyntaxContext context)
 10    {
 1911        if (context.SemanticModel.GetDeclaredSymbol(context.Node) is not INamedTypeSymbol type ||
 1912            !TryGetStateContainerPrefix(type, out string prefix))
 13        {
 1514            return null;
 15        }
 16
 417        ImmutableArray<GeneratedStateProperty> states = type
 418            .GetMembers()
 419            .OfType<IPropertySymbol>()
 820            .Where(static property => IsUsableStateProperty(property) && IsPartialStateProperty(property))
 621            .Select(static property => new GeneratedStateProperty(
 622                property.Name,
 623                GetStateSegment(property)))
 424            .OrderBy(static property => property.Name, StringComparer.Ordinal)
 425            .ToImmutableArray();
 26
 427        if (states.Length == 0)
 28        {
 029            return null;
 30        }
 31
 432        TypeDeclarationSyntax syntax = (TypeDeclarationSyntax)context.Node;
 33
 434        return new GeneratedStateGroup(
 435            TypeName: type.Name,
 436            TypeMetadataName: type.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat),
 437            Namespace: type.ContainingNamespace?.IsGlobalNamespace == false
 438                ? type.ContainingNamespace.ToDisplayString()
 439                : null,
 440            Accessibility: GetAccessibility(type.DeclaredAccessibility),
 441            IsStatic: type.IsStatic,
 442            Prefix: prefix,
 443            States: states,
 444            SourceSpanStart: syntax.SpanStart);
 45    }
 46
 47    private static string GetAccessibility(Accessibility accessibility)
 48    {
 449        return accessibility switch
 450        {
 451            Accessibility.Public => "public",
 052            Accessibility.Internal => "internal",
 053            _ => "internal"
 454        };
 55    }
 56}

Methods/Properties

.cctor()
Initialize(Microsoft.CodeAnalysis.IncrementalGeneratorInitializationContext)
GetHandlerMethod(Microsoft.CodeAnalysis.GeneratorSyntaxContext)
GetCallbackDataPayload(Microsoft.CodeAnalysis.GeneratorSyntaxContext)
GetClassBasedHandlerMethod(Microsoft.CodeAnalysis.GeneratorSyntaxContext)
GetErrorHandlerMethod(Microsoft.CodeAnalysis.GeneratorSyntaxContext)
TryBuildHandler(Microsoft.CodeAnalysis.IMethodSymbol,System.Boolean,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedHandlerMethod&)
TryBuildErrorHandler(Microsoft.CodeAnalysis.IMethodSymbol,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedErrorHandlerMethod&)
TryBuildErrorExceptionTypes(System.Collections.Generic.IReadOnlyList`1<Microsoft.CodeAnalysis.AttributeData>,System.Collections.Immutable.ImmutableArray`1<Microsoft.CodeAnalysis.ITypeSymbol>&,System.Collections.Immutable.ImmutableArray`1<System.String>&)
TryBuildErrorParameters(Microsoft.CodeAnalysis.IMethodSymbol,System.Collections.Immutable.ImmutableArray`1<Microsoft.CodeAnalysis.ITypeSymbol>,System.Collections.Immutable.ImmutableArray`1<TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedErrorParameter>&,System.String&)
TryBuildAutoAnswerCallback(Microsoft.CodeAnalysis.IMethodSymbol,System.Boolean,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedAutoAnswerCallback&)
BuildRoutes(Microsoft.CodeAnalysis.IMethodSymbol,System.Boolean,Microsoft.CodeAnalysis.ITypeSymbol&)
TryBuildFilters(Microsoft.CodeAnalysis.IMethodSymbol,System.Collections.Immutable.ImmutableArray`1<TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedFilter>&)
AppendFilters(System.Collections.Immutable.ImmutableArray`1/Builder<TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedFilter>,Microsoft.CodeAnalysis.ISymbol)
AppendChatTypeFilters(System.Collections.Immutable.ImmutableArray`1/Builder<TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedFilter>,Microsoft.CodeAnalysis.ISymbol,System.String,System.String)
AppendSenderIdFilters(System.Collections.Immutable.ImmutableArray`1/Builder<TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedFilter>,Microsoft.CodeAnalysis.ISymbol,System.String,System.String)
AppendMarkerFilter(System.Collections.Immutable.ImmutableArray`1/Builder<TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedFilter>,Microsoft.CodeAnalysis.ISymbol,TeleFlow.Generators.TelegramBuiltInFilterSpec)
SupportsGeneratedFilter(TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedFilter,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedHandlerKind)
TryPrepareCustomFilters(System.Collections.Immutable.ImmutableArray`1<TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedFilter>,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedHandlerKind,System.Collections.Immutable.ImmutableArray`1<TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedFilter>&)
TryResolveCustomFilterContextType(TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedFilter,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedHandlerKind,System.String&)
ToFullyQualifiedTypeName(System.String)
CanonicalizeChatUsername(System.String)
HasChatMemberTransitionAttributes(Microsoft.CodeAnalysis.IMethodSymbol)
TryBuildRoleRequirements(Microsoft.CodeAnalysis.IMethodSymbol,System.Collections.Immutable.ImmutableArray`1<System.Int32>&)
TryBuildChatMemberTransitions(Microsoft.CodeAnalysis.IMethodSymbol,System.Collections.Immutable.ImmutableArray`1<TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedChatMemberTransition>&)
ToMetadataRouteKind(TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedHandlerKind)
IsInvalidCustomFilterType(Microsoft.CodeAnalysis.ITypeSymbol)
TryGetTelegramFilterContextTypes(Microsoft.CodeAnalysis.ITypeSymbol,Microsoft.CodeAnalysis.ITypeSymbol,System.Collections.Immutable.ImmutableArray`1<System.String>&)
GetModuleName(Microsoft.CodeAnalysis.INamedTypeSymbol)
TryGetExpectedContextType(TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedHandlerKind,System.String&)
HasExactlyOneExpectedContext(Microsoft.CodeAnalysis.IMethodSymbol,System.String)
HasValidCallbackPayloadParameter(Microsoft.CodeAnalysis.IMethodSymbol,Microsoft.CodeAnalysis.ITypeSymbol)
IsInvalidCallbackPayloadType(Microsoft.CodeAnalysis.ITypeSymbol)
IsAccessibleFromGeneratedCode(Microsoft.CodeAnalysis.INamedTypeSymbol)
TryGetCallbackDataFields(Microsoft.CodeAnalysis.INamedTypeSymbol,System.Boolean&,System.Collections.Immutable.ImmutableArray`1<TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedCallbackDataField>&)
TryGetConstructorCallbackDataFields(Microsoft.CodeAnalysis.INamedTypeSymbol,Microsoft.CodeAnalysis.IMethodSymbol,System.Boolean&,System.Collections.Immutable.ImmutableArray`1<TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedCallbackDataField>&)
TryGetPropertyCallbackDataFields(Microsoft.CodeAnalysis.INamedTypeSymbol,System.Collections.Immutable.ImmutableArray`1<TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedCallbackDataField>&)
TryGetCallbackDataFieldKind(Microsoft.CodeAnalysis.ITypeSymbol,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedCallbackDataFieldKind&)
IsInvalidConcreteNamedType(Microsoft.CodeAnalysis.ITypeSymbol)
IsSupportedReturnType(Microsoft.CodeAnalysis.ITypeSymbol)
TryGetErrorReturnKind(Microsoft.CodeAnalysis.ITypeSymbol,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedErrorReturnKind&)
IsValidCommand(System.String)
GetConstructorString(Microsoft.CodeAnalysis.AttributeData)
TryGetPrefixes(Microsoft.CodeAnalysis.AttributeData,System.Collections.Immutable.ImmutableArray`1<System.String>&)
GetCommandPrefixMode(Microsoft.CodeAnalysis.AttributeData)
HasInvalidNoPrefixConfiguration(Microsoft.CodeAnalysis.AttributeData,System.Int32)
IsSupportedCommandPrefixMode(System.Int32)
HasNamedArgument(Microsoft.CodeAnalysis.AttributeData,System.String)
CommandPatternStartsWithPrefix(System.String,System.Collections.Immutable.ImmutableArray`1<System.String>)
GetNamedBool(Microsoft.CodeAnalysis.AttributeData,System.String,System.Boolean)
TryParseTemplateRouteValues(System.String,System.Collections.Immutable.ImmutableDictionary`2<System.String,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedRouteValue>&)
TryParseRegexRouteValues(System.String,System.Collections.Immutable.ImmutableDictionary`2<System.String,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedRouteValue>&)
ContainsBrace(System.String)
TryResolveRouteValueNames(Microsoft.CodeAnalysis.IMethodSymbol,System.Collections.Immutable.ImmutableArray`1<TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedRoute>,System.Collections.Immutable.ImmutableHashSet`1<System.String>&)
IsSupportedRouteValueParameterType(Microsoft.CodeAnalysis.ITypeSymbol)
IsNullableRouteValueParameter(Microsoft.CodeAnalysis.ITypeSymbol)
GetRouteValueParameterSpecialType(Microsoft.CodeAnalysis.ITypeSymbol)
RouteConstraintMatchesParameter(System.String,Microsoft.CodeAnalysis.ITypeSymbol)
GetRouteValueTypeName(TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedRouteValue)
BuildTextFilters(Microsoft.CodeAnalysis.IMethodSymbol,System.Boolean)
GetRouteAttributes(Microsoft.CodeAnalysis.IMethodSymbol,System.String,System.Boolean)
GetGenericRouteAttributes(Microsoft.CodeAnalysis.IMethodSymbol,System.String,System.Boolean)
IsClassBasedHandlerType(Microsoft.CodeAnalysis.INamedTypeSymbol)
IsClassBasedRouteCompatible(Microsoft.CodeAnalysis.INamedTypeSymbol,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedHandlerKind,Microsoft.CodeAnalysis.ITypeSymbol)
GetDeclaredHandleAsyncMethods(Microsoft.CodeAnalysis.INamedTypeSymbol)
TryGetCallbackHandlerPayloadType(Microsoft.CodeAnalysis.INamedTypeSymbol,Microsoft.CodeAnalysis.ITypeSymbol&)
IsAssignableTo(Microsoft.CodeAnalysis.INamedTypeSymbol,System.String)
IsDirectlyAssignableTo(Microsoft.CodeAnalysis.INamedTypeSymbol,System.String)
TryBuildStates(Microsoft.CodeAnalysis.IMethodSymbol,System.Collections.Immutable.ImmutableArray`1<System.String>&,System.String&)
GetGenericStateAttributes(Microsoft.CodeAnalysis.ISymbol)
TryResolveTypedState(Microsoft.CodeAnalysis.AttributeData,System.String&)
TryResolveSceneStep(Microsoft.CodeAnalysis.IMethodSymbol,Microsoft.CodeAnalysis.AttributeData,System.String&,System.String&)
TryGetStateContainerPrefix(Microsoft.CodeAnalysis.INamedTypeSymbol,System.String&)
TryGetStateGroupPrefix(Microsoft.CodeAnalysis.INamedTypeSymbol,System.String&)
TryGetScenePrefix(Microsoft.CodeAnalysis.INamedTypeSymbol,System.String&)
TryGetAttributeStringPrefix(Microsoft.CodeAnalysis.INamedTypeSymbol,System.String,System.String&)
GetStateSegment(Microsoft.CodeAnalysis.IPropertySymbol)
IsUsableStateProperty(Microsoft.CodeAnalysis.IPropertySymbol)
IsPartialStateProperty(Microsoft.CodeAnalysis.IPropertySymbol)
ToCamelCase(System.String)
BuildParameters(Microsoft.CodeAnalysis.IMethodSymbol,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedHandlerKind,System.String,Microsoft.CodeAnalysis.ITypeSymbol,System.Collections.Immutable.ImmutableHashSet`1<System.String>)
GetParameterKind(Microsoft.CodeAnalysis.IParameterSymbol,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedHandlerKind,System.String,Microsoft.CodeAnalysis.ITypeSymbol,System.Collections.Immutable.ImmutableHashSet`1<System.String>)
BuildErrorParameters(Microsoft.CodeAnalysis.IMethodSymbol)
GetErrorParameterKind(Microsoft.CodeAnalysis.IParameterSymbol)
IsExceptionType(Microsoft.CodeAnalysis.ITypeSymbol)
IsTelegramContextType(Microsoft.CodeAnalysis.ITypeSymbol)
IsAssignableFrom(Microsoft.CodeAnalysis.ITypeSymbol,Microsoft.CodeAnalysis.ITypeSymbol)
IsErrorRouteValueParameterType(Microsoft.CodeAnalysis.ITypeSymbol)
IsRouteValueParameterType(Microsoft.CodeAnalysis.ITypeSymbol)
GenerateSource(System.Collections.Generic.IReadOnlyList`1<TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedHandlerMethod>,System.Collections.Generic.IReadOnlyList`1<TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedErrorHandlerMethod>,System.Collections.Generic.IReadOnlyList`1<TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedCallbackDataPayload>)
AppendCallbackDataCodecRegistration(System.Text.StringBuilder,System.Collections.Generic.IReadOnlyList`1<TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedCallbackDataPayload>)
GenerateStateGroupsSource(System.Collections.Generic.IReadOnlyList`1<TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedStateGroup>)
AppendRegistration(System.Text.StringBuilder,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedHandlerMethod,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedRoute,System.Int32,System.Int32)
AppendAutoAnswerCallback(System.Text.StringBuilder,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedAutoAnswerCallback)
AppendFilter(System.Text.StringBuilder,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedFilter)
AppendErrorRegistration(System.Text.StringBuilder,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedErrorHandlerMethod,System.String,System.Int32,System.Int32)
AppendCallbackDataCodec(System.Text.StringBuilder,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedCallbackDataPayload,System.Int32)
GetCallbackDataPackExpression(TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedCallbackDataField)
GetCallbackDataUnpackExpression(TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedCallbackDataPayload,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedCallbackDataField,System.Int32)
AppendCallbackDataHelpers(System.Text.StringBuilder)
AppendInvoker(System.Text.StringBuilder,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedHandlerMethod,System.Int32)
AppendErrorInvoker(System.Text.StringBuilder,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedErrorHandlerMethod,System.Int32)
AppendErrorInvocationArguments(System.Text.StringBuilder,TeleFlow.Generators.TelegramHandlerSourceGenerator/GeneratedErrorHandlerMethod)
ToNullableLiteral(System.String)
ToTypeOfNullable(System.String)
ToLiteral(System.String)
ToBoolLiteral(System.Boolean)
ToCommandPrefixModeLiteral(System.Int32)
ToAttributeCreationExpression(Microsoft.CodeAnalysis.AttributeData)
ToAttributeArgumentExpression(Microsoft.CodeAnalysis.TypedConstant)
ToPrimitiveLiteral(System.Object,Microsoft.CodeAnalysis.ITypeSymbol)
ToCharLiteral(System.Char)
ToEnumLiteral(Microsoft.CodeAnalysis.TypedConstant)
ToArrayLiteral(Microsoft.CodeAnalysis.TypedConstant)
GetStateGroup(Microsoft.CodeAnalysis.GeneratorSyntaxContext)
GetAccessibility(Microsoft.CodeAnalysis.Accessibility)