-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Summary
Add a source generator that produces boilerplate-free, GoF-consistent Decorator pattern implementations for interfaces and abstract classes.
The generator lives in PatternKit.Generators and emits self-contained, readable C# with no runtime PatternKit dependency.
Primary goals:
- Generate typed decorators without reflection.
- Make ordering explicit and deterministic.
- Support both “wrapper class” style and “pipeline” (next) style.
- Support sync + async members (favor
ValueTaskfor generated hooks).
Motivation / Problem
Decorator chains are frequently hand-written and drift:
- missing forwarding members
- inconsistent ordering semantics
- repeated logging/timing/retry boilerplate
- async gets messy quickly
Source generation can emit correct forwarding + consistent hooks once.
Supported Targets (must-have)
Decorator generation must support:
interfaceabstract class(virtual/abstract members only)
Decorator generation is opt-in via attribute on the contract.
Proposed User Experience
A) Generate base decorator + optional pipeline builder
[GenerateDecorator]
public partial interface IStorage
{
Stream OpenRead(string path);
ValueTask<Stream> OpenReadAsync(string path, CancellationToken ct = default);
}Generated (representative shape):
public abstract partial class StorageDecoratorBase : IStorage
{
protected StorageDecoratorBase(IStorage inner);
protected IStorage Inner { get; }
public virtual Stream OpenRead(string path) => Inner.OpenRead(path);
public virtual ValueTask<Stream> OpenReadAsync(string path, CancellationToken ct = default)
=> Inner.OpenReadAsync(path, ct);
}
public static partial class StorageDecorators
{
public static IStorage Compose(IStorage inner, params Func<IStorage, IStorage>[] decorators);
}B) Typed decorators written by users
public sealed class CachingStorage : StorageDecoratorBase
{
public CachingStorage(IStorage inner) : base(inner) { }
public override Stream OpenRead(string path) { /* cache */ }
}The generator ensures the base class always stays in sync with the contract.
Attributes / Surface Area
Namespace: PatternKit.Generators.Decorator
-
[GenerateDecorator]on contract typestring? BaseTypeName(default:<ContractName>DecoratorBase)string? HelpersTypeName(default:<ContractName>Decorators)DecoratorCompositionMode Composition(default:HelpersOnly)bool GenerateAsync(default: inferred)bool ForceAsync(default: false)
Optional:
[DecoratorIgnore]for members to exclude.
Enums:
DecoratorCompositionMode:None,HelpersOnly,PipelineNextStyle(v2)
Semantics (must-have)
Forwarding correctness
- Base decorator forwards all included members to
Inner. - For abstract class contracts, only virtual/abstract members are forwarded.
Composition helper ordering
If helper Compose is generated:
-
decorators applied in array order.
-
ordering must be documented:
decorators[0]is outermost by default.
Async
- Base decorator preserves exact signature from contract.
- No signature rewriting.
Diagnostics (must-have)
PKDEC001Type marked[GenerateDecorator]must bepartial.PKDEC002Unsupported member kind (e.g., event) for v1.PKDEC003Name conflict for generated base/helper types.PKDEC004Member not accessible for generation.
Generated Code Layout
ContractName.Decorator.g.cs
Determinism:
- stable emission order by fully-qualified member name.
Testing Expectations
- Base decorator forwards calls correctly.
- Composition helper wraps in deterministic order.
- Async forwarding works.
- Diagnostics: unsupported members, naming conflicts.
Acceptance Criteria
-
[GenerateDecorator]works for interfaces and abstract classes. - Generated base decorator forwards all members correctly.
- Optional composition helpers generated with deterministic ordering.
- Async members supported without signature rewriting.
- Diagnostics and tests included.