< Summary

Information
Class: NexusLabs.Needlr.Generators.CodeGen.FactoryCodeGenerator
Assembly: NexusLabs.Needlr.Generators
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators/CodeGen/FactoryCodeGenerator.cs
Line coverage
98%
Covered lines: 87
Uncovered lines: 1
Coverable lines: 88
Total lines: 172
Line coverage: 98.8%
Branch coverage
81%
Covered branches: 31
Total branches: 38
Branch coverage: 81.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
GenerateFactoryInterface(...)80%1010100%
GenerateFactoryImplementation(...)85.71%1414100%
GenerateFuncRegistration(...)78.57%141494.73%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators/CodeGen/FactoryCodeGenerator.cs

#LineLine coverage
 1// Copyright (c) NexusLabs. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System;
 5using System.Collections.Generic;
 6using System.Linq;
 7using System.Text;
 8
 9using NexusLabs.Needlr.Generators.Models;
 10
 11namespace NexusLabs.Needlr.Generators.CodeGen;
 12
 13/// <summary>
 14/// Generates factory interfaces and implementations for [GenerateFactory] attributed types.
 15/// </summary>
 16internal static class FactoryCodeGenerator
 17{
 18    internal static void GenerateFactoryInterface(StringBuilder builder, DiscoveredFactory factory, BreadcrumbWriter bre
 19    {
 2220        var factoryName = $"I{factory.SimpleTypeName}Factory";
 21
 2222        builder.AppendLine("/// <summary>");
 2223        builder.AppendLine($"/// Factory interface for creating instances of <see cref=\"{factory.TypeName}\"/>.");
 2224        builder.AppendLine("/// </summary>");
 2225        builder.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.Generators\", \"1
 2226        builder.AppendLine($"public interface {factoryName}");
 2227        builder.AppendLine("{");
 28
 29        // Generate Create method for each constructor
 9430        foreach (var ctor in factory.Constructors)
 31        {
 2532            var runtimeParamList = string.Join(", ", ctor.RuntimeParameters.Select(p =>
 2533            {
 3134                var simpleTypeName = GeneratorHelpers.GetSimpleTypeName(p.TypeName);
 3135                return $"{p.TypeName} {p.ParameterName ?? GeneratorHelpers.ToCamelCase(simpleTypeName)}";
 2536            }));
 37
 2538            builder.AppendLine($"    /// <summary>Creates a new instance of {factory.SimpleTypeName}.</summary>");
 39
 40            // Add <param> tags for documented runtime parameters
 11241            foreach (var param in ctor.RuntimeParameters)
 42            {
 3143                if (!string.IsNullOrWhiteSpace(param.DocumentationComment))
 44                {
 745                    var paramName = param.ParameterName ?? GeneratorHelpers.ToCamelCase(GeneratorHelpers.GetSimpleTypeNa
 746                    var escapedDoc = GeneratorHelpers.EscapeXmlContent(param.DocumentationComment!);
 747                    builder.AppendLine($"    /// <param name=\"{paramName}\">{escapedDoc}</param>");
 48                }
 49            }
 50
 2551            builder.AppendLine($"    {factory.ReturnTypeName} Create({runtimeParamList});");
 52        }
 53
 2254        builder.AppendLine("}");
 2255    }
 56
 57    internal static void GenerateFactoryImplementation(StringBuilder builder, DiscoveredFactory factory, BreadcrumbWrite
 58    {
 2259        var factoryInterfaceName = $"I{factory.SimpleTypeName}Factory";
 2260        var factoryImplName = $"{factory.SimpleTypeName}Factory";
 61
 62        // Collect all unique injectable parameters across all constructors
 2263        var allInjectableParams = factory.Constructors
 2564            .SelectMany(c => c.InjectableParameters)
 2265            .GroupBy(p => p.TypeName)
 1966            .Select(g => g.First())
 2267            .ToList();
 68
 2269        builder.AppendLine("/// <summary>");
 2270        builder.AppendLine($"/// Factory implementation for creating instances of <see cref=\"{factory.TypeName}\"/>.");
 2271        builder.AppendLine("/// </summary>");
 2272        builder.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.Generators\", \"1
 2273        builder.AppendLine($"internal sealed class {factoryImplName} : {factoryInterfaceName}");
 2274        builder.AppendLine("{");
 75
 76        // Fields for injectable dependencies
 8277        foreach (var param in allInjectableParams)
 78        {
 1979            var fieldName = "_" + GeneratorHelpers.ToCamelCase(GeneratorHelpers.GetSimpleTypeName(param.TypeName));
 1980            builder.AppendLine($"    private readonly {param.TypeName} {fieldName};");
 81        }
 82
 2283        builder.AppendLine();
 84
 85        // Constructor
 4186        var ctorParams = string.Join(", ", allInjectableParams.Select(p => $"{p.TypeName} {GeneratorHelpers.ToCamelCase(
 2287        builder.AppendLine($"    public {factoryImplName}({ctorParams})");
 2288        builder.AppendLine("    {");
 8289        foreach (var param in allInjectableParams)
 90        {
 1991            var fieldName = "_" + GeneratorHelpers.ToCamelCase(GeneratorHelpers.GetSimpleTypeName(param.TypeName));
 1992            var paramName = GeneratorHelpers.ToCamelCase(GeneratorHelpers.GetSimpleTypeName(param.TypeName));
 1993            builder.AppendLine($"        {fieldName} = {paramName};");
 94        }
 2295        builder.AppendLine("    }");
 2296        builder.AppendLine();
 97
 98        // Create methods for each constructor
 9499        foreach (var ctor in factory.Constructors)
 100        {
 25101            var runtimeParamList = string.Join(", ", ctor.RuntimeParameters.Select(p =>
 25102            {
 31103                var paramName = p.ParameterName ?? GeneratorHelpers.ToCamelCase(GeneratorHelpers.GetSimpleTypeName(p.Typ
 31104                return $"{p.TypeName} {paramName}";
 25105            }));
 106
 25107            builder.AppendLine($"    public {factory.ReturnTypeName} Create({runtimeParamList})");
 25108            builder.AppendLine("    {");
 25109            builder.Append($"        return new {factory.TypeName}(");
 110
 111            // Build constructor arguments - injectable first (from fields), then runtime
 25112            var allArgs = new List<string>();
 94113            foreach (var inj in ctor.InjectableParameters)
 114            {
 22115                var fieldName = "_" + GeneratorHelpers.ToCamelCase(GeneratorHelpers.GetSimpleTypeName(inj.TypeName));
 22116                allArgs.Add(fieldName);
 117            }
 112118            foreach (var rt in ctor.RuntimeParameters)
 119            {
 31120                var paramName = rt.ParameterName ?? GeneratorHelpers.ToCamelCase(GeneratorHelpers.GetSimpleTypeName(rt.T
 31121                allArgs.Add(paramName);
 122            }
 123
 25124            builder.Append(string.Join(", ", allArgs));
 25125            builder.AppendLine(");");
 25126            builder.AppendLine("    }");
 127        }
 128
 22129        builder.AppendLine("}");
 22130    }
 131
 132    internal static void GenerateFuncRegistration(StringBuilder builder, DiscoveredFactory factory, FactoryDiscoveryHelp
 133    {
 134        // Build Func<TRuntime..., TReturn> type - uses ReturnTypeName (interface if generic attribute used)
 56135        var runtimeTypes = string.Join(", ", ctor.RuntimeParameters.Select(p => p.TypeName));
 25136        var funcType = $"Func<{runtimeTypes}, {factory.ReturnTypeName}>";
 137
 138        // Build the lambda
 25139        var runtimeParams = string.Join(", ", ctor.RuntimeParameters.Select(p =>
 56140            p.ParameterName ?? GeneratorHelpers.ToCamelCase(GeneratorHelpers.GetSimpleTypeName(p.TypeName))));
 141
 25142        builder.AppendLine($"{indent}services.AddSingleton<{funcType}>(sp =>");
 25143        builder.AppendLine($"{indent}    ({runtimeParams}) => new {factory.TypeName}(");
 144
 145        // Build constructor call arguments
 25146        var allArgs = new List<string>();
 94147        foreach (var inj in ctor.InjectableParameters)
 148        {
 22149            if (inj.IsKeyed)
 150            {
 0151                allArgs.Add($"sp.GetRequiredKeyedService<{inj.TypeName}>(\"{GeneratorHelpers.EscapeStringLiteral(inj.Ser
 152            }
 153            else
 154            {
 22155                allArgs.Add($"sp.GetRequiredService<{inj.TypeName}>()");
 156            }
 157        }
 112158        foreach (var rt in ctor.RuntimeParameters)
 159        {
 31160            allArgs.Add(rt.ParameterName ?? GeneratorHelpers.ToCamelCase(GeneratorHelpers.GetSimpleTypeName(rt.TypeName)
 161        }
 162
 156163        for (int i = 0; i < allArgs.Count; i++)
 164        {
 53165            var arg = allArgs[i];
 53166            var isLast = i == allArgs.Count - 1;
 53167            builder.AppendLine($"{indent}        {arg}{(isLast ? ")" : ",")}");
 168        }
 25169        builder.AppendLine($"{indent});");
 25170    }
 171}
 172