| | | 1 | | // Copyright (c) NexusLabs. All rights reserved. |
| | | 2 | | // Licensed under the MIT License. |
| | | 3 | | |
| | | 4 | | using System; |
| | | 5 | | using System.Collections.Generic; |
| | | 6 | | using System.Collections.Immutable; |
| | | 7 | | using System.Linq; |
| | | 8 | | using System.Text; |
| | | 9 | | using System.Threading; |
| | | 10 | | |
| | | 11 | | using Microsoft.CodeAnalysis; |
| | | 12 | | using Microsoft.CodeAnalysis.CSharp.Syntax; |
| | | 13 | | using Microsoft.CodeAnalysis.Text; |
| | | 14 | | |
| | | 15 | | namespace NexusLabs.Needlr.AgentFramework.Generators; |
| | | 16 | | |
| | | 17 | | /// <summary> |
| | | 18 | | /// Source generator for Microsoft Agent Framework functions. |
| | | 19 | | /// Discovers classes with [AgentFunction] methods and generates a compile-time type registry. |
| | | 20 | | /// Also discovers classes with [AgentFunctionGroup] attributes and generates a group registry. |
| | | 21 | | /// Also discovers classes with [NeedlrAiAgent] attributes and generates an agent registry. |
| | | 22 | | /// Always emits a [ModuleInitializer] that auto-registers all discovered types with |
| | | 23 | | /// AgentFrameworkGeneratedBootstrap on assembly load. |
| | | 24 | | /// </summary> |
| | | 25 | | [Generator] |
| | | 26 | | public class AgentFrameworkFunctionRegistryGenerator : IIncrementalGenerator |
| | | 27 | | { |
| | | 28 | | private const string NeedlrAiAgentAttributeName = "NexusLabs.Needlr.AgentFramework.NeedlrAiAgentAttribute"; |
| | | 29 | | private const string AgentHandoffsToAttributeName = "NexusLabs.Needlr.AgentFramework.AgentHandoffsToAttribute"; |
| | | 30 | | private const string AgentGroupChatMemberAttributeName = "NexusLabs.Needlr.AgentFramework.AgentGroupChatMemberAttrib |
| | | 31 | | private const string AgentSequenceMemberAttributeName = "NexusLabs.Needlr.AgentFramework.AgentSequenceMemberAttribut |
| | | 32 | | private const string WorkflowRunTerminationConditionAttributeName = "NexusLabs.Needlr.AgentFramework.WorkflowRunTerm |
| | | 33 | | private const string ProgressSinksAttributeName = "NexusLabs.Needlr.AgentFramework.ProgressSinksAttribute"; |
| | | 34 | | private const string AgentGraphEdgeAttributeName = "NexusLabs.Needlr.AgentFramework.AgentGraphEdgeAttribute"; |
| | | 35 | | private const string AgentGraphEntryAttributeName = "NexusLabs.Needlr.AgentFramework.AgentGraphEntryAttribute"; |
| | | 36 | | private const string AgentGraphNodeAttributeName = "NexusLabs.Needlr.AgentFramework.AgentGraphNodeAttribute"; |
| | | 37 | | private const string AgentGraphReducerAttributeName = "NexusLabs.Needlr.AgentFramework.AgentGraphReducerAttribute"; |
| | | 38 | | |
| | | 39 | | public void Initialize(IncrementalGeneratorInitializationContext context) |
| | | 40 | | { |
| | | 41 | | // [AgentFunction] method-bearing classes → AgentFrameworkFunctionRegistry |
| | 101 | 42 | | var functionClasses = context.SyntaxProvider |
| | 101 | 43 | | .CreateSyntaxProvider( |
| | 111678 | 44 | | predicate: static (s, _) => s is ClassDeclarationSyntax, |
| | 1966 | 45 | | transform: static (ctx, ct) => AgentDiscoveryHelper.GetAgentFunctionTypeInfo(ctx, ct)) |
| | 2067 | 46 | | .Where(static m => m is not null); |
| | | 47 | | |
| | | 48 | | // [AgentFunctionGroup] class-level annotations → AgentFrameworkFunctionGroupRegistry |
| | 101 | 49 | | var groupClasses = context.SyntaxProvider |
| | 101 | 50 | | .CreateSyntaxProvider( |
| | 111678 | 51 | | predicate: static (s, _) => s is ClassDeclarationSyntax, |
| | 1966 | 52 | | transform: static (ctx, ct) => AgentDiscoveryHelper.GetAgentFunctionGroupEntries(ctx, ct)) |
| | 2067 | 53 | | .Where(static arr => arr.Length > 0); |
| | | 54 | | |
| | | 55 | | // [NeedlrAiAgent] declared agent types → AgentRegistry + partial companions |
| | 101 | 56 | | var agentClasses = context.SyntaxProvider |
| | 101 | 57 | | .ForAttributeWithMetadataName( |
| | 101 | 58 | | NeedlrAiAgentAttributeName, |
| | 107 | 59 | | predicate: static (s, _) => s is ClassDeclarationSyntax, |
| | 107 | 60 | | transform: static (ctx, ct) => AgentDiscoveryHelper.GetNeedlrAiAgentTypeInfo(ctx, ct)) |
| | 208 | 61 | | .Where(static m => m is not null); |
| | | 62 | | |
| | | 63 | | // [AgentHandoffsTo] annotations → handoff topology registry |
| | 101 | 64 | | var handoffEntries = context.SyntaxProvider |
| | 101 | 65 | | .ForAttributeWithMetadataName( |
| | 101 | 66 | | AgentHandoffsToAttributeName, |
| | 5 | 67 | | predicate: static (s, _) => s is ClassDeclarationSyntax, |
| | 5 | 68 | | transform: static (ctx, ct) => AgentDiscoveryHelper.GetHandoffEntries(ctx, ct)) |
| | 106 | 69 | | .Where(static arr => arr.Length > 0); |
| | | 70 | | |
| | | 71 | | // [AgentGroupChatMember] annotations → group chat registry |
| | 101 | 72 | | var groupChatEntries = context.SyntaxProvider |
| | 101 | 73 | | .ForAttributeWithMetadataName( |
| | 101 | 74 | | AgentGroupChatMemberAttributeName, |
| | 8 | 75 | | predicate: static (s, _) => s is ClassDeclarationSyntax, |
| | 8 | 76 | | transform: static (ctx, ct) => AgentDiscoveryHelper.GetGroupChatEntries(ctx, ct)) |
| | 109 | 77 | | .Where(static arr => arr.Length > 0); |
| | | 78 | | |
| | | 79 | | // [AgentSequenceMember] annotations → sequential pipeline registry |
| | 101 | 80 | | var sequenceEntries = context.SyntaxProvider |
| | 101 | 81 | | .ForAttributeWithMetadataName( |
| | 101 | 82 | | AgentSequenceMemberAttributeName, |
| | 24 | 83 | | predicate: static (s, _) => s is ClassDeclarationSyntax, |
| | 24 | 84 | | transform: static (ctx, ct) => AgentDiscoveryHelper.GetSequenceEntries(ctx, ct)) |
| | 125 | 85 | | .Where(static arr => arr.Length > 0); |
| | | 86 | | |
| | | 87 | | // [WorkflowRunTerminationCondition] → termination conditions per agent |
| | 101 | 88 | | var terminationConditionEntries = context.SyntaxProvider |
| | 101 | 89 | | .ForAttributeWithMetadataName( |
| | 101 | 90 | | WorkflowRunTerminationConditionAttributeName, |
| | 8 | 91 | | predicate: static (s, _) => s is ClassDeclarationSyntax, |
| | 8 | 92 | | transform: static (ctx, ct) => AgentDiscoveryHelper.GetTerminationConditionEntries(ctx, ct)) |
| | 109 | 93 | | .Where(static arr => arr.Length > 0); |
| | | 94 | | |
| | | 95 | | // [ProgressSinks] → per-agent progress sink declarations |
| | 101 | 96 | | var progressSinksEntries = context.SyntaxProvider |
| | 101 | 97 | | .ForAttributeWithMetadataName( |
| | 101 | 98 | | ProgressSinksAttributeName, |
| | 4 | 99 | | predicate: static (s, _) => s is ClassDeclarationSyntax, |
| | 4 | 100 | | transform: static (ctx, ct) => AgentDiscoveryHelper.GetProgressSinksEntries(ctx, ct)) |
| | 105 | 101 | | .Where(static arr => arr.Length > 0); |
| | | 102 | | |
| | | 103 | | // [AgentGraphEdge] annotations → graph edge topology |
| | 101 | 104 | | var graphEdgeEntries = context.SyntaxProvider |
| | 101 | 105 | | .ForAttributeWithMetadataName( |
| | 101 | 106 | | AgentGraphEdgeAttributeName, |
| | 23 | 107 | | predicate: static (s, _) => s is ClassDeclarationSyntax, |
| | 19 | 108 | | transform: static (ctx, ct) => GraphDiscoveryHelper.GetGraphEdgeEntries(ctx, ct)) |
| | 120 | 109 | | .Where(static arr => arr.Length > 0); |
| | | 110 | | |
| | | 111 | | // [AgentGraphEntry] annotations → graph entry points |
| | 101 | 112 | | var graphEntryPointEntries = context.SyntaxProvider |
| | 101 | 113 | | .ForAttributeWithMetadataName( |
| | 101 | 114 | | AgentGraphEntryAttributeName, |
| | 21 | 115 | | predicate: static (s, _) => s is ClassDeclarationSyntax, |
| | 21 | 116 | | transform: static (ctx, ct) => GraphDiscoveryHelper.GetGraphEntryPointEntries(ctx, ct)) |
| | 122 | 117 | | .Where(static arr => arr.Length > 0); |
| | | 118 | | |
| | | 119 | | // [AgentGraphNode] annotations → graph node join modes |
| | 101 | 120 | | var graphNodeEntries = context.SyntaxProvider |
| | 101 | 121 | | .ForAttributeWithMetadataName( |
| | 101 | 122 | | AgentGraphNodeAttributeName, |
| | 2 | 123 | | predicate: static (s, _) => s is ClassDeclarationSyntax, |
| | 2 | 124 | | transform: static (ctx, ct) => GraphDiscoveryHelper.GetGraphNodeEntries(ctx, ct)) |
| | 103 | 125 | | .Where(static arr => arr.Length > 0); |
| | | 126 | | |
| | | 127 | | // [AgentGraphReducer] annotations → graph reducer metadata |
| | 101 | 128 | | var graphReducerEntries = context.SyntaxProvider |
| | 101 | 129 | | .ForAttributeWithMetadataName( |
| | 101 | 130 | | AgentGraphReducerAttributeName, |
| | 3 | 131 | | predicate: static (s, _) => s is ClassDeclarationSyntax, |
| | 3 | 132 | | transform: static (ctx, ct) => GraphDiscoveryHelper.GetGraphReducerEntries(ctx, ct)) |
| | 104 | 133 | | .Where(static arr => arr.Length > 0); |
| | | 134 | | |
| | | 135 | | // Unified output: all pipelines combined with compilation metadata and build config. |
| | | 136 | | // Always emits all registries + [ModuleInitializer] bootstrap, even when empty. |
| | 101 | 137 | | var combined = functionClasses.Collect() |
| | 101 | 138 | | .Combine(groupClasses.Collect()) |
| | 101 | 139 | | .Combine(agentClasses.Collect()) |
| | 101 | 140 | | .Combine(handoffEntries.Collect()) |
| | 101 | 141 | | .Combine(groupChatEntries.Collect()) |
| | 101 | 142 | | .Combine(sequenceEntries.Collect()) |
| | 101 | 143 | | .Combine(terminationConditionEntries.Collect()) |
| | 101 | 144 | | .Combine(progressSinksEntries.Collect()) |
| | 101 | 145 | | .Combine(graphEdgeEntries.Collect()) |
| | 101 | 146 | | .Combine(graphEntryPointEntries.Collect()) |
| | 101 | 147 | | .Combine(graphNodeEntries.Collect()) |
| | 101 | 148 | | .Combine(graphReducerEntries.Collect()) |
| | 101 | 149 | | .Combine(context.CompilationProvider) |
| | 101 | 150 | | .Combine(context.AnalyzerConfigOptionsProvider); |
| | | 151 | | |
| | 101 | 152 | | context.RegisterSourceOutput(combined, |
| | 101 | 153 | | static (spc, data) => |
| | 101 | 154 | | { |
| | 101 | 155 | | var (((((((((((((functionData, groupData), agentData), handoffData), groupChatData), sequenceData), term |
| | 101 | 156 | | ExecuteAll(functionData, groupData, agentData, handoffData, groupChatData, sequenceData, terminationData |
| | 202 | 157 | | }); |
| | 101 | 158 | | } |
| | | 159 | | |
| | | 160 | | private static void ExecuteAll( |
| | | 161 | | ImmutableArray<AgentFunctionTypeInfo?> functionData, |
| | | 162 | | ImmutableArray<ImmutableArray<AgentFunctionGroupEntry>> groupData, |
| | | 163 | | ImmutableArray<NeedlrAiAgentTypeInfo?> agentData, |
| | | 164 | | ImmutableArray<ImmutableArray<HandoffEntry>> handoffData, |
| | | 165 | | ImmutableArray<ImmutableArray<GroupChatEntry>> groupChatData, |
| | | 166 | | ImmutableArray<ImmutableArray<SequenceEntry>> sequenceData, |
| | | 167 | | ImmutableArray<ImmutableArray<TerminationConditionEntry>> terminationData, |
| | | 168 | | ImmutableArray<ImmutableArray<ProgressSinksEntry>> progressSinksData, |
| | | 169 | | ImmutableArray<ImmutableArray<GraphEdgeEntry>> graphEdgeData, |
| | | 170 | | ImmutableArray<ImmutableArray<GraphEntryPointEntry>> graphEntryData, |
| | | 171 | | ImmutableArray<ImmutableArray<GraphNodeEntry>> graphNodeData, |
| | | 172 | | ImmutableArray<ImmutableArray<GraphReducerEntry>> graphReducerData, |
| | | 173 | | Compilation compilation, |
| | | 174 | | Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider configOptions, |
| | | 175 | | SourceProductionContext spc) |
| | | 176 | | { |
| | 101 | 177 | | var assemblyName = compilation.AssemblyName ?? "UnknownAssembly"; |
| | 101 | 178 | | var safeAssemblyName = AgentDiscoveryHelper.SanitizeIdentifier(assemblyName); |
| | | 179 | | |
| | 101 | 180 | | var validFunctionTypes = functionData |
| | 20 | 181 | | .Where(t => t.HasValue) |
| | 20 | 182 | | .Select(t => t!.Value) |
| | 101 | 183 | | .ToList(); |
| | | 184 | | |
| | 115 | 185 | | var allGroupEntries = groupData.SelectMany(a => a).ToList(); |
| | 101 | 186 | | var groupedByName = allGroupEntries |
| | 14 | 187 | | .GroupBy(e => e.GroupName) |
| | 141 | 188 | | .ToDictionary(g => g.Key, g => g.Select(e => e.TypeName).Distinct().ToList()); |
| | | 189 | | |
| | 101 | 190 | | var validAgentTypes = agentData |
| | 107 | 191 | | .Where(t => t.HasValue) |
| | 107 | 192 | | .Select(t => t!.Value) |
| | 101 | 193 | | .ToList(); |
| | | 194 | | |
| | 106 | 195 | | var allHandoffEntries = handoffData.SelectMany(a => a).ToList(); |
| | 101 | 196 | | var handoffByInitialAgent = allHandoffEntries |
| | 5 | 197 | | .GroupBy(e => (e.InitialAgentTypeName, e.InitialAgentClassName)) |
| | 101 | 198 | | .ToDictionary( |
| | 5 | 199 | | g => g.Key, |
| | 111 | 200 | | g => g.Select(e => (e.TargetAgentTypeName, e.HandoffReason)).ToList()); |
| | | 201 | | |
| | 109 | 202 | | var allGroupChatEntries = groupChatData.SelectMany(a => a).ToList(); |
| | 101 | 203 | | var groupChatByGroupName = allGroupChatEntries |
| | 8 | 204 | | .GroupBy(e => e.GroupName) |
| | 101 | 205 | | .ToDictionary( |
| | 4 | 206 | | g => g.Key, |
| | 12 | 207 | | g => g.OrderBy(e => e.Order) |
| | 8 | 208 | | .ThenBy(e => e.AgentTypeName, StringComparer.Ordinal) |
| | 8 | 209 | | .Select(e => e.AgentTypeName) |
| | 105 | 210 | | .Distinct() |
| | 105 | 211 | | .ToList()); |
| | | 212 | | |
| | 125 | 213 | | var allSequenceEntries = sequenceData.SelectMany(a => a).ToList(); |
| | 101 | 214 | | var sequenceByPipelineName = allSequenceEntries |
| | 24 | 215 | | .GroupBy(e => e.PipelineName) |
| | 101 | 216 | | .ToDictionary( |
| | 13 | 217 | | g => g.Key, |
| | 162 | 218 | | g => g.OrderBy(e => e.Order).Select(e => e.AgentTypeName).ToList()); |
| | | 219 | | |
| | 101 | 220 | | var conditionsByAgentTypeName = terminationData |
| | 8 | 221 | | .SelectMany(a => a) |
| | 8 | 222 | | .GroupBy(e => e.AgentTypeName) |
| | 117 | 223 | | .ToDictionary(g => g.Key, g => g.ToList()); |
| | | 224 | | |
| | 101 | 225 | | var progressSinksByAgent = progressSinksData |
| | 4 | 226 | | .SelectMany(a => a) |
| | 109 | 227 | | .ToDictionary(e => e.AgentClassName, e => e.SinkTypeFQNs); |
| | | 228 | | |
| | 120 | 229 | | var allGraphEdges = graphEdgeData.SelectMany(a => a).ToList(); |
| | 122 | 230 | | var allGraphEntryPoints = graphEntryData.SelectMany(a => a).ToList(); |
| | 103 | 231 | | var allGraphNodes = graphNodeData.SelectMany(a => a).ToList(); |
| | 104 | 232 | | var allGraphReducers = graphReducerData.SelectMany(a => a).ToList(); |
| | | 233 | | |
| | 101 | 234 | | var graphDataByName = BuildGraphDataByName(allGraphEdges, allGraphEntryPoints, allGraphNodes, allGraphReducers); |
| | | 235 | | |
| | | 236 | | // Always emit all registries (may be empty) and the bootstrap |
| | 101 | 237 | | spc.AddSource("AgentFrameworkFunctions.g.cs", |
| | 101 | 238 | | SourceText.From(RegistryCodeGenerator.GenerateRegistrySource(validFunctionTypes, safeAssemblyName), Encoding |
| | | 239 | | |
| | 101 | 240 | | spc.AddSource("AgentFrameworkFunctionGroups.g.cs", |
| | 101 | 241 | | SourceText.From(RegistryCodeGenerator.GenerateGroupRegistrySource(groupedByName, safeAssemblyName), Encoding |
| | | 242 | | |
| | 101 | 243 | | spc.AddSource("AgentRegistry.g.cs", |
| | 101 | 244 | | SourceText.From(RegistryCodeGenerator.GenerateAgentRegistrySource(validAgentTypes, safeAssemblyName), Encodi |
| | | 245 | | |
| | 101 | 246 | | spc.AddSource("AgentHandoffTopologyRegistry.g.cs", |
| | 101 | 247 | | SourceText.From(RegistryCodeGenerator.GenerateHandoffTopologyRegistrySource(handoffByInitialAgent, safeAssem |
| | | 248 | | |
| | 101 | 249 | | spc.AddSource("AgentGroupChatRegistry.g.cs", |
| | 101 | 250 | | SourceText.From(RegistryCodeGenerator.GenerateGroupChatRegistrySource(groupChatByGroupName, safeAssemblyName |
| | | 251 | | |
| | 101 | 252 | | spc.AddSource("AgentSequentialTopologyRegistry.g.cs", |
| | 101 | 253 | | SourceText.From(RegistryCodeGenerator.GenerateSequentialTopologyRegistrySource(sequenceByPipelineName, safeA |
| | | 254 | | |
| | 101 | 255 | | spc.AddSource("AgentGraphTopologyRegistry.g.cs", |
| | 101 | 256 | | SourceText.From(RegistryCodeGenerator.GenerateGraphTopologyRegistrySource(graphDataByName, safeAssemblyName) |
| | | 257 | | |
| | 101 | 258 | | spc.AddSource("NeedlrAgentFrameworkBootstrap.g.cs", |
| | 101 | 259 | | SourceText.From(BootstrapCodeGenerator.GenerateBootstrapSource(safeAssemblyName), Encoding.UTF8)); |
| | | 260 | | |
| | 101 | 261 | | spc.AddSource("WorkflowFactoryExtensions.g.cs", |
| | 101 | 262 | | SourceText.From(ExtensionsCodeGenerator.GenerateWorkflowFactoryExtensionsSource( |
| | 101 | 263 | | handoffByInitialAgent, groupChatByGroupName, sequenceByPipelineName, |
| | 101 | 264 | | conditionsByAgentTypeName, graphDataByName, safeAssemblyName), Encoding.UTF8)); |
| | | 265 | | |
| | 101 | 266 | | spc.AddSource("AgentFactoryExtensions.g.cs", |
| | 101 | 267 | | SourceText.From(ExtensionsCodeGenerator.GenerateAgentFactoryExtensionsSource(validAgentTypes, progressSinksB |
| | | 268 | | |
| | 101 | 269 | | spc.AddSource("AgentTopologyConstants.g.cs", |
| | 101 | 270 | | SourceText.From(ExtensionsCodeGenerator.GenerateAgentTopologyConstantsSource(validAgentTypes, allGroupEntrie |
| | | 271 | | |
| | 101 | 272 | | spc.AddSource("AgentFrameworkSyringeExtensions.g.cs", |
| | 101 | 273 | | SourceText.From(ExtensionsCodeGenerator.GenerateSyringeExtensionsSource(allGroupEntries, safeAssemblyName), |
| | | 274 | | |
| | 101 | 275 | | if (progressSinksByAgent.Count > 0) |
| | | 276 | | { |
| | 3 | 277 | | spc.AddSource("GeneratedProgressSinkRegistrations.g.cs", |
| | 3 | 278 | | SourceText.From(ExtensionsCodeGenerator.GenerateProgressSinkRegistrationSource(progressSinksByAgent, saf |
| | | 279 | | } |
| | | 280 | | |
| | 101 | 281 | | spc.AddSource("GeneratedAIFunctionProvider.g.cs", |
| | 101 | 282 | | SourceText.From(AIFunctionProviderCodeGenerator.GenerateAIFunctionProviderSource(validFunctionTypes, safeAss |
| | | 283 | | |
| | 101 | 284 | | configOptions.GlobalOptions.TryGetValue("build_property.NeedlrDiagnostics", out var diagValue); |
| | 101 | 285 | | if (string.Equals(diagValue, "true", StringComparison.OrdinalIgnoreCase)) |
| | | 286 | | { |
| | 6 | 287 | | var mermaid = TopologyGraphCodeGenerator.GenerateMermaidDiagram(handoffByInitialAgent, groupChatByGroupName, |
| | | 288 | | |
| | 6 | 289 | | spc.AddSource("AgentTopologyGraph.g.cs", |
| | 6 | 290 | | SourceText.From(TopologyGraphCodeGenerator.GenerateTopologyGraphSource(mermaid, safeAssemblyName), Encod |
| | | 291 | | } |
| | | 292 | | |
| | | 293 | | // Partial companions for [NeedlrAiAgent] classes declared as partial |
| | 331 | 294 | | foreach (var agentType in validAgentTypes.Where(a => a.IsPartial)) |
| | | 295 | | { |
| | 11 | 296 | | var safeTypeName = agentType.TypeName |
| | 11 | 297 | | .Replace("global::", "") |
| | 11 | 298 | | .Replace(".", "_") |
| | 11 | 299 | | .Replace("<", "_") |
| | 11 | 300 | | .Replace(">", "_"); |
| | | 301 | | |
| | 11 | 302 | | spc.AddSource($"{safeTypeName}.NeedlrAiAgent.g.cs", |
| | 11 | 303 | | SourceText.From(BootstrapCodeGenerator.GeneratePartialCompanionSource(agentType, groupedByName), Encodin |
| | | 304 | | } |
| | 101 | 305 | | } |
| | | 306 | | |
| | | 307 | | private static Dictionary<string, GraphData> BuildGraphDataByName( |
| | | 308 | | List<GraphEdgeEntry> allEdges, |
| | | 309 | | List<GraphEntryPointEntry> allEntryPoints, |
| | | 310 | | List<GraphNodeEntry> allNodes, |
| | | 311 | | List<GraphReducerEntry> allReducers) |
| | | 312 | | { |
| | 101 | 313 | | var graphNames = new HashSet<string>(StringComparer.Ordinal); |
| | 271 | 314 | | foreach (var e in allEdges) graphNames.Add(e.GraphName); |
| | 265 | 315 | | foreach (var e in allEntryPoints) graphNames.Add(e.GraphName); |
| | 208 | 316 | | foreach (var e in allNodes) graphNames.Add(e.GraphName); |
| | 211 | 317 | | foreach (var e in allReducers) graphNames.Add(e.GraphName); |
| | | 318 | | |
| | 101 | 319 | | var result = new Dictionary<string, GraphData>(StringComparer.Ordinal); |
| | 244 | 320 | | foreach (var name in graphNames) |
| | | 321 | | { |
| | 21 | 322 | | result[name] = new GraphData( |
| | 25 | 323 | | allEdges.Where(e => string.Equals(e.GraphName, name, StringComparison.Ordinal)).ToList(), |
| | 23 | 324 | | allEntryPoints.Where(e => string.Equals(e.GraphName, name, StringComparison.Ordinal)).ToList(), |
| | 2 | 325 | | allNodes.Where(e => string.Equals(e.GraphName, name, StringComparison.Ordinal)).ToList(), |
| | 24 | 326 | | allReducers.Where(e => string.Equals(e.GraphName, name, StringComparison.Ordinal)).ToList()); |
| | | 327 | | } |
| | | 328 | | |
| | 101 | 329 | | return result; |
| | | 330 | | } |
| | | 331 | | } |
| | | 332 | | |
| | | 333 | | internal sealed class GraphData |
| | | 334 | | { |
| | | 335 | | public GraphData( |
| | | 336 | | List<GraphEdgeEntry> edges, |
| | | 337 | | List<GraphEntryPointEntry> entryPoints, |
| | | 338 | | List<GraphNodeEntry> nodes, |
| | | 339 | | List<GraphReducerEntry> reducers) |
| | | 340 | | { |
| | | 341 | | Edges = edges; |
| | | 342 | | EntryPoints = entryPoints; |
| | | 343 | | Nodes = nodes; |
| | | 344 | | Reducers = reducers; |
| | | 345 | | } |
| | | 346 | | |
| | | 347 | | public List<GraphEdgeEntry> Edges { get; } |
| | | 348 | | public List<GraphEntryPointEntry> EntryPoints { get; } |
| | | 349 | | public List<GraphNodeEntry> Nodes { get; } |
| | | 350 | | public List<GraphReducerEntry> Reducers { get; } |
| | | 351 | | } |