MermaidDiagramGenerator.cs 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. using System.Text;
  2. using RackPeek.Domain.Resources;
  3. namespace RackPeek.Domain.UseCases.Mermaid;
  4. public static class MermaidDiagramGenerator
  5. {
  6. public static MermaidExportResult ToMermaidDiagram(
  7. this IReadOnlyList<Resource> resources,
  8. MermaidExportOptions? options = null)
  9. {
  10. MermaidExportOptions resolvedOptions = options ?? new MermaidExportOptions();
  11. var sb = new StringBuilder();
  12. var warnings = new List<string>();
  13. sb.AppendLine(resolvedOptions.DiagramType);
  14. foreach (Resource r in resources.OrderBy(x => x.Name))
  15. {
  16. if (resolvedOptions.IncludeTags.Any())
  17. {
  18. var tags = r.Tags ?? Array.Empty<string>();
  19. var match = resolvedOptions
  20. .IncludeTags
  21. .Any(t => tags.Contains(t, StringComparer.OrdinalIgnoreCase));
  22. if (!match)
  23. continue;
  24. }
  25. var nodeId = SanitizeId(r.Name);
  26. var label = BuildNodeLabel(r, resolvedOptions);
  27. sb.AppendLine($" {nodeId}[\"{label}\"]");
  28. }
  29. if (sb.Length == 0)
  30. warnings.Add("No Mermaid diagram entries generated.");
  31. return new MermaidExportResult(sb.ToString().TrimEnd(), warnings);
  32. }
  33. private static string BuildNodeLabel(Resource r, MermaidExportOptions options)
  34. {
  35. if (!options.IncludeLabels || r.Labels.Count == 0)
  36. return r.Name;
  37. IEnumerable<KeyValuePair<string, string>> filtered;
  38. if (options.LabelWhitelist is null)
  39. {
  40. filtered = r.Labels;
  41. }
  42. else
  43. {
  44. filtered = r.Labels.Where(kvp =>
  45. options.LabelWhitelist.Contains(kvp.Key, StringComparer.OrdinalIgnoreCase));
  46. }
  47. var labelParts = filtered
  48. .Select(kvp => $"{kvp.Key}: {kvp.Value}")
  49. .ToList();
  50. if (labelParts.Count == 0)
  51. return r.Name;
  52. return $"{r.Name}\\n{string.Join("\\n", labelParts)}";
  53. }
  54. private static string SanitizeId(string name)
  55. {
  56. var sb = new StringBuilder();
  57. foreach (var ch in name.Trim().ToLowerInvariant())
  58. {
  59. if (char.IsLetterOrDigit(ch) || ch == '_')
  60. {
  61. sb.Append(ch);
  62. }
  63. else if (ch == '-' || ch == '.' || ch == ' ')
  64. {
  65. sb.Append('_');
  66. }
  67. }
  68. return sb.Length == 0 ? "node" : sb.ToString();
  69. }
  70. }