Skip to content

Commit

Permalink
Add mermaid graph format
Browse files Browse the repository at this point in the history
  • Loading branch information
sulmar committed Dec 20, 2023
1 parent 5dbf264 commit c1f602e
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 0 deletions.
23 changes: 23 additions & 0 deletions src/Stateless/Graph/MermaidGraph.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Stateless.Reflection;

namespace Stateless.Graph
{
/// <summary>
/// Class to generate a MermaidGraph
/// </summary>
public static class MermaidGraph
{
/// <summary>
/// Generate a Mermaid graph from the state machine info
/// </summary>
/// <param name="machineInfo"></param>
/// <returns></returns>
public static string Format(StateMachineInfo machineInfo)
{
var graph = new StateGraph(machineInfo);

return graph.ToGraph(new MermaidGraphStyle());
}

}
}
87 changes: 87 additions & 0 deletions src/Stateless/Graph/MermaidGraphStyle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using Stateless.Reflection;
using System;
using System.Collections.Generic;
using System.Reflection.Emit;
using System.Text;

namespace Stateless.Graph
{
/// <summary>
/// Class to generate a graph in mermaid format
/// </summary>
public class MermaidGraphStyle : GraphStyleBase
{
/// <summary>
/// Returns the formatted text for a single superstate and its substates.
/// For example, for DOT files this would be a subgraph containing nodes for all the substates.
/// </summary>
/// <param name="stateInfo">The superstate to generate text for</param>
/// <returns>Description of the superstate, and all its substates, in the desired format</returns>
public override string FormatOneCluster(SuperState stateInfo)
{
string stateRepresentationString = "";
return stateRepresentationString;
}

/// <summary>
/// Generate the text for a single decision node
/// </summary>
/// <param name="nodeName">Name of the node</param>
/// <param name="label">Label for the node</param>
/// <returns></returns>
public override string FormatOneDecisionNode(string nodeName, string label)
{
return String.Empty;
}

/// <summary>
/// Generate the text for a single state
/// </summary>
/// <param name="state">The state to generate text for</param>
/// <returns></returns>
public override string FormatOneState(State state)
{
return String.Empty;
}

/// <summary>Get the text that starts a new graph</summary>
/// <returns></returns>
public override string GetPrefix()
{
return "stateDiagram-v2";
}

/// <summary>
///
/// </summary>
/// <param name="initialState"></param>
/// <returns></returns>
public override string GetInitialTransition(StateInfo initialState)
{
return $"\r\n[*] --> {initialState}";
}



/// <summary>
///
/// </summary>
/// <param name="sourceNodeName"></param>
/// <param name="trigger"></param>
/// <param name="actions"></param>
/// <param name="destinationNodeName"></param>
/// <param name="guards"></param>
/// <returns></returns>
public override string FormatOneTransition(string sourceNodeName, string trigger, IEnumerable<string> actions, string destinationNodeName, IEnumerable<string> guards)
{
string label = trigger ?? "";

return FormatOneLine(sourceNodeName, destinationNodeName, label);
}

internal string FormatOneLine(string fromNodeName, string toNodeName, string label)
{
return $"\t{fromNodeName} --> {toNodeName} : {label}";
}
}
}
57 changes: 57 additions & 0 deletions test/Stateless.Tests/MermaidGraphFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using Xunit;

namespace Stateless.Tests
{
public class MermaidGraphFixture
{
[Fact]
public void Format_InitialTransition_ShouldReturns()
{
var expected = "stateDiagram-v2\r\n[*] --> A";

var sm = new StateMachine<State, Trigger>(State.A);

var result = Graph.MermaidGraph.Format(sm.GetInfo());

Assert.Equal(expected, result);

}

[Fact]
public void Format_SimpleTransition()
{
var expected = "stateDiagram-v2\r\n\tA --> B : X\r\n[*] --> A";

var sm = new StateMachine<State, Trigger>(State.A);

sm.Configure(State.A)
.Permit(Trigger.X, State.B);

var result = Graph.MermaidGraph.Format(sm.GetInfo());

Assert.Equal(expected, result);

}

[Fact]
public void TwoSimpleTransitions()
{
var expected = """
stateDiagram-v2
A --> B : X
A --> C : Y
""";

var sm = new StateMachine<State, Trigger>(State.A);

sm.Configure(State.A)
.Permit(Trigger.X, State.B)
.Permit(Trigger.Y, State.C);

var result = Graph.MermaidGraph.Format(sm.GetInfo());

Assert.Equal(expected, result);

}
}
}

0 comments on commit c1f602e

Please sign in to comment.