| File: src\Shared\MermaidGraphExporter.cs | Web Access |
| Project: src\src\Aspire.Cli\Aspire.Cli.csproj (aspire) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Globalization; using System.Text; namespace Aspire.Shared; /// <summary> /// Exports resource names and relationships as a Mermaid flowchart. /// </summary> internal static class MermaidGraphExporter { public static string Export<TResource>( IEnumerable<TResource> resources, Func<TResource, string> getName, Func<TResource, string> getDisplayName, Func<TResource, IEnumerable<string>> getReferencedNames) { var orderedResources = resources.OrderBy(getName, StringComparer.Ordinal).ToList(); var nodeIds = new Dictionary<string, string>(StringComparer.Ordinal); var diagram = new StringBuilder("flowchart LR\n"); for (var i = 0; i < orderedResources.Count; i++) { var resource = orderedResources[i]; // Resource names can contain Mermaid syntax or reserved words such as "end". // Keep identifiers separate from labels, including for replicas with the same display name. var nodeId = "resource" + i.ToString(CultureInfo.InvariantCulture); nodeIds.Add(getName(resource), nodeId); diagram.Append(" ").Append(nodeId).Append("[\""); AppendLabel(diagram, getDisplayName(resource)); diagram.Append("\"]\n"); } foreach (var resource in orderedResources) { foreach (var reference in getReferencedNames(resource).Distinct(StringComparer.Ordinal).Order(StringComparer.Ordinal)) { // References outside the exported resource set must not introduce extra nodes. if (nodeIds.TryGetValue(reference, out var targetId)) { diagram.Append(" ").Append(nodeIds[getName(resource)]).Append(" --> ").Append(targetId).Append('\n'); } } } return diagram.ToString(); } private static void AppendLabel(StringBuilder diagram, string label) { // Mermaid quoted labels use decimal entities (for example: api#34;v2#34;). // Escape HTML and entity delimiters too so labels remain text, not markup or diagram syntax. // https://mermaid.js.org/syntax/flowchart.html#entity-codes-to-escape-characters foreach (var character in label) { if (character is '"' or '#' or '&' or '<' or '>' or '\\' or '`') { diagram.Append('#').Append(((int)character).ToString(CultureInfo.InvariantCulture)).Append(';'); } else { diagram.Append(char.IsControl(character) ? ' ' : character); } } } }