< Summary

Information
Class: NexusLabs.Needlr.Generators.Export.GraphExporter
Assembly: NexusLabs.Needlr.Generators
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators/Export/GraphExporter.cs
Line coverage
81%
Covered lines: 242
Uncovered lines: 55
Coverable lines: 297
Total lines: 573
Line coverage: 81.4%
Branch coverage
68%
Covered branches: 81
Total branches: 118
Branch coverage: 68.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
GenerateGraphJson(...)100%11100%
BuildGraph(...)33.33%701845.71%
BuildTypeLookup(...)50%391860%
MapToGraphService(...)81.81%232287.32%
ComputeStatistics(...)75%4494.11%
GetSimpleTypeName(...)80%101093.75%
SimplifyGenericParameters(...)93.75%161695.23%
SerializeToJson(...)75%4496.42%
SerializeService(...)83.33%181898.5%
SerializeDiagnostic(...)0%2040%
Escape(...)100%22100%
NullableString(...)100%22100%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators/Export/GraphExporter.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Text;
 5using NexusLabs.Needlr.Generators.Models;
 6
 7namespace NexusLabs.Needlr.Generators.Export;
 8
 9/// <summary>
 10/// Generates the Needlr dependency graph JSON for IDE tooling.
 11/// </summary>
 12internal static class GraphExporter
 13{
 14    /// <summary>
 15    /// Generates the needlr-graph.json content from the discovery result.
 16    /// </summary>
 17    public static string GenerateGraphJson(
 18        DiscoveryResult discoveryResult,
 19        string assemblyName,
 20        string? projectPath,
 21        IReadOnlyList<CollectedDiagnostic>? diagnostics = null,
 22        IReadOnlyDictionary<string, IReadOnlyList<DiscoveredType>>? referencedAssemblyTypes = null)
 23    {
 424        var graph = BuildGraph(discoveryResult, assemblyName, projectPath, diagnostics, referencedAssemblyTypes);
 425        return SerializeToJson(graph);
 26    }
 27
 28    private static NeedlrGraph BuildGraph(
 29        DiscoveryResult discoveryResult,
 30        string assemblyName,
 31        string? projectPath,
 32        IReadOnlyList<CollectedDiagnostic>? diagnostics,
 33        IReadOnlyDictionary<string, IReadOnlyList<DiscoveredType>>? referencedAssemblyTypes)
 34    {
 435        var graph = new NeedlrGraph
 436        {
 437            SchemaVersion = "1.0",
 438            GeneratedAt = DateTime.UtcNow.ToString("O"),
 439            ProjectPath = projectPath,
 440            AssemblyName = assemblyName
 441        };
 42
 43        // Build type lookup for resolving dependencies (include referenced assembly types)
 444        var typeLookup = BuildTypeLookup(discoveryResult, referencedAssemblyTypes);
 45
 46        // Map injectable types from current assembly to graph services
 486047        foreach (var type in discoveryResult.InjectableTypes)
 48        {
 242649            var service = MapToGraphService(type, assemblyName, typeLookup, discoveryResult);
 242650            graph.Services.Add(service);
 51        }
 52
 53        // Add types from referenced assemblies with [GenerateTypeRegistry]
 454        if (referencedAssemblyTypes != null)
 55        {
 856            foreach (var kvp in referencedAssemblyTypes)
 57            {
 058                var refAssemblyName = kvp.Key;
 059                var types = kvp.Value;
 060                foreach (var type in types)
 61                {
 062                    var service = MapToGraphService(type, refAssemblyName, typeLookup, discoveryResult);
 063                    graph.Services.Add(service);
 64                }
 65            }
 66        }
 67
 68        // Add diagnostics if provided
 469        if (diagnostics != null)
 70        {
 071            foreach (var diag in diagnostics)
 72            {
 073                graph.Diagnostics.Add(new GraphDiagnostic
 074                {
 075                    Id = diag.Id,
 076                    Severity = diag.Severity,
 077                    Message = diag.Message,
 078                    Location = diag.FilePath != null ? new GraphLocation
 079                    {
 080                        FilePath = diag.FilePath,
 081                        Line = diag.Line,
 082                        Column = 0
 083                    } : null,
 084                    RelatedServices = diag.RelatedServices?.ToList() ?? new List<string>()
 085                });
 86            }
 87        }
 88
 89        // Compute statistics (include referenced assembly types in count)
 490        graph.Statistics = ComputeStatistics(discoveryResult, referencedAssemblyTypes);
 91
 492        return graph;
 93    }
 94
 95    private static Dictionary<string, DiscoveredType> BuildTypeLookup(
 96        DiscoveryResult discoveryResult,
 97        IReadOnlyDictionary<string, IReadOnlyList<DiscoveredType>>? referencedAssemblyTypes)
 98    {
 499        var lookup = new Dictionary<string, DiscoveredType>();
 100
 101        // Add types from current assembly
 4860102        foreach (var type in discoveryResult.InjectableTypes)
 103        {
 2426104            lookup[type.TypeName] = type;
 4870105            foreach (var iface in type.InterfaceNames)
 106            {
 9107                if (!lookup.ContainsKey(iface))
 108                {
 9109                    lookup[iface] = type;
 110                }
 111            }
 112        }
 113
 114        // Add types from referenced assemblies for dependency resolution
 4115        if (referencedAssemblyTypes != null)
 116        {
 8117            foreach (var kvp in referencedAssemblyTypes)
 118            {
 0119                foreach (var type in kvp.Value)
 120                {
 0121                    if (!lookup.ContainsKey(type.TypeName))
 122                    {
 0123                        lookup[type.TypeName] = type;
 124                    }
 0125                    foreach (var iface in type.InterfaceNames)
 126                    {
 0127                        if (!lookup.ContainsKey(iface))
 128                        {
 0129                            lookup[iface] = type;
 130                        }
 131                    }
 132                }
 133            }
 134        }
 135
 4136        return lookup;
 137    }
 138
 139    private static GraphService MapToGraphService(
 140        DiscoveredType type,
 141        string assemblyName,
 142        Dictionary<string, DiscoveredType> typeLookup,
 143        DiscoveryResult discoveryResult)
 144    {
 2426145        var service = new GraphService
 2426146        {
 2426147            Id = type.TypeName,
 2426148            TypeName = GetSimpleTypeName(type.TypeName),
 2426149            FullTypeName = type.TypeName,
 2426150            AssemblyName = assemblyName,
 2426151            Lifetime = type.Lifetime.ToString(),
 2426152            Location = type.SourceFilePath != null ? new GraphLocation
 2426153            {
 2426154                FilePath = type.SourceFilePath,
 2426155                Line = type.SourceLine,
 2426156                Column = 0
 2426157            } : null,
 2426158            ServiceKeys = type.ServiceKeys.ToList(),
 2426159            Metadata = new GraphServiceMetadata
 2426160            {
 2426161                IsDisposable = type.IsDisposable,
 0162                HasFactory = discoveryResult.Factories.Any(f => f.TypeName == type.TypeName),
 0163                IsHostedService = discoveryResult.HostedServices.Any(h => h.TypeName == type.TypeName),
 11488164                IsPlugin = discoveryResult.PluginTypes.Any(p => p.TypeName == type.TypeName)
 2426165            }
 2426166        };
 167
 168        // Map interfaces with locations
 4870169        foreach (var ifaceInfo in type.InterfaceInfos)
 170        {
 9171            service.Interfaces.Add(new GraphInterface
 9172            {
 9173                Name = GetSimpleTypeName(ifaceInfo.FullName),
 9174                FullName = ifaceInfo.FullName,
 9175                Location = ifaceInfo.HasLocation ? new GraphLocation
 9176                {
 9177                    FilePath = ifaceInfo.SourceFilePath!,
 9178                    Line = ifaceInfo.SourceLine,
 9179                    Column = 0
 9180                } : null
 9181            });
 182        }
 183        // Fall back to InterfaceNames if no InterfaceInfos (for backwards compat)
 2426184        if (type.InterfaceInfos.Length == 0)
 185        {
 4834186            foreach (var iface in type.InterfaceNames)
 187            {
 0188                service.Interfaces.Add(new GraphInterface
 0189                {
 0190                    Name = GetSimpleTypeName(iface),
 0191                    FullName = iface
 0192                });
 193            }
 194        }
 195
 196        // Map dependencies from constructor parameters
 6004197        foreach (var param in type.ConstructorParameters)
 198        {
 576199            var dependency = new GraphDependency
 576200            {
 576201                ParameterName = param.ParameterName ?? string.Empty,
 576202                TypeName = GetSimpleTypeName(param.TypeName),
 576203                FullTypeName = param.TypeName,
 576204                IsKeyed = param.IsKeyed,
 576205                ServiceKey = param.ServiceKey
 576206            };
 207
 208            // Try to resolve the dependency
 576209            if (typeLookup.TryGetValue(param.TypeName, out var resolved))
 210            {
 192211                dependency.ResolvedTo = resolved.TypeName;
 192212                dependency.ResolvedLifetime = resolved.Lifetime.ToString();
 213            }
 214
 576215            service.Dependencies.Add(dependency);
 216        }
 217
 218        // Map decorators
 2426219        var decorators = discoveryResult.Decorators
 606220            .Where(d => type.InterfaceNames.Contains(d.ServiceTypeName))
 2427221            .OrderBy(d => d.Order);
 222
 4854223        foreach (var decorator in decorators)
 224        {
 1225            service.Decorators.Add(new GraphDecorator
 1226            {
 1227                TypeName = decorator.DecoratorTypeName,
 1228                Order = decorator.Order
 1229            });
 230        }
 231
 232        // Map interceptors
 2426233        var intercepted = discoveryResult.InterceptedServices
 2426234            .FirstOrDefault(i => i.TypeName == type.TypeName);
 235
 2426236        if (intercepted.TypeName != null)
 237        {
 0238            service.Interceptors = intercepted.AllInterceptorTypeNames.ToList();
 239        }
 240
 241        // Collect attributes
 2426242        service.Attributes.Add(type.Lifetime.ToString());
 2426243        if (type.IsKeyed)
 244        {
 0245            service.Attributes.Add("Keyed");
 246        }
 247
 2426248        return service;
 249    }
 250
 251    private static GraphStatistics ComputeStatistics(
 252        DiscoveryResult discoveryResult,
 253        IReadOnlyDictionary<string, IReadOnlyList<DiscoveredType>>? referencedAssemblyTypes)
 254    {
 255        // Get all types for statistics - current assembly + referenced assemblies
 4256        var allTypes = new List<DiscoveredType>(discoveryResult.InjectableTypes);
 4257        if (referencedAssemblyTypes != null)
 258        {
 8259            foreach (var kvp in referencedAssemblyTypes)
 260            {
 0261                allTypes.AddRange(kvp.Value);
 262            }
 263        }
 264
 4265        return new GraphStatistics
 4266        {
 4267            TotalServices = allTypes.Count,
 2426268            Singletons = allTypes.Count(t => t.Lifetime == GeneratorLifetime.Singleton),
 2426269            Scoped = allTypes.Count(t => t.Lifetime == GeneratorLifetime.Scoped),
 2426270            Transient = allTypes.Count(t => t.Lifetime == GeneratorLifetime.Transient),
 4271            Decorators = discoveryResult.Decorators.Count,
 4272            Interceptors = discoveryResult.InterceptedServices.Count,
 4273            Factories = discoveryResult.Factories.Count,
 4274            Options = discoveryResult.Options.Count,
 4275            HostedServices = discoveryResult.HostedServices.Count,
 4276            Plugins = discoveryResult.PluginTypes.Count
 4277        };
 278    }
 279
 280    private static string GetSimpleTypeName(string fullTypeName)
 281    {
 282        // Remove global:: prefix
 3063283        var name = fullTypeName;
 3063284        if (name.StartsWith("global::"))
 285        {
 2971286            name = name.Substring(8);
 287        }
 288
 289        // Handle generic types like Lazy<T> or IReadOnlyList<Assembly>
 290        // We want to preserve the generic structure but simplify inner types
 3063291        var genericStart = name.IndexOf('<');
 3063292        if (genericStart >= 0)
 293        {
 294            // Get the outer type name (before generic params)
 44295            var outerPart = name.Substring(0, genericStart);
 44296            var lastDot = outerPart.LastIndexOf('.');
 44297            var simpleOuter = lastDot >= 0 ? outerPart.Substring(lastDot + 1) : outerPart;
 298
 299            // Get the generic parameters and simplify them recursively
 44300            var genericEnd = name.LastIndexOf('>');
 44301            if (genericEnd > genericStart)
 302            {
 44303                var genericParams = name.Substring(genericStart + 1, genericEnd - genericStart - 1);
 304                // Simplify each generic parameter (split by comma, handle nested generics)
 44305                var simplifiedParams = SimplifyGenericParameters(genericParams);
 44306                return $"{simpleOuter}<{simplifiedParams}>";
 307            }
 308
 0309            return simpleOuter;
 310        }
 311
 312        // Get just the type name (after last dot)
 3019313        var idx = name.LastIndexOf('.');
 3019314        return idx >= 0 ? name.Substring(idx + 1) : name;
 315    }
 316
 317    private static string SimplifyGenericParameters(string genericParams)
 318    {
 319        // Handle nested generics by tracking depth
 44320        var result = new StringBuilder();
 44321        var depth = 0;
 44322        var currentParam = new StringBuilder();
 323
 2824324        foreach (var c in genericParams)
 325        {
 1368326            if (c == '<')
 327            {
 8328                depth++;
 8329                currentParam.Append(c);
 330            }
 1360331            else if (c == '>')
 332            {
 8333                depth--;
 8334                currentParam.Append(c);
 335            }
 1352336            else if (c == ',' && depth == 0)
 337            {
 338                // End of parameter at top level
 8339                if (result.Length > 0)
 340                {
 0341                    result.Append(", ");
 342                }
 8343                result.Append(GetSimpleTypeName(currentParam.ToString().Trim()));
 8344                currentParam.Clear();
 345            }
 346            else
 347            {
 1344348                currentParam.Append(c);
 349            }
 350        }
 351
 352        // Add last parameter
 44353        if (currentParam.Length > 0)
 354        {
 44355            if (result.Length > 0)
 356            {
 8357                result.Append(", ");
 358            }
 44359            result.Append(GetSimpleTypeName(currentParam.ToString().Trim()));
 360        }
 361
 44362        return result.ToString();
 363    }
 364
 365    /// <summary>
 366    /// Serializes the graph to JSON without using System.Text.Json (not available in all targets).
 367    /// Uses simple string building for source generator compatibility.
 368    /// </summary>
 369    private static string SerializeToJson(NeedlrGraph graph)
 370    {
 4371        var sb = new StringBuilder();
 4372        sb.AppendLine("{");
 4373        sb.AppendLine($"  \"schemaVersion\": \"{Escape(graph.SchemaVersion)}\",");
 4374        sb.AppendLine($"  \"generatedAt\": \"{Escape(graph.GeneratedAt)}\",");
 4375        sb.AppendLine($"  \"projectPath\": {NullableString(graph.ProjectPath)},");
 4376        sb.AppendLine($"  \"assemblyName\": {NullableString(graph.AssemblyName)},");
 377
 378        // Services array
 4379        sb.AppendLine("  \"services\": [");
 4860380        for (int i = 0; i < graph.Services.Count; i++)
 381        {
 2426382            SerializeService(sb, graph.Services[i], i == graph.Services.Count - 1);
 383        }
 4384        sb.AppendLine("  ],");
 385
 386        // Diagnostics array
 4387        sb.AppendLine("  \"diagnostics\": [");
 8388        for (int i = 0; i < graph.Diagnostics.Count; i++)
 389        {
 0390            SerializeDiagnostic(sb, graph.Diagnostics[i], i == graph.Diagnostics.Count - 1);
 391        }
 4392        sb.AppendLine("  ],");
 393
 394        // Statistics object
 4395        sb.AppendLine("  \"statistics\": {");
 4396        sb.AppendLine($"    \"totalServices\": {graph.Statistics.TotalServices},");
 4397        sb.AppendLine($"    \"singletons\": {graph.Statistics.Singletons},");
 4398        sb.AppendLine($"    \"scoped\": {graph.Statistics.Scoped},");
 4399        sb.AppendLine($"    \"transient\": {graph.Statistics.Transient},");
 4400        sb.AppendLine($"    \"decorators\": {graph.Statistics.Decorators},");
 4401        sb.AppendLine($"    \"interceptors\": {graph.Statistics.Interceptors},");
 4402        sb.AppendLine($"    \"factories\": {graph.Statistics.Factories},");
 4403        sb.AppendLine($"    \"options\": {graph.Statistics.Options},");
 4404        sb.AppendLine($"    \"hostedServices\": {graph.Statistics.HostedServices},");
 4405        sb.AppendLine($"    \"plugins\": {graph.Statistics.Plugins}");
 4406        sb.AppendLine("  }");
 407
 4408        sb.AppendLine("}");
 4409        return sb.ToString();
 410    }
 411
 412    private static void SerializeService(StringBuilder sb, GraphService service, bool isLast)
 413    {
 2426414        sb.AppendLine("    {");
 2426415        sb.AppendLine($"      \"id\": \"{Escape(service.Id)}\",");
 2426416        sb.AppendLine($"      \"typeName\": \"{Escape(service.TypeName)}\",");
 2426417        sb.AppendLine($"      \"fullTypeName\": \"{Escape(service.FullTypeName)}\",");
 2426418        sb.AppendLine($"      \"assemblyName\": {NullableString(service.AssemblyName)},");
 419
 420        // Interfaces
 2426421        sb.AppendLine("      \"interfaces\": [");
 4870422        for (int i = 0; i < service.Interfaces.Count; i++)
 423        {
 9424            var iface = service.Interfaces[i];
 9425            var comma = i < service.Interfaces.Count - 1 ? "," : "";
 9426            sb.AppendLine("        {");
 9427            sb.AppendLine($"          \"name\": \"{Escape(iface.Name)}\",");
 9428            sb.AppendLine($"          \"fullName\": \"{Escape(iface.FullName)}\",");
 9429            if (iface.Location != null)
 430            {
 9431                sb.AppendLine("          \"location\": {");
 9432                sb.AppendLine($"            \"filePath\": {NullableString(iface.Location.FilePath)},");
 9433                sb.AppendLine($"            \"line\": {iface.Location.Line},");
 9434                sb.AppendLine($"            \"column\": {iface.Location.Column}");
 9435                sb.AppendLine("          }");
 436            }
 437            else
 438            {
 0439                sb.AppendLine("          \"location\": null");
 440            }
 9441            sb.AppendLine($"        }}{comma}");
 442        }
 2426443        sb.AppendLine("      ],");
 444
 2426445        sb.AppendLine($"      \"lifetime\": \"{Escape(service.Lifetime)}\",");
 446
 447        // Location
 2426448        if (service.Location != null)
 449        {
 10450            sb.AppendLine("      \"location\": {");
 10451            sb.AppendLine($"        \"filePath\": {NullableString(service.Location.FilePath)},");
 10452            sb.AppendLine($"        \"line\": {service.Location.Line},");
 10453            sb.AppendLine($"        \"column\": {service.Location.Column}");
 10454            sb.AppendLine("      },");
 455        }
 456        else
 457        {
 2416458            sb.AppendLine("      \"location\": null,");
 459        }
 460
 461        // Dependencies
 2426462        sb.AppendLine("      \"dependencies\": [");
 6004463        for (int i = 0; i < service.Dependencies.Count; i++)
 464        {
 576465            var dep = service.Dependencies[i];
 576466            var comma = i < service.Dependencies.Count - 1 ? "," : "";
 576467            sb.AppendLine("        {");
 576468            sb.AppendLine($"          \"parameterName\": \"{Escape(dep.ParameterName)}\",");
 576469            sb.AppendLine($"          \"typeName\": \"{Escape(dep.TypeName)}\",");
 576470            sb.AppendLine($"          \"fullTypeName\": \"{Escape(dep.FullTypeName)}\",");
 576471            sb.AppendLine($"          \"resolvedTo\": {NullableString(dep.ResolvedTo)},");
 576472            sb.AppendLine($"          \"resolvedLifetime\": {NullableString(dep.ResolvedLifetime)},");
 576473            sb.AppendLine($"          \"isKeyed\": {dep.IsKeyed.ToString().ToLowerInvariant()},");
 576474            sb.AppendLine($"          \"serviceKey\": {NullableString(dep.ServiceKey)}");
 576475            sb.AppendLine($"        }}{comma}");
 476        }
 2426477        sb.AppendLine("      ],");
 478
 479        // Decorators
 2426480        sb.AppendLine("      \"decorators\": [");
 4854481        for (int i = 0; i < service.Decorators.Count; i++)
 482        {
 1483            var dec = service.Decorators[i];
 1484            var comma = i < service.Decorators.Count - 1 ? "," : "";
 1485            sb.AppendLine($"        {{ \"typeName\": \"{Escape(dec.TypeName)}\", \"order\": {dec.Order} }}{comma}");
 486        }
 2426487        sb.AppendLine("      ],");
 488
 489        // Interceptors
 2426490        sb.Append("      \"interceptors\": [");
 2426491        sb.Append(string.Join(", ", service.Interceptors.Select(i => $"\"{Escape(i)}\"")));
 2426492        sb.AppendLine("],");
 493
 494        // Attributes
 2426495        sb.Append("      \"attributes\": [");
 4852496        sb.Append(string.Join(", ", service.Attributes.Select(a => $"\"{Escape(a)}\"")));
 2426497        sb.AppendLine("],");
 498
 499        // Service keys
 2426500        sb.Append("      \"serviceKeys\": [");
 2426501        sb.Append(string.Join(", ", service.ServiceKeys.Select(k => $"\"{Escape(k)}\"")));
 2426502        sb.AppendLine("],");
 503
 504        // Metadata
 2426505        sb.AppendLine("      \"metadata\": {");
 2426506        sb.AppendLine($"        \"hasFactory\": {service.Metadata.HasFactory.ToString().ToLowerInvariant()},");
 2426507        sb.AppendLine($"        \"hasOptions\": {service.Metadata.HasOptions.ToString().ToLowerInvariant()},");
 2426508        sb.AppendLine($"        \"isHostedService\": {service.Metadata.IsHostedService.ToString().ToLowerInvariant()},")
 2426509        sb.AppendLine($"        \"isDisposable\": {service.Metadata.IsDisposable.ToString().ToLowerInvariant()},");
 2426510        sb.AppendLine($"        \"isPlugin\": {service.Metadata.IsPlugin.ToString().ToLowerInvariant()}");
 2426511        sb.AppendLine("      }");
 512
 2426513        sb.AppendLine(isLast ? "    }" : "    },");
 2426514    }
 515
 516    private static void SerializeDiagnostic(StringBuilder sb, GraphDiagnostic diagnostic, bool isLast)
 517    {
 0518        sb.AppendLine("    {");
 0519        sb.AppendLine($"      \"id\": \"{Escape(diagnostic.Id)}\",");
 0520        sb.AppendLine($"      \"severity\": \"{Escape(diagnostic.Severity)}\",");
 0521        sb.AppendLine($"      \"message\": \"{Escape(diagnostic.Message)}\",");
 522
 0523        if (diagnostic.Location != null)
 524        {
 0525            sb.AppendLine("      \"location\": {");
 0526            sb.AppendLine($"        \"filePath\": {NullableString(diagnostic.Location.FilePath)},");
 0527            sb.AppendLine($"        \"line\": {diagnostic.Location.Line},");
 0528            sb.AppendLine($"        \"column\": {diagnostic.Location.Column}");
 0529            sb.AppendLine("      },");
 530        }
 531        else
 532        {
 0533            sb.AppendLine("      \"location\": null,");
 534        }
 535
 0536        sb.Append("      \"relatedServices\": [");
 0537        sb.Append(string.Join(", ", diagnostic.RelatedServices.Select(s => $"\"{Escape(s)}\"")));
 0538        sb.AppendLine("]");
 539
 0540        sb.AppendLine(isLast ? "    }" : "    },");
 0541    }
 542
 543    private static string Escape(string value)
 544    {
 16718545        if (string.IsNullOrEmpty(value))
 595546            return value;
 547
 16123548        return value
 16123549            .Replace("\\", "\\\\")
 16123550            .Replace("\"", "\\\"")
 16123551            .Replace("\n", "\\n")
 16123552            .Replace("\r", "\\r")
 16123553            .Replace("\t", "\\t");
 554    }
 555
 556    private static string NullableString(string? value)
 557    {
 4181558        return value == null ? "null" : $"\"{Escape(value)}\"";
 559    }
 560}
 561
 562/// <summary>
 563/// Represents a diagnostic collected during generation for inclusion in the graph.
 564/// </summary>
 565internal sealed class CollectedDiagnostic
 566{
 567    public string Id { get; set; } = string.Empty;
 568    public string Severity { get; set; } = string.Empty;
 569    public string Message { get; set; } = string.Empty;
 570    public string? FilePath { get; set; }
 571    public int Line { get; set; }
 572    public IReadOnlyList<string>? RelatedServices { get; set; }
 573}

Methods/Properties

GenerateGraphJson(NexusLabs.Needlr.Generators.Models.DiscoveryResult,System.String,System.String,System.Collections.Generic.IReadOnlyList`1<NexusLabs.Needlr.Generators.Export.CollectedDiagnostic>,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Collections.Generic.IReadOnlyList`1<NexusLabs.Needlr.Generators.Models.DiscoveredType>>)
BuildGraph(NexusLabs.Needlr.Generators.Models.DiscoveryResult,System.String,System.String,System.Collections.Generic.IReadOnlyList`1<NexusLabs.Needlr.Generators.Export.CollectedDiagnostic>,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Collections.Generic.IReadOnlyList`1<NexusLabs.Needlr.Generators.Models.DiscoveredType>>)
BuildTypeLookup(NexusLabs.Needlr.Generators.Models.DiscoveryResult,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Collections.Generic.IReadOnlyList`1<NexusLabs.Needlr.Generators.Models.DiscoveredType>>)
MapToGraphService(NexusLabs.Needlr.Generators.Models.DiscoveredType,System.String,System.Collections.Generic.Dictionary`2<System.String,NexusLabs.Needlr.Generators.Models.DiscoveredType>,NexusLabs.Needlr.Generators.Models.DiscoveryResult)
ComputeStatistics(NexusLabs.Needlr.Generators.Models.DiscoveryResult,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Collections.Generic.IReadOnlyList`1<NexusLabs.Needlr.Generators.Models.DiscoveredType>>)
GetSimpleTypeName(System.String)
SimplifyGenericParameters(System.String)
SerializeToJson(NexusLabs.Needlr.Generators.Export.NeedlrGraph)
SerializeService(System.Text.StringBuilder,NexusLabs.Needlr.Generators.Export.GraphService,System.Boolean)
SerializeDiagnostic(System.Text.StringBuilder,NexusLabs.Needlr.Generators.Export.GraphDiagnostic,System.Boolean)
Escape(System.String)
NullableString(System.String)