< 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    {
 45521        var builder = new StringBuilder();
 45522        var safeAssemblyName = GeneratorHelpers.SanitizeIdentifier(assemblyName);
 45523        var timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss");
 24
 45525        builder.AppendLine("// <auto-generated/>");
 45526        builder.AppendLine("// Needlr Service Catalog");
 45527        builder.AppendLine($"// Generated: {timestamp} UTC");
 45528        builder.AppendLine();
 45529        builder.AppendLine("#nullable enable");
 45530        builder.AppendLine();
 45531        builder.AppendLine($"namespace {safeAssemblyName}.Generated;");
 45532        builder.AppendLine();
 33
 45534        breadcrumbs.WriteInlineComment(builder, "", $"ServiceCatalog: {discoveryResult.InjectableTypes.Count} services, 
 35
 45536        builder.AppendLine("/// <summary>");
 45537        builder.AppendLine("/// Compile-time service catalog containing all discovered registrations.");
 45538        builder.AppendLine("/// </summary>");
 45539        builder.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.Generators\", \"1
 45540        builder.AppendLine($"public sealed class ServiceCatalog : global::NexusLabs.Needlr.Catalog.IServiceCatalog");
 45541        builder.AppendLine("{");
 45542        builder.AppendLine($"    /// <inheritdoc />");
 45543        builder.AppendLine($"    public string AssemblyName => \"{GeneratorHelpers.EscapeStringLiteral(assemblyName)}\";
 45544        builder.AppendLine();
 45545        builder.AppendLine($"    /// <inheritdoc />");
 45546        builder.AppendLine($"    public string GeneratedAt => \"{timestamp}\";");
 45547        builder.AppendLine();
 48
 49        // Services
 45550        GenerateServicesProperty(builder, discoveryResult.InjectableTypes, projectDirectory);
 51
 52        // Decorators
 45553        GenerateDecoratorsProperty(builder, discoveryResult.Decorators, projectDirectory);
 54
 55        // Hosted Services
 45556        GenerateHostedServicesProperty(builder, discoveryResult.HostedServices, projectDirectory);
 57
 58        // Intercepted Services
 45559        GenerateInterceptedServicesProperty(builder, discoveryResult.InterceptedServices, projectDirectory);
 60
 61        // Options
 45562        GenerateOptionsProperty(builder, discoveryResult.Options, projectDirectory);
 63
 64        // Plugins
 45565        GeneratePluginsProperty(builder, discoveryResult.PluginTypes, projectDirectory);
 66
 45567        builder.AppendLine("}");
 68
 45569        return builder.ToString();
 70    }
 71
 72    private static void GenerateServicesProperty(
 73        StringBuilder builder,
 74        IReadOnlyList<DiscoveredType> types,
 75        string? projectDirectory)
 76    {
 45577        builder.AppendLine($"    /// <inheritdoc />");
 45578        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 45579        builder.AppendLine("    {");
 80
 46798681        foreach (var type in types)
 82        {
 23353883            var shortName = GeneratorHelpers.GetShortTypeName(type.TypeName);
 23353884            var lifetimeStr = type.Lifetime switch
 23353885            {
 23351086                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"
 23353890            };
 23353891            var sourceFilePath = GetRelativeSourcePath(type.SourceFilePath, projectDirectory);
 23353892            var sourceFilePathLiteral = sourceFilePath != null
 23353893                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 23353894                : "null";
 95
 96            // Build interfaces array (for backwards compat)
 23379197            var interfacesArray = $"new string[] {{ {string.Join(", ", type.InterfaceNames.Select(i => $"\"{GeneratorHel
 98
 99            // Build interface entries array with locations
 233538100            var interfaceEntriesBuilder = new StringBuilder();
 233538101            interfaceEntriesBuilder.Append("new global::NexusLabs.Needlr.Catalog.InterfaceEntry[] { ");
 467582102            foreach (var ifaceInfo in type.InterfaceInfos)
 103            {
 253104                var ifaceFilePath = GetRelativeSourcePath(ifaceInfo.SourceFilePath, projectDirectory);
 253105                var ifaceFilePathLiteral = ifaceFilePath != null
 253106                    ? $"\"{GeneratorHelpers.EscapeStringLiteral(ifaceFilePath)}\""
 253107                    : "null";
 253108                interfaceEntriesBuilder.Append($"new global::NexusLabs.Needlr.Catalog.InterfaceEntry(\"{GeneratorHelpers
 109            }
 233538110            interfaceEntriesBuilder.Append('}');
 111
 112            // Build constructor parameters array
 233538113            var constructorParamsBuilder = new StringBuilder();
 233538114            constructorParamsBuilder.Append("new global::NexusLabs.Needlr.Catalog.ConstructorParameterEntry[] { ");
 658658115            foreach (var param in type.ConstructorParameters)
 116            {
 95791117                var serviceKeyLiteral = param.ServiceKey != null
 95791118                    ? $"\"{GeneratorHelpers.EscapeStringLiteral(param.ServiceKey)}\""
 95791119                    : "null";
 95791120                var paramName = param.ParameterName ?? "unknown";
 95791121                constructorParamsBuilder.Append($"new global::NexusLabs.Needlr.Catalog.ConstructorParameterEntry(\"{Gene
 122            }
 233538123            constructorParamsBuilder.Append('}');
 124
 125            // Build service keys array
 233543126            var serviceKeysArray = $"new string[] {{ {string.Join(", ", type.ServiceKeys.Select(k => $"\"{GeneratorHelpe
 127
 233538128            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.ServiceCatalogEntry(\"{GeneratorHelpers.Es
 129        }
 130
 455131        builder.AppendLine("    };");
 455132        builder.AppendLine();
 455133    }
 134
 135    private static void GenerateDecoratorsProperty(
 136        StringBuilder builder,
 137        IReadOnlyList<DiscoveredDecorator> decorators,
 138        string? projectDirectory)
 139    {
 455140        builder.AppendLine($"    /// <inheritdoc />");
 455141        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 455142        builder.AppendLine("    {");
 143
 962144        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
 455155        builder.AppendLine("    };");
 455156        builder.AppendLine();
 455157    }
 158
 159    private static void GenerateHostedServicesProperty(
 160        StringBuilder builder,
 161        IReadOnlyList<DiscoveredHostedService> hostedServices,
 162        string? projectDirectory)
 163    {
 455164        builder.AppendLine($"    /// <inheritdoc />");
 455165        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 455166        builder.AppendLine("    {");
 167
 924168        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
 455192        builder.AppendLine("    };");
 455193        builder.AppendLine();
 455194    }
 195
 196    private static void GenerateInterceptedServicesProperty(
 197        StringBuilder builder,
 198        IReadOnlyList<DiscoveredInterceptedService> interceptedServices,
 199        string? projectDirectory)
 200    {
 455201        builder.AppendLine($"    /// <inheritdoc />");
 455202        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 455203        builder.AppendLine("    {");
 204
 938205        foreach (var intercepted in interceptedServices)
 206        {
 14207            var shortName = GeneratorHelpers.GetShortTypeName(intercepted.TypeName);
 14208            var lifetimeStr = intercepted.Lifetime switch
 14209            {
 3210                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"
 14214            };
 14215            var sourceFilePath = GetRelativeSourcePath(intercepted.SourceFilePath, projectDirectory);
 14216            var sourceFilePathLiteral = sourceFilePath != null
 14217                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 14218                : "null";
 219
 28220            var interfacesArray = $"new string[] {{ {string.Join(", ", intercepted.InterfaceNames.Select(i => $"\"{Gener
 31221            var interceptorsArray = $"new string[] {{ {string.Join(", ", intercepted.AllInterceptorTypeNames.Select(i =>
 222
 14223            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.InterceptedServiceCatalogEntry(\"{Generato
 224        }
 225
 455226        builder.AppendLine("    };");
 455227        builder.AppendLine();
 455228    }
 229
 230    private static void GenerateOptionsProperty(
 231        StringBuilder builder,
 232        IReadOnlyList<DiscoveredOptions> options,
 233        string? projectDirectory)
 234    {
 455235        builder.AppendLine($"    /// <inheritdoc />");
 455236        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 455237        builder.AppendLine("    {");
 238
 1240239        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
 455253        builder.AppendLine("    };");
 455254        builder.AppendLine();
 455255    }
 256
 257    private static void GeneratePluginsProperty(
 258        StringBuilder builder,
 259        IReadOnlyList<DiscoveredPlugin> plugins,
 260        string? projectDirectory)
 261    {
 455262        builder.AppendLine($"    /// <inheritdoc />");
 455263        builder.AppendLine($"    public global::System.Collections.Generic.IReadOnlyList<global::NexusLabs.Needlr.Catalo
 455264        builder.AppendLine("    {");
 265
 3722266        foreach (var plugin in plugins)
 267        {
 1406268            var shortName = GeneratorHelpers.GetShortTypeName(plugin.TypeName);
 1406269            var sourceFilePath = GetRelativeSourcePath(plugin.SourceFilePath, projectDirectory);
 1406270            var sourceFilePathLiteral = sourceFilePath != null
 1406271                ? $"\"{GeneratorHelpers.EscapeStringLiteral(sourceFilePath)}\""
 1406272                : "null";
 273
 2817274            var interfacesArray = $"new string[] {{ {string.Join(", ", plugin.InterfaceNames.Select(i => $"\"{GeneratorH
 275
 1406276            builder.AppendLine($"        new global::NexusLabs.Needlr.Catalog.PluginCatalogEntry(\"{GeneratorHelpers.Esc
 277        }
 278
 455279        builder.AppendLine("    };");
 455280    }
 281
 282    private static string? GetRelativeSourcePath(string? fullPath, string? projectDirectory)
 283    {
 235409284        if (fullPath == null || projectDirectory == null)
 235409285            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}