Skip to content

Commit 657db50

Browse files
committed
Add Start() so that the initial state can emit commands and immediately transition. Renamed InitialStateOrDefault to InitialState since it isn't optional.
1 parent 867b03e commit 657db50

File tree

7 files changed

+22
-31
lines changed

7 files changed

+22
-31
lines changed

FunctionalStateMachine.Core.Tests/StateMachineImmediateTransitionTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public void ImmediateTransitionGuard_CanBlockTransition()
6161
.TransitionTo(State.Waiting)
6262
.Done()
6363
.For(State.Waiting)
64+
.For(State.Ready)
6465
.Build();
6566

6667
var (state, _, commands) = machine.Start(new Data("stop"));

FunctionalStateMachine.Core/StateMachine.cs

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -77,29 +77,22 @@ public bool TryFire(
7777
throwOnUnhandled: false);
7878
}
7979

80-
public TState? InitialStateOrDefault()
80+
public TState InitialState
8181
{
82-
return _hasInitialState ? ResolveInitialLeaf(_initialState!) : default;
83-
}
84-
85-
public (TState State, TData Data, IReadOnlyList<TCommand> Commands) Start(TData data)
86-
{
87-
if (!_hasInitialState)
82+
get
8883
{
89-
throw new InvalidOperationException("No initial state has been configured.");
90-
}
84+
if (!_hasInitialState)
85+
{
86+
throw new InvalidOperationException("No initial state has been configured.");
87+
}
9188

92-
return Enter(_initialState!, data);
89+
return ResolveInitialLeaf(_initialState!);
90+
}
9391
}
9492

95-
public (TState State, TData Data, IReadOnlyList<TCommand> Commands) Enter(TState state, TData data)
93+
public (TState State, TData Data, IReadOnlyList<TCommand> Commands) Start(TData data)
9694
{
97-
if (!_states.ContainsKey(state))
98-
{
99-
throw new InvalidOperationException($"State '{state}' is not configured.");
100-
}
101-
102-
var targetState = ResolveInitialLeaf(state);
95+
var targetState = InitialState;
10396
var commandList = new List<TCommand>();
10497
AppendInitialEntryCommands(commandList, targetState, data);
10598
var (finalState, finalData) = ApplyImmediateTransitions(commandList, targetState, data);
@@ -111,6 +104,7 @@ public bool TryFire(
111104
return (finalState, finalData, commands);
112105
}
113106

107+
114108
private bool TryFireInternal(
115109
TTrigger trigger,
116110
TState currentState,
@@ -453,6 +447,7 @@ private void AppendInitialEntryCommands(
453447
}
454448
}
455449

450+
456451
private List<TState> GetHierarchyChain(TState state)
457452
{
458453
var chain = new List<TState>();
@@ -1743,19 +1738,14 @@ public bool TryFire(
17431738
return _inner.TryFire(trigger, currentState, new NoData(), out newState, out _, out commands);
17441739
}
17451740

1746-
public TState? InitialStateOrDefault() => _inner.InitialStateOrDefault();
1741+
public TState InitialState => _inner.InitialState;
17471742

17481743
public (TState State, IReadOnlyList<TCommand> Commands) Start()
17491744
{
17501745
var (state, _, commands) = _inner.Start(new NoData());
17511746
return (state, commands);
17521747
}
17531748

1754-
public (TState State, IReadOnlyList<TCommand> Commands) Enter(TState state)
1755-
{
1756-
var (newState, _, commands) = _inner.Enter(state, new NoData());
1757-
return (newState, commands);
1758-
}
17591749

17601750
internal sealed class StateConfiguration
17611751
{

FunctionalStateMachine.Samples/LightSwitchSample.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@ public class LightSwitchDemo(ITestOutputHelper output)
4646
public void Demo()
4747
{
4848
var machine = LightSwitchSample.Build();
49-
var state = machine.InitialStateOrDefault();
49+
var state = machine.InitialState;
5050

5151
for (int i = 0; i < 5; i++)
5252
{
53-
(state, var commands) = machine.Fire(LightTrigger.Toggle, state);
54-
Run(commands);
53+
(state, var toggleCommands) = machine.Fire(LightTrigger.Toggle, state);
54+
Run(toggleCommands);
5555
}
5656

5757
}

FunctionalStateMachine.Samples/ShoppingTrolleySample.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ public class ShoppingTrolleyDemo(ITestOutputHelper output)
168168
public void Demo_Card_Payment()
169169
{
170170
var machine = ShoppingTrolleySample.Build();
171-
var currentState = machine.InitialStateOrDefault();
171+
var currentState = machine.InitialState;
172172
var currentData = new CartSession(new ShopData([], 0));
173173

174174
(currentState, currentData) = Fire(CartTrigger.StartShopping(), currentState, currentData, machine);
@@ -183,7 +183,7 @@ public void Demo_Card_Payment()
183183
public void Demo_Cash_Payment()
184184
{
185185
var machine = ShoppingTrolleySample.Build();
186-
var currentState = machine.InitialStateOrDefault();
186+
var currentState = machine.InitialState;
187187
var currentData = new CartSession(new ShopData([], 0));
188188

189189
(currentState, currentData) = Fire(CartTrigger.StartShopping(), currentState, currentData, machine);

FunctionalStateMachine.Samples/ShoppingTrolleySampleTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public void PayByCash_OverpaymentIssuesRefund()
8888
private static (ShopState State, CartSession Data) StartState(
8989
StateMachine<ShopState, CartTrigger, CartSession, ShopCommand> machine)
9090
{
91-
return (machine.InitialStateOrDefault(), new CartSession(new ShopData([], 0)));
91+
return (machine.InitialState, new CartSession(new ShopData([], 0)));
9292
}
9393

9494
private static (ShopState State, CartSession Data) Fire(

docs/entry-exit.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,4 @@ var machine = StateMachine<State, Trigger, Data, Command>.Create()
3030

3131
## Notes
3232

33-
- Entry actions run when a state is entered. For the initial state, call `Start(...)` or `Start()` to emit entry commands.
33+
- Entry actions run when a state is entered. The initial state does not trigger `OnEntry` until the machine transitions into it, unless you call `Start(...)`.

docs/immediate-transitions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,5 @@ var (state, data, commands) = machine.Start(Data.Initial);
3838
## Notes
3939

4040
- Immediate transitions run only when a state is entered.
41-
- Use `Start(...)` (or `Start()` for no-data machines) to run entry actions and immediate transitions from the initial state.
41+
- Use `Start(data)` to run `OnEntry` and immediate transitions from the initial state.
4242
- If no immediate transition guard matches, the machine stays in the current state.

0 commit comments

Comments
 (0)