Skip to content

Commit 90b46e1

Browse files
committed
Add labels to the guard clauses for the diagram. Clean up superstate transitions. Point transitions into a superstate at the starting substate.
1 parent 3a8c582 commit 90b46e1

6 files changed

Lines changed: 378 additions & 17 deletions

File tree

FunctionalStateMachine.Core/StateMachineBuilder.cs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,24 +264,48 @@ public TransitionConfiguration Guard(Func<TData, TTrigger, bool> guard)
264264
return this;
265265
}
266266

267+
public TransitionConfiguration Guard(string label, Func<TData, TTrigger, bool> guard)
268+
{
269+
_inner.Guard(guard);
270+
return this;
271+
}
272+
267273
public TransitionConfiguration Guard(Func<TState, TData, TTrigger, bool> guard)
268274
{
269275
_inner.Guard(guard);
270276
return this;
271277
}
272278

279+
public TransitionConfiguration Guard(string label, Func<TState, TData, TTrigger, bool> guard)
280+
{
281+
_inner.Guard(guard);
282+
return this;
283+
}
284+
273285
public TransitionConfiguration Guard(Func<TData, bool> guard)
274286
{
275287
_inner.Guard(guard);
276288
return this;
277289
}
278290

291+
public TransitionConfiguration Guard(string label, Func<TData, bool> guard)
292+
{
293+
_inner.Guard(guard);
294+
return this;
295+
}
296+
279297
public TransitionConfiguration Guard(Func<TState, TData, bool> guard)
280298
{
281299
_inner.Guard(guard);
282300
return this;
283301
}
284302

303+
public TransitionConfiguration Guard(string label, Func<TState, TData, bool> guard)
304+
{
305+
_inner.Guard(guard);
306+
return this;
307+
}
308+
285309
public TransitionConfiguration ModifyData(Func<TData, TTrigger, TData> updater)
286310
{
287311
_inner.ModifyData(updater);
@@ -459,31 +483,65 @@ public TransitionConfiguration<TDerivedTrigger> Guard(
459483
return this;
460484
}
461485

486+
public TransitionConfiguration<TDerivedTrigger> Guard(
487+
string label,
488+
Func<TData, TDerivedTrigger, bool> guard)
489+
{
490+
_inner.Guard(guard);
491+
return this;
492+
}
493+
462494
public TransitionConfiguration<TDerivedTrigger> Guard(
463495
Func<TState, TData, TDerivedTrigger, bool> guard)
464496
{
465497
_inner.Guard(guard);
466498
return this;
467499
}
468500

501+
public TransitionConfiguration<TDerivedTrigger> Guard(
502+
string label,
503+
Func<TState, TData, TDerivedTrigger, bool> guard)
504+
{
505+
_inner.Guard(guard);
506+
return this;
507+
}
508+
469509
public TransitionConfiguration<TDerivedTrigger> Guard(Func<TData, bool> guard)
470510
{
471511
_inner.Guard(guard);
472512
return this;
473513
}
474514

515+
public TransitionConfiguration<TDerivedTrigger> Guard(string label, Func<TData, bool> guard)
516+
{
517+
_inner.Guard(guard);
518+
return this;
519+
}
520+
475521
public TransitionConfiguration<TDerivedTrigger> Guard(Func<TState, bool> guard)
476522
{
477523
_inner.Guard(guard);
478524
return this;
479525
}
480526

527+
public TransitionConfiguration<TDerivedTrigger> Guard(string label, Func<TState, bool> guard)
528+
{
529+
_inner.Guard(guard);
530+
return this;
531+
}
532+
481533
public TransitionConfiguration<TDerivedTrigger> Guard(Func<TState, TData, bool> guard)
482534
{
483535
_inner.Guard(guard);
484536
return this;
485537
}
486538

539+
public TransitionConfiguration<TDerivedTrigger> Guard(string label, Func<TState, TData, bool> guard)
540+
{
541+
_inner.Guard(guard);
542+
return this;
543+
}
544+
487545
public TransitionConfiguration<TDerivedTrigger> ModifyData(
488546
Func<TData, TDerivedTrigger, TData> updater)
489547
{
@@ -1129,12 +1187,23 @@ public TransitionConfiguration Guard(Func<TState, TTrigger, bool> guard)
11291187
return this;
11301188
}
11311189

1190+
public TransitionConfiguration Guard(string label, Func<TState, TTrigger, bool> guard)
1191+
{
1192+
_inner.Guard(guard);
1193+
return this;
1194+
}
1195+
11321196
public TransitionConfiguration Guard(Func<TState, bool> guard)
11331197
{
11341198
_inner.Guard(guard);
11351199
return this;
11361200
}
11371201

1202+
public TransitionConfiguration Guard(string label, Func<TState, bool> guard)
1203+
{
1204+
_inner.Guard(guard);
1205+
return this;
1206+
}
11381207
public TransitionConfiguration Execute(Func<TState, TTrigger, TCommand> action)
11391208
{
11401209
_inner.Execute(action);
@@ -1266,12 +1335,26 @@ public TransitionConfiguration<TDerivedTrigger> Guard(
12661335
return this;
12671336
}
12681337

1338+
public TransitionConfiguration<TDerivedTrigger> Guard(
1339+
string label,
1340+
Func<TState, TDerivedTrigger, bool> guard)
1341+
{
1342+
_inner.Guard(guard);
1343+
return this;
1344+
}
1345+
12691346
public TransitionConfiguration<TDerivedTrigger> Guard(Func<TState, bool> guard)
12701347
{
12711348
_inner.Guard(guard);
12721349
return this;
12731350
}
12741351

1352+
public TransitionConfiguration<TDerivedTrigger> Guard(string label, Func<TState, bool> guard)
1353+
{
1354+
_inner.Guard(guard);
1355+
return this;
1356+
}
1357+
12751358
public TransitionConfiguration<TDerivedTrigger> Execute(
12761359
Func<TState, TDerivedTrigger, TCommand> action)
12771360
{

FunctionalStateMachine.Diagrams.Tests/DiagramBuilderTests.cs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,47 @@ flowchart LR
5050
Assert.Equal(Normalize(expected), Normalize(diagram));
5151
}
5252

53+
[Fact]
54+
public void Renders_guard_labels_when_provided()
55+
{
56+
var source = """
57+
using FunctionalStateMachine.Core;
58+
59+
public static class Sample
60+
{
61+
public static object Build() =>
62+
StateMachine<State, Trigger, Data, Command>.Create()
63+
.StartWith(State.Idle)
64+
.For(State.Idle)
65+
.On<Trigger.Go>()
66+
.Guard("has access", data => data.Allowed)
67+
.TransitionTo(State.Running)
68+
.For(State.Running)
69+
.Build();
70+
}
71+
72+
public enum State
73+
{
74+
Idle,
75+
Running
76+
}
77+
78+
public abstract record Trigger
79+
{
80+
public sealed record Go : Trigger;
81+
}
82+
83+
public sealed record Data(bool Allowed);
84+
85+
public abstract record Command;
86+
""";
87+
88+
var diagram = DiagramBuilder.GenerateDiagram(source, "Build", "Guarded");
89+
90+
Assert.NotNull(diagram);
91+
Assert.Contains("Trigger.Go [has access]", diagram);
92+
}
93+
5394
[Fact]
5495
public void Builds_internal_transition_when_no_transition_to_is_declared()
5596
{
@@ -189,6 +230,90 @@ public abstract record Command;
189230
Assert.Contains("S_State_CheckingOut[State.CheckingOut]", diagram);
190231
}
191232

233+
[Fact]
234+
public void Renders_superstate_transitions_from_container_port()
235+
{
236+
var source = """
237+
using FunctionalStateMachine.Core;
238+
239+
public static class Sample
240+
{
241+
public static object Build() =>
242+
StateMachine<State, Trigger, Command>.Create()
243+
.StartWith(State.InStore)
244+
.For(State.InStore)
245+
.StartsWith(State.Shopping)
246+
.On<Trigger.Cancel>()
247+
.TransitionTo(State.Outside)
248+
.For(State.Shopping)
249+
.SubStateOf(State.InStore)
250+
.For(State.Outside)
251+
.Build();
252+
}
253+
254+
public enum State
255+
{
256+
Outside,
257+
InStore,
258+
Shopping
259+
}
260+
261+
public abstract record Trigger
262+
{
263+
public sealed record Cancel : Trigger;
264+
}
265+
266+
public abstract record Command;
267+
""";
268+
269+
var diagram = DiagramBuilder.GenerateDiagram(source, "Build", "Port");
270+
271+
Assert.NotNull(diagram);
272+
Assert.Contains("P_State_InStore", diagram);
273+
}
274+
275+
[Fact]
276+
public void Transitions_to_superstate_target_initial_substate()
277+
{
278+
var source = """
279+
using FunctionalStateMachine.Core;
280+
281+
public static class Sample
282+
{
283+
public static object Build() =>
284+
StateMachine<State, Trigger, Command>.Create()
285+
.StartWith(State.Outside)
286+
.For(State.InStore)
287+
.StartsWith(State.Shopping)
288+
.For(State.Outside)
289+
.On<Trigger.EnterStore>()
290+
.TransitionTo(State.InStore)
291+
.For(State.Shopping)
292+
.SubStateOf(State.InStore)
293+
.Build();
294+
}
295+
296+
public enum State
297+
{
298+
Outside,
299+
InStore,
300+
Shopping
301+
}
302+
303+
public abstract record Trigger
304+
{
305+
public sealed record EnterStore : Trigger;
306+
}
307+
308+
public abstract record Command;
309+
""";
310+
311+
var diagram = DiagramBuilder.GenerateDiagram(source, "Build", "EntryToSuper");
312+
313+
Assert.NotNull(diagram);
314+
Assert.Contains("S_State_Outside -->|Trigger.EnterStore| S_State_Shopping", diagram);
315+
}
316+
192317
private static string Normalize(string? value)
193318
{
194319
if (value is null)

0 commit comments

Comments
 (0)