< Summary

Information
Class: NexusLabs.Needlr.Generators.OpenDecoratorDiscoveryHelper
Assembly: NexusLabs.Needlr.Generators
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators/OpenDecoratorDiscoveryHelper.cs
Line coverage
90%
Covered lines: 30
Uncovered lines: 3
Coverable lines: 33
Total lines: 114
Line coverage: 90.9%
Branch coverage
93%
Covered branches: 28
Total branches: 30
Branch coverage: 93.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_DecoratorType()100%11100%
get_OpenGenericInterface()100%11100%
get_Order()100%11100%
GetOpenDecoratorForAttributes(...)100%2222100%
HasOpenDecoratorForAttribute(...)75%11862.5%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators/OpenDecoratorDiscoveryHelper.cs

#LineLine coverage
 1using Microsoft.CodeAnalysis;
 2
 3namespace NexusLabs.Needlr.Generators;
 4
 5/// <summary>
 6/// Helper for discovering open generic decorator types from Roslyn symbols.
 7/// </summary>
 8internal static class OpenDecoratorDiscoveryHelper
 9{
 10    private const string OpenDecoratorForAttributeName = "OpenDecoratorForAttribute";
 11    private const string OpenDecoratorForAttributeFullName = "NexusLabs.Needlr.Generators.OpenDecoratorForAttribute";
 12
 13    /// <summary>
 14    /// Result of open generic decorator discovery.
 15    /// </summary>
 16    public readonly struct OpenDecoratorInfo
 17    {
 18        public OpenDecoratorInfo(
 19            INamedTypeSymbol decoratorType,
 20            INamedTypeSymbol openGenericInterface,
 21            int order)
 22        {
 723            DecoratorType = decoratorType;
 724            OpenGenericInterface = openGenericInterface;
 725            Order = order;
 726        }
 27
 28        /// <summary>The open generic decorator type symbol (e.g., LoggingDecorator{T}).</summary>
 729        public INamedTypeSymbol DecoratorType { get; }
 30
 31        /// <summary>The open generic interface being decorated (e.g., IHandler{T}).</summary>
 732        public INamedTypeSymbol OpenGenericInterface { get; }
 33
 34        /// <summary>Order in which decorator wraps (lower = outer).</summary>
 735        public int Order { get; }
 36    }
 37
 38    /// <summary>
 39    /// Gets all OpenDecoratorFor attributes applied to a type.
 40    /// </summary>
 41    /// <param name="typeSymbol">The type symbol to check.</param>
 42    /// <returns>A list of open decorator info for each OpenDecoratorFor attribute found.</returns>
 43    public static IReadOnlyList<OpenDecoratorInfo> GetOpenDecoratorForAttributes(INamedTypeSymbol typeSymbol)
 44    {
 158186545        var result = new List<OpenDecoratorInfo>();
 46
 760089047        foreach (var attribute in typeSymbol.GetAttributes())
 48        {
 221858049            var attrClass = attribute.AttributeClass;
 221858050            if (attrClass is null)
 51                continue;
 52
 53            // Check if this is OpenDecoratorForAttribute
 221858054            if (attrClass.Name != OpenDecoratorForAttributeName)
 55                continue;
 56
 757            var fullName = attrClass.ToDisplayString();
 758            if (fullName != OpenDecoratorForAttributeFullName)
 59                continue;
 60
 61            // Get the type argument from the constructor (first positional argument)
 762            if (attribute.ConstructorArguments.Length < 1)
 63                continue;
 64
 765            var typeArg = attribute.ConstructorArguments[0];
 766            if (typeArg.Value is not INamedTypeSymbol openGenericInterface)
 67                continue;
 68
 69            // Validate it's an open generic interface
 770            if (!openGenericInterface.IsUnboundGenericType || openGenericInterface.TypeKind != TypeKind.Interface)
 71                continue;
 72
 73            // Get the Order property value
 774            int order = 0;
 1775            foreach (var namedArg in attribute.NamedArguments)
 76            {
 377                if (namedArg.Key == "Order" && namedArg.Value.Value is int orderValue)
 78                {
 379                    order = orderValue;
 380                    break;
 81                }
 82            }
 83
 784            result.Add(new OpenDecoratorInfo(typeSymbol, openGenericInterface, order));
 85        }
 86
 158186587        return result;
 88    }
 89
 90    /// <summary>
 91    /// Checks if a type has any OpenDecoratorFor attributes.
 92    /// </summary>
 93    /// <param name="typeSymbol">The type symbol to check.</param>
 94    /// <returns>True if the type has at least one OpenDecoratorFor attribute.</returns>
 95    public static bool HasOpenDecoratorForAttribute(INamedTypeSymbol typeSymbol)
 96    {
 5097        foreach (var attribute in typeSymbol.GetAttributes())
 98        {
 799            var attrClass = attribute.AttributeClass;
 7100            if (attrClass is null)
 101                continue;
 102
 7103            if (attrClass.Name == OpenDecoratorForAttributeName)
 104            {
 0105                var fullName = attrClass.ToDisplayString();
 0106                if (fullName == OpenDecoratorForAttributeFullName)
 0107                    return true;
 108            }
 109        }
 110
 18111        return false;
 112    }
 113
 114}