< Summary

Information
Class: NexusLabs.Needlr.Generators.CodeGen.ServiceCatalogCodeGenerator
Assembly: NexusLabs.Needlr.Generators
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators/CodeGen/ServiceCatalogCodeGenerator.cs
Line coverage
95%
Covered lines: 157
Uncovered lines: 7
Coverable lines: 164
Total lines: 295
Line coverage: 95.7%
Branch coverage
78%
Covered branches: 52
Total branches: 66
Branch coverage: 78.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
GenerateServiceCatalogSource(...)100%11100%
GenerateServicesProperty(...)85%202097.5%
GenerateDecoratorsProperty(...)75%44100%
GenerateHostedServicesProperty(...)75%1212100%
GenerateInterceptedServicesProperty(...)62.5%8890.9%
GenerateOptionsProperty(...)91.66%1212100%
GeneratePluginsProperty(...)100%44100%
GetRelativeSourcePath(...)50%17633.33%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Text;
 5
 6using NexusLabs.Needlr.Generators.Models;
 7
 8namespace NexusLabs.Needlr.Generators.CodeGen;
 9
 10/// <summary>
 11/// Generates the ServiceCatalog implementation from DiscoveryResult.
 12/// </summary>
 13internal static class ServiceCatalogCodeGenerator
 14{
 15    internal static string GenerateServiceCatalogSource(
 16        DiscoveryResult discoveryResult,
 17        string assemblyName,
 18        string? projectDirectory,
 19        BreadcrumbWriter breadcrumbs)
 20    {
 43821        var builder = new StringBuilder();
 43822        var safeAssemblyName = GeneratorHelpers.SanitizeIdentifier(assemblyName);
 43823        var timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss");
 24
 43825        builder.AppendLine("// <auto-generated/>");
 43826        builder.AppendLine("// Needlr Service Catalog");
 43827        builder.AppendLine($"// Generated: {timestamp} UTC");
 43828        builder.AppendLine();
 43829        builder.AppendLine("#nullable enable");
 43830        builder.AppendLine();
 43831        builder.AppendLine($"namespace {safeAssemblyName}.Generated;");
 43832        builder.AppendLine();
 33
 43834        breadcrumbs.WriteInlineComment(builder, "", $"ServiceCatalog: {discoveryResult.InjectableTypes.Count} services, 
 35
 43836        builder.AppendLine("/// <summary>");
 43837        builder.AppendLine("/// Compile-time service catalog containing all discovered registrations.");
 43838        builder.AppendLine("/// </summary>");
 43839        builder.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.Generators\", \"1
 43840        builder.AppendLine($"public sealed class ServiceCatalog : global::NexusLabs.Needlr.Catalog.IServiceCatalog");
 43841        builder.AppendLine("{");
 43842        builder.AppendLine($"    /// <inheritdoc />");
 43843        builder.AppendLine($"    public string AssemblyName => \"{GeneratorHelpers.EscapeStringLiteral(assemblyName)}\";
 43844        builder.AppendLine();
 43845        builder.AppendLine($"    /// <inheritdoc />");
 43846        builder.AppendLine($"    public string GeneratedAt => \"{timestamp}\";");
 43847        builder.AppendLine();
 48
 49        // Services
 43850        GenerateServicesProperty(builder, discoveryResult.InjectableTypes, projectDirectory);
 51
 52        // Decorators
 43853        GenerateDecoratorsProperty(builder, discoveryResult.Decorators, projectDirectory);
 54
 55        // Hosted Services
 43856        GenerateHostedServicesProperty(builder, discoveryResult.HostedServices, projectDirectory);
 57
 58        // Intercepted Services
 43859        GenerateInterceptedServicesProperty(builder, discoveryResult.InterceptedServices, projectDirectory);
 60
 61        // Options
 43862        GenerateOptionsProperty(builder, discoveryResult.Options, projectDirectory);
 63
 64        // Plugins
 43865        GeneratePluginsProperty(builder, discoveryResult.PluginTypes, projectDirectory);
 66
 43867        builder.AppendLine("}");
 68
 43869        return builder.ToString();
 70    }
 71
 72    private static void GenerateServicesProperty(
 73        StringBuilder builder,
 74        IReadOnlyList<DiscoveredType> types,
 75        string? projectDirectory)
 76    {
 43877        builder.AppendLine($"    /// <inheritdoc />");
 43878        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 43879        builder.AppendLine("    {");
 80
 46307481        foreach (var type in types)
 82        {
 23109983            var shortName = GeneratorHelpers.GetShortTypeName(type.TypeName);
 23109984            var lifetimeStr = type.Lifetime switch
 23109985            {
 23107186                GeneratorLifetime.Singleton => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Singleton",
 2387                GeneratorLifetime.Scoped => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Scoped",
 588                GeneratorLifetime.Transient => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Transient",
 089                _ => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Scoped"
 23109990            };
 23109991            var sourceFilePath = GetRelativeSourcePath(type.SourceFilePath, projectDirectory);
 23109992            var sourceFilePathLiteral = sourceFilePath != null
 23109993                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 23109994                : "null";
 95
 96            // Build interfaces array (for backwards compat)
 23134497            var interfacesArray = $"new string[] {{ {string.Join(", ", type.InterfaceNames.Select(i => $"\"{GeneratorHel
 98
 99            // Build interface entries array with locations
 231099100            var interfaceEntriesBuilder = new StringBuilder();
 231099101            interfaceEntriesBuilder.Append("new global::NexusLabs.Needlr.Catalog.InterfaceEntry[] { ");
 462688102            foreach (var ifaceInfo in type.InterfaceInfos)
 103            {
 245104                var ifaceFilePath = GetRelativeSourcePath(ifaceInfo.SourceFilePath, projectDirectory);
 245105                var ifaceFilePathLiteral = ifaceFilePath != null
 245106                    ? $"\"{GeneratorHelpers.EscapeStringLiteral(ifaceFilePath)}\""
 245107                    : "null";
 245108                interfaceEntriesBuilder.Append($"new global::NexusLabs.Needlr.Catalog.InterfaceEntry(\"{GeneratorHelpers
 109            }
 231099110            interfaceEntriesBuilder.Append('}');
 111
 112            // Build constructor parameters array
 231099113            var constructorParamsBuilder = new StringBuilder();
 231099114            constructorParamsBuilder.Append("new global::NexusLabs.Needlr.Catalog.ConstructorParameterEntry[] { ");
 571564115            foreach (var param in type.ConstructorParameters)
 116            {
 54683117                var serviceKeyLiteral = param.ServiceKey != null
 54683118                    ? $"\"{GeneratorHelpers.EscapeStringLiteral(param.ServiceKey)}\""
 54683119                    : "null";
 54683120                var paramName = param.ParameterName ?? "unknown";
 54683121                constructorParamsBuilder.Append($"new global::NexusLabs.Needlr.Catalog.ConstructorParameterEntry(\"{Gene
 122            }
 231099123            constructorParamsBuilder.Append('}');
 124
 125            // Build service keys array
 231104126            var serviceKeysArray = $"new string[] {{ {string.Join(", ", type.ServiceKeys.Select(k => $"\"{GeneratorHelpe
 127
 231099128            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.ServiceCatalogEntry(\"{GeneratorHelpers.Es
 129        }
 130
 438131        builder.AppendLine("    };");
 438132        builder.AppendLine();
 438133    }
 134
 135    private static void GenerateDecoratorsProperty(
 136        StringBuilder builder,
 137        IReadOnlyList<DiscoveredDecorator> decorators,
 138        string? projectDirectory)
 139    {
 438140        builder.AppendLine($"    /// <inheritdoc />");
 438141        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 438142        builder.AppendLine("    {");
 143
 928144        foreach (var decorator in decorators)
 145        {
 26146            var shortName = GeneratorHelpers.GetShortTypeName(decorator.DecoratorTypeName);
 26147            var sourceFilePath = GetRelativeSourcePath(decorator.SourceFilePath, projectDirectory);
 26148            var sourceFilePathLiteral = sourceFilePath != null
 26149                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 26150                : "null";
 151
 26152            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.DecoratorCatalogEntry(\"{GeneratorHelpers.
 153        }
 154
 438155        builder.AppendLine("    };");
 438156        builder.AppendLine();
 438157    }
 158
 159    private static void GenerateHostedServicesProperty(
 160        StringBuilder builder,
 161        IReadOnlyList<DiscoveredHostedService> hostedServices,
 162        string? projectDirectory)
 163    {
 438164        builder.AppendLine($"    /// <inheritdoc />");
 438165        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 438166        builder.AppendLine("    {");
 167
 890168        foreach (var hosted in hostedServices)
 169        {
 7170            var shortName = GeneratorHelpers.GetShortTypeName(hosted.TypeName);
 7171            var sourceFilePath = GetRelativeSourcePath(hosted.SourceFilePath, projectDirectory);
 7172            var sourceFilePathLiteral = sourceFilePath != null
 7173                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 7174                : "null";
 175
 176            // Build constructor parameters array
 7177            var constructorParamsBuilder = new StringBuilder();
 7178            constructorParamsBuilder.Append("new global::NexusLabs.Needlr.Catalog.ConstructorParameterEntry[] { ");
 16179            foreach (var param in hosted.ConstructorParameters)
 180            {
 1181                var serviceKeyLiteral = param.ServiceKey != null
 1182                    ? $"\"{GeneratorHelpers.EscapeStringLiteral(param.ServiceKey)}\""
 1183                    : "null";
 1184                var paramName = param.ParameterName ?? "unknown";
 1185                constructorParamsBuilder.Append($"new global::NexusLabs.Needlr.Catalog.ConstructorParameterEntry(\"{Gene
 186            }
 7187            constructorParamsBuilder.Append('}');
 188
 7189            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.HostedServiceCatalogEntry(\"{GeneratorHelp
 190        }
 191
 438192        builder.AppendLine("    };");
 438193        builder.AppendLine();
 438194    }
 195
 196    private static void GenerateInterceptedServicesProperty(
 197        StringBuilder builder,
 198        IReadOnlyList<DiscoveredInterceptedService> interceptedServices,
 199        string? projectDirectory)
 200    {
 438201        builder.AppendLine($"    /// <inheritdoc />");
 438202        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 438203        builder.AppendLine("    {");
 204
 902205        foreach (var intercepted in interceptedServices)
 206        {
 13207            var shortName = GeneratorHelpers.GetShortTypeName(intercepted.TypeName);
 13208            var lifetimeStr = intercepted.Lifetime switch
 13209            {
 2210                GeneratorLifetime.Singleton => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Singleton",
 11211                GeneratorLifetime.Scoped => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Scoped",
 0212                GeneratorLifetime.Transient => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Transient",
 0213                _ => "global::NexusLabs.Needlr.Catalog.ServiceCatalogLifetime.Scoped"
 13214            };
 13215            var sourceFilePath = GetRelativeSourcePath(intercepted.SourceFilePath, projectDirectory);
 13216            var sourceFilePathLiteral = sourceFilePath != null
 13217                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 13218                : "null";
 219
 26220            var interfacesArray = $"new string[] {{ {string.Join(", ", intercepted.InterfaceNames.Select(i => $"\"{Gener
 29221            var interceptorsArray = $"new string[] {{ {string.Join(", ", intercepted.AllInterceptorTypeNames.Select(i =>
 222
 13223            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.InterceptedServiceCatalogEntry(\"{Generato
 224        }
 225
 438226        builder.AppendLine("    };");
 438227        builder.AppendLine();
 438228    }
 229
 230    private static void GenerateOptionsProperty(
 231        StringBuilder builder,
 232        IReadOnlyList<DiscoveredOptions> options,
 233        string? projectDirectory)
 234    {
 438235        builder.AppendLine($"    /// <inheritdoc />");
 438236        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 438237        builder.AppendLine("    {");
 238
 1206239        foreach (var opt in options)
 240        {
 165241            var shortName = GeneratorHelpers.GetShortTypeName(opt.TypeName);
 165242            var sourceFilePath = GetRelativeSourcePath(opt.SourceFilePath, projectDirectory);
 165243            var sourceFilePathLiteral = sourceFilePath != null
 165244                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 165245                : "null";
 165246            var nameLiteral = opt.Name != null
 165247                ? $"\"{GeneratorHelpers.EscapeStringLiteral(opt.Name)}\""
 165248                : "null";
 249
 165250            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.OptionsCatalogEntry(\"{GeneratorHelpers.Es
 251        }
 252
 438253        builder.AppendLine("    };");
 438254        builder.AppendLine();
 438255    }
 256
 257    private static void GeneratePluginsProperty(
 258        StringBuilder builder,
 259        IReadOnlyList<DiscoveredPlugin> plugins,
 260        string? projectDirectory)
 261    {
 438262        builder.AppendLine($"    /// <inheritdoc />");
 438263        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 438264        builder.AppendLine("    {");
 265
 3642266        foreach (var plugin in plugins)
 267        {
 1383268            var shortName = GeneratorHelpers.GetShortTypeName(plugin.TypeName);
 1383269            var sourceFilePath = GetRelativeSourcePath(plugin.SourceFilePath, projectDirectory);
 1383270            var sourceFilePathLiteral = sourceFilePath != null
 1383271                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 1383272                : "null";
 273
 2771274            var interfacesArray = $"new string[] {{ {string.Join(", ", plugin.InterfaceNames.Select(i => $"\"{GeneratorH
 275
 1383276            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.PluginCatalogEntry(\"{GeneratorHelpers.Esc
 277        }
 278
 438279        builder.AppendLine("    };");
 438280    }
 281
 282    private static string? GetRelativeSourcePath(string? fullPath, string? projectDirectory)
 283    {
 232938284        if (fullPath == null || projectDirectory == null)
 232938285            return fullPath;
 286
 0287        if (fullPath.StartsWith(projectDirectory, StringComparison.OrdinalIgnoreCase))
 288        {
 0289            var relative = fullPath.Substring(projectDirectory.Length);
 0290            return relative.TrimStart('/', '\\');
 291        }
 292
 0293        return fullPath;
 294    }
 295}