< 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    {
 2520        var factoryName = $"I{factory.SimpleTypeName}Factory";
 21
 2522        builder.AppendLine("/// <summary>");
 2523        builder.AppendLine($"/// Factory interface for creating instances of <see cref=\"{factory.TypeName}\"/>.");
 2524        builder.AppendLine("/// </summary>");
 2525        builder.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.Generators\", \"1
 2526        builder.AppendLine($"public interface {factoryName}");
 2527        builder.AppendLine("{");
 28
 29        // Generate Create method for each constructor
 10630        foreach (var ctor in factory.Constructors)
 31        {
 2832            var runtimeParamList = string.Join(", ", ctor.RuntimeParameters.Select(p =>
 2833            {
 3434                var simpleTypeName = GeneratorHelpers.GetSimpleTypeName(p.TypeName);
 3435                return $"{p.TypeName} {p.ParameterName ?? GeneratorHelpers.ToCamelCase(simpleTypeName)}";
 2836            }));
 37
 2838            builder.AppendLine($"    /// <summary>Creates a new instance of {factory.SimpleTypeName}.</summary>");
 39
 40            // Add <param> tags for documented runtime parameters
 12441            foreach (var param in ctor.RuntimeParameters)
 42            {
 3443                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
 2851            builder.AppendLine($"    {factory.ReturnTypeName} Create({runtimeParamList});");
 52        }
 53
 2554        builder.AppendLine("}");
 2555    }
 56
 57    internal static void GenerateFactoryImplementation(StringBuilder builder, DiscoveredFactory factory, BreadcrumbWrite
 58    {
 2559        var factoryInterfaceName = $"I{factory.SimpleTypeName}Factory";
 2560        var factoryImplName = $"{factory.SimpleTypeName}Factory";
 61
 62        // Collect all unique injectable parameters across all constructors
 2563        var allInjectableParams = factory.Constructors
 2864            .SelectMany(c => c.InjectableParameters)
 2265            .GroupBy(p => p.TypeName)
 1966            .Select(g => g.First())
 2567            .ToList();
 68
 2569        builder.AppendLine("/// <summary>");
 2570        builder.AppendLine($"/// Factory implementation for creating instances of <see cref=\"{factory.TypeName}\"/>.");
 2571        builder.AppendLine("/// </summary>");
 2572        builder.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.Generators\", \"1
 2573        builder.AppendLine($"internal sealed class {factoryImplName} : {factoryInterfaceName}");
 2574        builder.AppendLine("{");
 75
 76        // Fields for injectable dependencies
 8877        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
 2583        builder.AppendLine();
 84
 85        // Constructor
 4486        var ctorParams = string.Join(", ", allInjectableParams.Select(p => $"{p.TypeName} {GeneratorHelpers.ToCamelCase(
 2587        builder.AppendLine($"    public {factoryImplName}({ctorParams})");
 2588        builder.AppendLine("    {");
 8889        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        }
 2595        builder.AppendLine("    }");
 2596        builder.AppendLine();
 97
 98        // Create methods for each constructor
 10699        foreach (var ctor in factory.Constructors)
 100        {
 28101            var runtimeParamList = string.Join(", ", ctor.RuntimeParameters.Select(p =>
 28102            {
 34103                var paramName = p.ParameterName ?? GeneratorHelpers.ToCamelCase(GeneratorHelpers.GetSimpleTypeName(p.Typ
 34104                return $"{p.TypeName} {paramName}";
 28105            }));
 106
 28107            builder.AppendLine($"    public {factory.ReturnTypeName} Create({runtimeParamList})");
 28108            builder.AppendLine("    {");
 28109            builder.Append($"        return new {factory.TypeName}(");
 110
 111            // Build constructor arguments - injectable first (from fields), then runtime
 28112            var allArgs = new List<string>();
 100113            foreach (var inj in ctor.InjectableParameters)
 114            {
 22115                var fieldName = "_" + GeneratorHelpers.ToCamelCase(GeneratorHelpers.GetSimpleTypeName(inj.TypeName));
 22116                allArgs.Add(fieldName);
 117            }
 124118            foreach (var rt in ctor.RuntimeParameters)
 119            {
 34120                var paramName = rt.ParameterName ?? GeneratorHelpers.ToCamelCase(GeneratorHelpers.GetSimpleTypeName(rt.T
 34121                allArgs.Add(paramName);
 122            }
 123
 28124            builder.Append(string.Join(", ", allArgs));
 28125            builder.AppendLine(");");
 28126            builder.AppendLine("    }");
 127        }
 128
 25129        builder.AppendLine("}");
 25130    }
 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)
 62135        var runtimeTypes = string.Join(", ", ctor.RuntimeParameters.Select(p => p.TypeName));
 28136        var funcType = $"Func<{runtimeTypes}, {factory.ReturnTypeName}>";
 137
 138        // Build the lambda
 28139        var runtimeParams = string.Join(", ", ctor.RuntimeParameters.Select(p =>
 62140            p.ParameterName ?? GeneratorHelpers.ToCamelCase(GeneratorHelpers.GetSimpleTypeName(p.TypeName))));
 141
 28142        builder.AppendLine($"{indent}services.AddSingleton<{funcType}>(sp =>");
 28143        builder.AppendLine($"{indent}    ({runtimeParams}) => new {factory.TypeName}(");
 144
 145        // Build constructor call arguments
 28146        var allArgs = new List<string>();
 100147        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        }
 124158        foreach (var rt in ctor.RuntimeParameters)
 159        {
 34160            allArgs.Add(rt.ParameterName ?? GeneratorHelpers.ToCamelCase(GeneratorHelpers.GetSimpleTypeName(rt.TypeName)
 161        }
 162
 168163        for (int i = 0; i < allArgs.Count; i++)
 164        {
 56165            var arg = allArgs[i];
 56166            var isLast = i == allArgs.Count - 1;
 56167            builder.AppendLine($"{indent}        {arg}{(isLast ? ")" : ",")}");
 168        }
 28169        builder.AppendLine($"{indent});");
 28170    }
 171}
 172