-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement Mermaid charting and graph functionalities
This commit introduces the ability to generate Mermaid diagrams using C# language. It incorporates new classes such as XyChart, Timeline, and Flowchart. Furthermore, it implements tests for these features to guarantee their correctness. Noticeably, it supports customizing different properties of graphs, such as the shape of nodes in a flowchart, and the direction of links. Useful additions include an 'IMermaidable' interface and numerous helper classes like Node, Link, Subgraph, and Series.
- Loading branch information
1 parent
8d7ebbf
commit b4396f2
Showing
24 changed files
with
769 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
using Frank.Mermaid.Flowchart; | ||
using Frank.Mermaid.Timeline; | ||
using Xunit.Abstractions; | ||
|
||
namespace Frank.Mermaid.Tests; | ||
|
||
public class FlowchartTests | ||
{ | ||
private readonly ITestOutputHelper _outputHelper; | ||
|
||
public FlowchartTests(ITestOutputHelper outputHelper) | ||
{ | ||
_outputHelper = outputHelper; | ||
} | ||
|
||
[Fact] | ||
public void Test1() | ||
{ | ||
var flowchart = new Flowchart.Flowchart(); | ||
var node1 = new Node("Node 1"); | ||
var node2 = new Node("Node 2"); | ||
flowchart.AddNode(node1); | ||
flowchart.AddNode(node2); | ||
|
||
var link = new Link(node1, node2); | ||
flowchart.AddLink(link); | ||
|
||
var subgraph = new Subgraph("Subgraph 1", Direction.TopToBottom); | ||
var subgraphNode1 = new Node("Subgraph Node 1"); | ||
var subgraphNode2 = new Node("Subgraph Node 2"); | ||
subgraph.AddNode(subgraphNode1); | ||
subgraph.AddNode(subgraphNode2); | ||
|
||
var subgraphLink = new Link(subgraphNode1, subgraphNode2, "Subgraph Link 1"); | ||
subgraph.AddLink(subgraphLink); | ||
|
||
var link2 = new Link(subgraph, node2, "Subgraph Link to Node 2"); | ||
flowchart.AddLink(link2); | ||
|
||
flowchart.AddSubgraph(subgraph); | ||
|
||
var writer = flowchart.ToMermaidSyntax(); | ||
var result = writer.ToString(); | ||
|
||
_outputHelper.WriteLine(result); | ||
} | ||
|
||
[Fact] | ||
public void Test2() | ||
{ | ||
/* | ||
Testcase: | ||
flowchart LR | ||
subgraph subgraph1 | ||
direction TB | ||
top1[top] --> bottom1[bottom] | ||
end | ||
subgraph subgraph2 | ||
direction TB | ||
top2[top] --> bottom2[bottom] | ||
end | ||
%% ^ These subgraphs are identical, except for the links to them: | ||
%% Link *to* subgraph1: subgraph1 direction is maintained | ||
outside --> subgraph1 | ||
%% Link *within* subgraph2: | ||
%% subgraph2 inherits the direction of the top-level graph (LR) | ||
outside ---> top2 | ||
*/ | ||
|
||
var flowchart = new Flowchart.Flowchart(); | ||
|
||
var subgraph1 = new Subgraph("subgraph1", Direction.TopToBottom); | ||
var top1 = new Node("top"); | ||
var bottom1 = new Node("bottom"); | ||
subgraph1.AddNode(top1); | ||
subgraph1.AddNode(bottom1); | ||
var link1 = new Link(top1, bottom1); | ||
subgraph1.AddLink(link1); | ||
flowchart.AddSubgraph(subgraph1); | ||
|
||
var subgraph2 = new Subgraph("subgraph2", Direction.TopToBottom); | ||
var top2 = new Node("top"); | ||
var bottom2 = new Node("bottom"); | ||
subgraph2.AddNode(top2); | ||
subgraph2.AddNode(bottom2); | ||
var link2 = new Link(top2, bottom2); | ||
subgraph2.AddLink(link2); | ||
flowchart.AddSubgraph(subgraph2); | ||
|
||
var outside = new Node("outside"); | ||
flowchart.AddNode(outside); | ||
var link3 = new Link(outside, subgraph1); | ||
flowchart.AddLink(link3); | ||
|
||
var link4 = new Link(outside, top2); | ||
flowchart.AddLink(link4); | ||
|
||
var writer = flowchart.ToMermaidSyntax(); | ||
var result = writer.ToString(); | ||
|
||
_outputHelper.WriteLine(result); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using Frank.Mermaid.Timeline; | ||
using Xunit.Abstractions; | ||
|
||
namespace Frank.Mermaid.Tests; | ||
|
||
public class TimelineTests | ||
{ | ||
private readonly ITestOutputHelper _outputHelper; | ||
|
||
public TimelineTests(ITestOutputHelper outputHelper) | ||
{ | ||
_outputHelper = outputHelper; | ||
} | ||
|
||
[Fact] | ||
public void Test1() | ||
{ | ||
var timeline = new Timeline.Timeline("My Timeline"); | ||
var section1 = new Section("Section 1"); | ||
section1.AddEvent(new Event("Event 1", new DateTime(2022, 1, 1))); | ||
section1.AddEvent(new Event("Event 2", new DateTime(2022, 1, 2))); | ||
timeline.AddSection(section1); | ||
|
||
var section2 = new Section("Section 2"); | ||
section2.AddEvent(new Event("Event 3", new DateTime(2022, 1, 3))); | ||
section2.AddEvent(new Event("Event 4", new DateTime(2022, 1, 4))); | ||
timeline.AddSection(section2); | ||
|
||
var section3 = new Section("Section 3"); | ||
section3.AddEvent(new Event("Event 5", new DateTime(2022, 1, 5))); | ||
section3.AddEvent(new Event("Event 6", new DateTime(2022, 1, 6))); | ||
timeline.AddSection(section3); | ||
|
||
var writer = timeline.ToMermaidSyntax(); | ||
var result = writer.ToString(); | ||
|
||
_outputHelper.WriteLine(result); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
namespace Frank.Mermaid.Flowchart; | ||
|
||
public enum Direction | ||
{ | ||
TopToBottom, | ||
TopDown, | ||
BottomToTop, | ||
RightToLeft, | ||
LeftToRight | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
namespace Frank.Mermaid.Flowchart; | ||
|
||
public static class DirectionExtensions | ||
{ | ||
public static string ToMermaidSyntax(this Direction direction) | ||
{ | ||
return direction switch | ||
{ | ||
Direction.TopToBottom => "TB", | ||
Direction.TopDown => "TD", | ||
Direction.BottomToTop => "BT", | ||
Direction.RightToLeft => "RL", | ||
Direction.LeftToRight => "LR", | ||
_ => throw new ArgumentOutOfRangeException(nameof(direction), direction, null) | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
using CodegenCS; | ||
|
||
namespace Frank.Mermaid.Flowchart; | ||
|
||
public class Flowchart : IMermaidable | ||
{ | ||
private readonly List<Node> _nodes = new(); | ||
private readonly List<Link> _links = new(); | ||
private readonly List<Subgraph> _subgraphs = new(); | ||
private readonly Direction _direction; | ||
|
||
public Flowchart(Direction direction = Direction.TopToBottom) | ||
{ | ||
_direction = direction; | ||
} | ||
|
||
public void AddSubgraph(Subgraph subgraph) => _subgraphs.Add(subgraph); | ||
public void AddSubgraphs(IEnumerable<Subgraph> subgraphs) => _subgraphs.AddRange(subgraphs); | ||
|
||
public void AddNode(Node node) => _nodes.Add(node); | ||
public void AddNodes(IEnumerable<Node> nodes) => _nodes.AddRange(nodes); | ||
|
||
public void AddLink(Link link) => _links.Add(link); | ||
public void AddLinks(IEnumerable<Link> links) => _links.AddRange(links); | ||
|
||
/// <inheritdoc /> | ||
public Guid Id { get; } = Guid.NewGuid(); | ||
|
||
/// <inheritdoc /> | ||
public ICodegenTextWriter ToMermaidSyntax() | ||
{ | ||
var writer = new CodegenTextWriter(); | ||
writer.WriteLine("flowchart {0}", _direction.ToMermaidSyntax()); | ||
writer.IncreaseIndent(); | ||
|
||
foreach (var node in _nodes) | ||
{ | ||
writer.Write(node.ToMermaidSyntax()); | ||
} | ||
|
||
foreach (var link in _links) | ||
{ | ||
writer.Write(link.ToMermaidSyntax()); | ||
} | ||
|
||
foreach (var subgraph in _subgraphs) | ||
{ | ||
writer.Write(subgraph.ToMermaidSyntax()); | ||
} | ||
|
||
writer.DecreaseIndent(); | ||
|
||
return writer; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
using CodegenCS; | ||
|
||
namespace Frank.Mermaid.Flowchart; | ||
|
||
public class Line : IMermaidable | ||
{ | ||
private readonly LineStyle _lineStyle; | ||
private readonly int _lineWidth; | ||
|
||
public Line(LineStyle lineStyle, int lineWidth = 1) | ||
{ | ||
_lineStyle = lineStyle; | ||
_lineWidth = lineWidth; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public Guid Id { get; } = Guid.NewGuid(); | ||
|
||
/// <inheritdoc /> | ||
public ICodegenTextWriter ToMermaidSyntax() | ||
{ | ||
var writer = new CodegenTextWriter(); | ||
var arrow = _lineStyle.ToString().Contains("Arrow") ? ">" : ""; | ||
|
||
switch (_lineStyle) | ||
{ | ||
case LineStyle.Normal: | ||
case LineStyle.NormalWithArrow: | ||
writer.Write(new string('-', _lineWidth)); | ||
break; | ||
case LineStyle.Thick: | ||
case LineStyle.ThickWithArrow: | ||
writer.Write(new string('=', _lineWidth)); | ||
break; | ||
case LineStyle.Dotted: | ||
case LineStyle.DottedWithArrow: | ||
for (var i = 0; i < _lineWidth; i++) | ||
{ | ||
writer.Write(i == _lineWidth - 1 ? "." : ".-"); | ||
} | ||
break; | ||
default: | ||
throw new ArgumentOutOfRangeException(); | ||
} | ||
|
||
writer.Write(arrow); | ||
|
||
return writer; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
namespace Frank.Mermaid.Flowchart; | ||
|
||
public enum LineStyle | ||
{ | ||
Normal, | ||
NormalWithArrow, | ||
Thick, | ||
ThickWithArrow, | ||
Dotted, | ||
DottedWithArrow | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
using CodegenCS; | ||
|
||
namespace Frank.Mermaid.Flowchart; | ||
|
||
public class Link : IMermaidable | ||
{ | ||
public Link(string source, string target, string? label = null) | ||
{ | ||
Source = source; | ||
Target = target; | ||
Label = label; | ||
} | ||
|
||
public string? Label { get; } | ||
|
||
public string Target { get; } | ||
|
||
public string Source { get; } | ||
|
||
public Line Line { get; private set; } = new Line(LineStyle.Normal, 3); | ||
|
||
public Link(IMermaidable source, IMermaidable target, string? label = null) | ||
{ | ||
Source = GetIdentifier(source); | ||
Target = GetIdentifier(target); | ||
Label = label; | ||
} | ||
|
||
private string? GetIdentifier(IMermaidable source) | ||
{ | ||
if (source is Subgraph subgraph) | ||
{ | ||
return subgraph.Label; | ||
} | ||
|
||
return source.Id.ToString("N"); | ||
} | ||
|
||
public void SetLineStyle(Line line) | ||
{ | ||
Line = line; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public Guid Id { get; } = Guid.NewGuid(); | ||
|
||
/// <inheritdoc /> | ||
public ICodegenTextWriter ToMermaidSyntax() | ||
{ | ||
var writer = new CodegenTextWriter(); | ||
|
||
writer.Write("{0} {1}{2} {3}", Source, Line.ToMermaidSyntax(), GetLabel(), Target); | ||
|
||
writer.WriteLine(); | ||
return writer; | ||
} | ||
|
||
private string GetLabel() => !string.IsNullOrWhiteSpace(Label) ? $"|{Label}|" : string.Empty; | ||
|
||
/* | ||
Length 1 2 3 | ||
Normal --- ---- ----- | ||
Normal with arrow --> ---> ----> | ||
Thick === ==== ===== | ||
Thick with arrow ==> ===> ====> | ||
Dotted -.- -..- -...- | ||
Dotted with arrow -.-> -..-> -...-> | ||
*/ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
namespace Frank.Mermaid.Flowchart; | ||
|
||
public enum LinkType | ||
{ | ||
Default, | ||
Dotted, | ||
Thick, | ||
DottedThick | ||
} |
Oops, something went wrong.