-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Summary
Add a source generator that produces a complete implementation of the State pattern as a deterministic state machine with:
- explicit states and triggers
- deterministic transitions
- optional guards
- optional entry/exit hooks
- sync + async support (favor
ValueTask)
The generator lives in PatternKit.Generators and emits self-contained C# with no runtime PatternKit dependency.
Motivation / Problem
State pattern implementations tend to devolve into:
- giant switch statements
- scattered transition rules
- inconsistent guard behavior
- unclear concurrency/ordering
We want a generator that produces:
- explicit, readable transition tables
- deterministic semantics
- actionable diagnostics
- predictable performance (no reflection, minimal allocations)
Supported Targets (must-have)
The generator must support:
partial classpartial structpartial record classpartial record struct
The annotated type represents the state machine host, exposing Fire(...) / FireAsync(...) and a State property.
Proposed User Experience
A) Enum-based states/triggers
public enum OrderState { Draft, Submitted, Paid, Shipped, Cancelled }
public enum OrderTrigger { Submit, Pay, Ship, Cancel }
[StateMachine(typeof(OrderState), typeof(OrderTrigger))]
public partial class OrderFlow
{
[StateTransition(From = OrderState.Draft, Trigger = OrderTrigger.Submit, To = OrderState.Submitted)]
private void OnSubmit();
[StateGuard(From = OrderState.Submitted, Trigger = OrderTrigger.Pay)]
private bool CanPay() => true;
[StateTransition(From = OrderState.Submitted, Trigger = OrderTrigger.Pay, To = OrderState.Paid)]
private ValueTask OnPayAsync(CancellationToken ct);
[StateEntry(OrderState.Shipped)]
private void OnEnterShipped();
[StateExit(OrderState.Paid)]
private void OnExitPaid();
}Generated (representative shape):
public partial class OrderFlow
{
public OrderState State { get; private set; }
public bool CanFire(OrderTrigger trigger);
public void Fire(OrderTrigger trigger);
public ValueTask FireAsync(OrderTrigger trigger, CancellationToken ct = default);
}B) Type-based states (optional v2)
V1 scope can restrict states/triggers to enums for simplicity and performance.
Attributes / Surface Area
Namespace: PatternKit.Generators.State
Core
-
[StateMachine(Type stateType, Type triggerType)]on the hoststring FireMethodName = "Fire"string FireAsyncMethodName = "FireAsync"string CanFireMethodName = "CanFire"bool GenerateAsync(default: inferred)bool ForceAsync(default: false)StateMachineInvalidTriggerPolicy InvalidTrigger(default: Throw)StateMachineGuardFailurePolicy GuardFailure(default: Throw)
-
[StateTransition]on methods that execute on a transitionTState FromTTrigger TriggerTState To
-
[StateGuard]on methods that determine whether a transition is allowedFrom,Trigger
-
[StateEntry]/[StateExit]on hook methodsTState State
Policies:
InvalidTriggerPolicy: Throw | Ignore | ReturnFalse (if using TryFire)GuardFailurePolicy: Throw | Ignore | ReturnFalse
Semantics (must-have)
Deterministic transition resolution
- A transition is uniquely identified by
(FromState, Trigger). - Duplicate transitions are an error.
Guard evaluation
-
If a guard exists for
(FromState, Trigger):- evaluate guard first
- if guard fails, apply
GuardFailurePolicy
Execution order
When a transition occurs:
- Exit hooks for
FromState(if any) - Transition action method (
[StateTransition]) (if any) - Update
State = ToState - Entry hooks for
ToState(if any)
This order is deterministic and must be documented.
Async
- If any transition action or entry/exit hook is async (
ValueTask), generateFireAsync. - Prefer
ValueTaskacross generated signatures. - Cancellation token passed through to async hooks when applicable.
Concurrency
V1 scope:
- Not thread-safe by default.
- Optional
Lockingmode can be added later. If we include in v1, it must be explicit and tested.
Diagnostics (must-have)
Stable IDs, actionable:
PKST001Type marked[StateMachine]must bepartial.PKST002State type must be anenum(v1).PKST003Trigger type must be anenum(v1).PKST004Duplicate transition detected for (From, Trigger).PKST005Transition method signature invalid.PKST006Guard method signature invalid (must return bool / ValueTask if async guards supported).PKST007Entry/Exit hook signature invalid.PKST008Async method detected but async generation disabled; enableGenerateAsync/ForceAsync.
Generated Code Layout
TypeName.StateMachine.g.cs
Determinism:
- Transition tables generated in stable order: by
FromState, thenTrigger, then method name (for diagnostics).
Testing Expectations
-
Happy path transitions update state and call hooks in correct order.
-
Guard prevents transition and applies policy correctly.
-
Invalid trigger behavior matches policy.
-
Async transitions use
ValueTaskand propagate cancellation. -
Diagnostics:
- duplicate transitions
- invalid signatures
- non-enum state/trigger types (v1)
Acceptance Criteria
-
[StateMachine]works for class/struct/record class/record struct. - State/Trigger enums supported (v1).
- Deterministic transition resolution and execution order.
- Guards supported with documented policies.
- Entry/Exit hooks supported.
- Async path uses
ValueTaskwhen needed. - Diagnostics cover invalid usage and conflicts.
- Tests cover transitions, guards, hooks, async, and diagnostics.