-
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 Command pattern for consumer-defined operations.
The generator lives in PatternKit.Generators and emits self-contained, readable C# with no runtime PatternKit dependency.
Primary goals:
- Define commands as first-class, strongly-typed operations.
- Generate boilerplate for execution, optional undo, and optional async execution.
- Support deterministic ordering and composition (macro commands).
- Provide actionable diagnostics and test coverage.
Motivation / Problem
Command implementations often repeat the same scaffolding:
- ICommand interfaces + per-command classes
- argument and validation boilerplate
- consistent naming and dispatch wiring
- optional undo support and history handling
- sync/async duplication
This is ideal for source generation: generate the boring parts, keep consumer code focused on behavior.
Supported Targets (must-have)
The generator must support commands expressed as:
partial class/partial structpartial record class/partial record struct
Two primary command models must be supported:
- Explicit command types (commands are real user types; generator emits executor adapters).
- Command host (a static/instance host groups multiple command methods).
Proposed User Experience
A) Command type model (recommended)
[Command]
public partial record struct RenameUser(Guid UserId, string NewName);
public sealed class UserService
{
[CommandHandler]
private void Handle(in RenameUser cmd) { /* ... */ }
}Generated (representative shape):
public readonly partial struct RenameUserCommand
{
public static void Execute(UserService handler, in RenameUser cmd);
public static ValueTask ExecuteAsync(UserService handler, RenameUser cmd, CancellationToken ct = default);
}B) Command host model (grouped commands)
[CommandHost]
public static partial class UserCommands
{
[CommandCase]
public static void Rename(UserService svc, Guid userId, string newName);
[CommandCase]
public static ValueTask DisableAsync(UserService svc, Guid userId, CancellationToken ct);
}Generated:
- strongly-typed command wrapper types (optional)
- consistent
Execute/ExecuteAsyncentrypoints
Attributes / Surface Area
Namespace: PatternKit.Generators.Command
Core
-
[Command]on command typesstring? CommandTypeName(default:<TypeName>Command)bool GenerateAsync(default: inferred)bool ForceAsync(default: false)bool GenerateUndo(default: false)
-
[CommandHandler]on methods that handle a commandType? CommandType(optional if inferrable)
Optional grouping:
[CommandHost]on a static partial class[CommandCase]on methods within a host
Undo/redo integration (optional v1, recommended v2):
[CommandUndo]on undo method- caretaker/history generator can be a separate issue or follow-up.
Semantics (must-have)
Handler resolution
V1: no DI scanning.
- Generator wires a command to an explicitly annotated handler method.
- If multiple handlers match, error.
- If no handler found, diagnostic.
Execution signatures
Supported handler signatures:
void Handle(in TCommand cmd)void Handle(TCommand cmd)ValueTask HandleAsync(TCommand cmd, CancellationToken ct = default)
Generated entrypoints:
Execute(handler, in cmd)for sync handlersExecuteAsync(handler, cmd, ct)for async handlers (ValueTask)
Command composition (macro commands)
Optional v1 feature (but valuable):
- generate
MacroCommand<TContext>builder that executes multiple commands in order. - deterministically executes in registration order.
Diagnostics (must-have)
Stable IDs, actionable:
PKCMD001Type marked[Command]must bepartial.PKCMD002No handler found for command type.PKCMD003Multiple handlers found for command type.PKCMD004Handler method signature invalid.PKCMD005Async handler detected but async generation disabled (enable GenerateAsync/ForceAsync).PKCMD006Command host marked[CommandHost]must bepartialandstatic.PKCMD007Command case signature invalid.
Generated Code Layout
TypeName.Command.g.csTypeName.CommandHost.g.cs(if host feature enabled)
Determinism:
- stable ordering by fully-qualified symbol name for emission.
Testing Expectations
-
Command execution routes to correct handler.
-
Async handler uses
ValueTaskand respects cancellation. -
Diagnostics:
- missing handler
- duplicate handlers
- invalid signatures
-
Optional macro command:
- executes in deterministic order
- stops on exception (default policy) and documents alternative policies (v2)
Acceptance Criteria
-
[Command]works for class/struct/record class/record struct. - Generates
ExecuteandExecuteAsync(ValueTask) entrypoints when appropriate. - Deterministic handler resolution with diagnostics.
- Optional CommandHost grouping supported (if included in v1).
- Tests cover execution routing, async behavior, and diagnostics.