< 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
69%
Covered lines: 30
Uncovered lines: 13
Coverable lines: 43
Total lines: 149
Line coverage: 69.7%
Branch coverage
66%
Covered branches: 28
Total branches: 42
Branch coverage: 66.6%
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%
FindClosedImplementations(...)0%156120%

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    {
 142025445        var result = new List<OpenDecoratorInfo>();
 46
 682817447        foreach (var attribute in typeSymbol.GetAttributes())
 48        {
 199383349            var attrClass = attribute.AttributeClass;
 199383350            if (attrClass is null)
 51                continue;
 52
 53            // Check if this is OpenDecoratorForAttribute
 199383354            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
 142025487        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    {
 4697        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
 16111        return false;
 112    }
 113
 114    /// <summary>
 115    /// Finds all closed implementations of an open generic interface in the given types.
 116    /// </summary>
 117    /// <param name="openGenericInterface">The open generic interface (e.g., IHandler{}).</param>
 118    /// <param name="allTypes">All types to search through.</param>
 119    /// <returns>A dictionary mapping closed interface types to their implementing types.</returns>
 120    public static Dictionary<INamedTypeSymbol, List<INamedTypeSymbol>> FindClosedImplementations(
 121        INamedTypeSymbol openGenericInterface,
 122        IEnumerable<INamedTypeSymbol> allTypes)
 123    {
 0124        var result = new Dictionary<INamedTypeSymbol, List<INamedTypeSymbol>>(SymbolEqualityComparer.Default);
 125
 0126        foreach (var type in allTypes)
 127        {
 0128            if (type.IsAbstract || type.TypeKind != TypeKind.Class)
 129                continue;
 130
 131            // Check all interfaces implemented by this type
 0132            foreach (var iface in type.AllInterfaces)
 133            {
 134                // Check if this interface is a closed version of the open generic
 0135                if (iface.OriginalDefinition.Equals(openGenericInterface, SymbolEqualityComparer.Default))
 136                {
 0137                    if (!result.TryGetValue(iface, out var implementors))
 138                    {
 0139                        implementors = new List<INamedTypeSymbol>();
 0140                        result[iface] = implementors;
 141                    }
 0142                    implementors.Add(type);
 143                }
 144            }
 145        }
 146
 0147        return result;
 148    }
 149}