Skip to content

Generator: Create Template Method Pattern #47

@JerrettDavis

Description

@JerrettDavis

Summary

Add a source generator that produces a complete implementation of the Template Method pattern for consumer-defined workflows.

The generator lives in PatternKit.Generators and emits self-contained, readable C# with no runtime PatternKit dependency.

Primary goals:

  • Define a canonical sequence of steps (the “template”).
  • Allow consumers to override hooks and optional steps safely.
  • Support sync + async flows (favoring ValueTask).
  • Provide deterministic ordering, diagnostics, and testability.

Motivation / Problem

Template Method is commonly re-implemented as:

  • base classes with virtual methods (inheritance tax)
  • ad-hoc pipelines with no clear contract for overrides
  • scattered hooks with inconsistent error handling

We want a declarative, boilerplate-free way to define a workflow that:

  • makes step order explicit
  • makes override points explicit
  • is friendly to records/immutability
  • is reflection-free and generator-deterministic

Supported Targets (must-have)

The generator must support:

  • partial class
  • partial struct
  • partial record class
  • partial record struct

The annotated type represents the workflow host (the place consumers call Execute/ExecuteAsync).


Proposed User Experience

Minimal template with required steps

[Template]
public partial class ImportWorkflow
{
    [TemplateStep(Order = 0)]
    private void Validate(ImportContext ctx);

    [TemplateStep(Order = 1)]
    private void Transform(ImportContext ctx);

    [TemplateStep(Order = 2)]
    private void Persist(ImportContext ctx);
}

Generated (representative shape):

  • Execute(ImportContext ctx) invokes steps in deterministic order.
  • ExecuteAsync(ImportContext ctx, CancellationToken ct = default) emitted when async steps/hooks exist or ForceAsync=true.

Hooks + error handling

[Template(GenerateAsync = true)]
public partial class ImportWorkflow
{
    [TemplateHook(HookPoint.BeforeAll)]
    private void OnStart(ImportContext ctx);

    [TemplateStep(0)]
    private ValueTask ValidateAsync(ImportContext ctx, CancellationToken ct);

    [TemplateStep(1)]
    private void Transform(ImportContext ctx);

    [TemplateHook(HookPoint.OnError)]
    private void OnError(ImportContext ctx, Exception ex);

    [TemplateHook(HookPoint.AfterAll)]
    private void OnComplete(ImportContext ctx);
}

Attributes / Surface Area

Namespace: PatternKit.Generators.Template

Core attributes

  • [Template] on the workflow host
  • [TemplateStep] on methods that form the required step sequence
  • [TemplateHook] on methods that plug into hook points

Suggested shapes:

TemplateAttribute

  • string ExecuteMethodName = "Execute"
  • string ExecuteAsyncMethodName = "ExecuteAsync"
  • bool GenerateAsync (default: inferred)
  • bool ForceAsync (default: false)
  • TemplateErrorPolicy ErrorPolicy (default: Rethrow)

TemplateStepAttribute

  • int Order (required in v1)
  • bool Optional (default: false)
  • string? Name (optional; for diagnostics)

TemplateHookAttribute

  • HookPoint HookPoint (BeforeAll, AfterAll, OnError)
  • int? StepOrder (reserved for v2: BeforeStep/AfterStep targeting)

Step Semantics

  • Steps execute in ascending Order.
  • Duplicate orders are errors.
  • Steps are invoked exactly once unless marked optional and suppressed (v2). In v1, Optional only affects error-policy eligibility.

Async rules

  • If any step/hook returns ValueTask or accepts CancellationToken, generator emits async path.
  • Sync Execute can exist alongside async ExecuteAsync.
  • Generated async APIs use ValueTask.

Error rules

  • Default: throw after invoking OnError (if present).
  • If ErrorPolicy = HandleAndContinue, only allowed when all remaining steps are optional. Otherwise emit diagnostic.

Diagnostics (must-have)

Stable IDs, actionable:

  • PKTMP001 Type marked [Template] must be partial.
  • PKTMP002 No [TemplateStep] methods found.
  • PKTMP003 Duplicate step order detected.
  • PKTMP004 Step method signature invalid.
  • PKTMP005 Hook method signature invalid.
  • PKTMP006 Mixed sync/async signatures not supported (explain required shapes).
  • PKTMP007 CancellationToken required for async step but missing.
  • PKTMP008 HandleAndContinue not allowed when non-optional steps remain.

Generated Code Layout

  • TypeName.Template.g.cs

Deterministic ordering:

  • steps ordered by Order, then by fully-qualified method name for tie-breaking in diagnostics.

Testing Expectations

  • Deterministic ordering with multiple steps.
  • Hooks fire at correct points.
  • Error behavior: OnError invoked; default rethrow policy enforced.
  • Async path uses ValueTask and respects cancellation.
  • Diagnostics: duplicate order, invalid signatures.

Acceptance Criteria

  • [Template] works for class/struct/record class/record struct.
  • Generated Execute runs steps deterministically.
  • Generated ExecuteAsync uses ValueTask and supports cancellation when needed.
  • Hooks supported: BeforeAll, AfterAll, OnError (v1).
  • Diagnostics cover ordering + signature correctness

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions