diff --git a/PatternKit.Core/packages.lock.json b/PatternKit.Core/packages.lock.json deleted file mode 100644 index a07e21a3..00000000 --- a/PatternKit.Core/packages.lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "version": 1, - "dependencies": { - "net9.0": {} - } -} \ No newline at end of file diff --git a/docs/generators/examples.md b/docs/generators/examples.md new file mode 100644 index 00000000..feb9b4a3 --- /dev/null +++ b/docs/generators/examples.md @@ -0,0 +1,74 @@ +# Generator Examples (DI + Orchestration) + +Two production-flavored samples live in `src/PatternKit.Examples/Generators` to show the factory generators in action with `IServiceCollection`. + +## 1) ServiceModules with FactoryMethod + +`ServiceModules` is a `static partial` class annotated with `[FactoryMethod]`: + +```csharp +[FactoryMethod(typeof(string), CreateMethodName = "ConfigureModule")] +public static partial class ServiceModules +{ + [FactoryCase("metrics")] + public static IServiceCollection AddMetrics(IServiceCollection services) => + services.AddSingleton(); + + [FactoryCase("caching")] + public static IServiceCollection AddCaching(IServiceCollection services) => + services.AddSingleton(); + + [FactoryDefault] + public static IServiceCollection AddDefaults(IServiceCollection services) => + services.AddSingleton(); +} +``` + +Generated API (sync): + +```csharp +ServiceModules.ConfigureModule("metrics", services); +ServiceModules.TryCreate("workers", out var updated, services); +``` + +Usage (bootstrap modules from config): + +```csharp +var services = new ServiceCollection(); +foreach (var module in new[] { "metrics", "caching", "workers" }) +{ + ServiceModules.ConfigureModule(module, services); +} +var provider = services.BuildServiceProvider(); +``` + +## 2) ApplicationOrchestrator with FactoryClass + +An interface/abstract base marked `[FactoryClass]` emits a concrete factory: + +```csharp +[FactoryClass(typeof(string), GenerateEnumKeys = true)] +public interface IOrchestratorStep +{ + ValueTask ExecuteAsync(IServiceProvider services, CancellationToken ct = default); +} + +[FactoryClassKey("seed")] public sealed class SeedDataStep : IOrchestratorStep { … } +[FactoryClassKey("warm-cache")] public sealed class WarmCacheStep : IOrchestratorStep { … } +[FactoryClassKey("start-workers")] public sealed class StartWorkersStep : IOrchestratorStep { … } +``` + +Generated factory (partial) is `OrchestratorStepFactory` with: + +```csharp +var factory = new OrchestratorStepFactory(); +var step = await factory.CreateAsync("warm-cache"); +await step.ExecuteAsync(provider, cancellationToken); +``` + +`ApplicationOrchestrator` consumes it to run configured steps in order. This pattern works well for task pipelines defined by configuration or tenant-specific input. + +## Where to find the code + +- `src/PatternKit.Examples/Generators/FactoryGeneratorExamples.cs` +- Project references `PatternKit.Generators` as an analyzer so the factories are generated at build time. diff --git a/docs/generators/factory-class.md b/docs/generators/factory-class.md new file mode 100644 index 00000000..a335dd17 --- /dev/null +++ b/docs/generators/factory-class.md @@ -0,0 +1,69 @@ +# Factory Class Generator + +Generate a GoF-style factory class from an abstract base or interface using `[FactoryClass]` on the base and `[FactoryClassKey]` on concrete implementations. + +## Quick start + +```csharp +using PatternKit.Generators.Factories; + +namespace Demo; + +[FactoryClass(typeof(string), FactoryTypeName = "NotificationFactory")] +public interface INotification { } + +[FactoryClassKey("email")] +public class Email : INotification { } + +[FactoryClassKey("sms")] +public class Sms : INotification { } +``` + +Generated factory (sync): + +```csharp +public sealed partial class NotificationFactory +{ + public INotification Create(string key); + public bool TryCreate(string key, out INotification result); // unless GenerateTryCreate = false +} +``` + +Async is generated when any product exposes `public static CreateAsync()` returning `Task` or `ValueTask`: + +```csharp +public ValueTask CreateAsync(string key); +public ValueTask<(bool Success, INotification Result)> TryCreateAsync(string key); +``` + +## Enum keys + +Set `GenerateEnumKeys = true` to emit: + +- Nested `enum Keys` containing all registered keys (sanitized identifiers). +- Overloads of `Create`/`TryCreate`/`CreateAsync`/`TryCreateAsync` that accept the enum. + +## Behavior + +- **Base requirement** — `[FactoryClass]` must decorate an interface or abstract class. +- **Product discovery** — `[FactoryClassKey]` types must be concrete and implement/derive exactly one `[FactoryClass]` base. +- **Construction** — Uses public parameterless ctor when present; otherwise looks for `static CreateAsync()` on the product. Async factories are awaited; sync products are wrapped in `ValueTask.FromResult` for async APIs. +- **Key handling** — Keys must convert implicitly to `KeyType`. Duplicate keys per base are rejected. +- **Defaults** — No default branch; unknown keys throw in `Create` and return `false` in `TryCreate`. + +## Diagnostics + +| Id | Message | Fix | +| --- | --- | --- | +| PKCF001 | `[FactoryClass]` must be interface or abstract class | Mark the base `interface` or `abstract class`. | +| PKCF002 | `[FactoryClassKey]` type maps to multiple bases | Ensure each product implements only one `[FactoryClass]` base. | +| PKCF003 | `[FactoryClassKey]` type must be concrete | Remove `abstract` and supply an accessible ctor. | +| PKCF004 | Duplicate factory key | Remove duplicate keys per base type. | +| PKCF005 | Invalid factory key value | Ensure the key literal converts implicitly to `KeyType`. | +| PKCF006 | Missing accessible constructor | Add public parameterless ctor or `static CreateAsync()` returning `Task/ValueTask`. | + +## Tips + +- Use `FactoryTypeName` to control the emitted factory name; otherwise `{BaseName}Factory` is used. +- When you need enum-friendly calling code (e.g., switches), enable `GenerateEnumKeys` for typed overloads. +- Prefer `ValueTask` for `CreateAsync` on products; `Task` is wrapped when present. diff --git a/docs/generators/factory-method.md b/docs/generators/factory-method.md new file mode 100644 index 00000000..72492b61 --- /dev/null +++ b/docs/generators/factory-method.md @@ -0,0 +1,67 @@ +# Factory Method Generator + +Convert a `static partial` class into a keyed factory using `[FactoryMethod]`, `[FactoryCase]`, and `[FactoryDefault]`. The generator produces `Create`, `TryCreate`, and async siblings when needed. + +## Quick start + +```csharp +using PatternKit.Generators.Factories; + +namespace Demo; + +[FactoryMethod(typeof(string), CreateMethodName = "Make")] +public static partial class MimeFactory +{ + [FactoryCase("json")] + public static string Json() => "application/json"; + + [FactoryDefault] + public static string Default() => "application/octet-stream"; +} +``` + +Generated API (sync): + +```csharp +public static string Make(string key); +public static bool TryCreate(string key, out string value); +``` + +Async is generated when any case/default returns `ValueTask` or `Task`: + +```csharp +public static ValueTask MakeAsync(string key); +public static ValueTask<(bool Success, string Result)> TryCreateAsync(string key); +``` + +## Behavior + +- **Key matching** + - `KeyType == string` and `CaseInsensitiveStrings == true` (default) → `String.Equals` with `OrdinalIgnoreCase`. + - Otherwise exact match (`Ordinal` for strings). +- **Default path** + - If `[FactoryDefault]` exists, it is used when no case matches. + - Otherwise `Create` throws `ArgumentOutOfRangeException`; `TryCreate` returns `false` and `default`. +- **Parameters** + - All `[FactoryCase]`/`[FactoryDefault]` methods must be `static` with identical signatures (return + parameters). Parameters are forwarded into generated methods. +- **Async rules** + - If any mapping is async, generated async APIs use `ValueTask`. + - `Task` cases are wrapped in `ValueTask`. + - Synchronous cases are wrapped with `ValueTask.FromResult`. + +## Diagnostics + +| Id | Message | Fix | +| --- | --- | --- | +| PKKF001 | `[FactoryMethod]` type must be static partial | Mark the class `static partial`. | +| PKKF002 | Factory methods must share the same signature | Align return type and parameters across all cases/default. | +| PKKF003 | Duplicate factory key | Remove or rename the duplicate key. | +| PKKF004 | Multiple default factory methods | Keep only one `[FactoryDefault]`. | +| PKKF005 | Invalid factory key value | Ensure the key literal converts implicitly to `KeyType`. | +| PKKF006 | Factory methods must be static | Mark all `[FactoryCase]/[FactoryDefault]` methods `static`. | + +## Tips + +- Prefer small, pure mapping methods; let the generated factory handle validation and branching. +- For string keys that must be case-sensitive, set `CaseInsensitiveStrings = false` and test both `Create` and `TryCreate`. +- Use `CreateMethodName` to match existing APIs (`Make`, `FromKey`, etc.). diff --git a/docs/generators/index.md b/docs/generators/index.md new file mode 100644 index 00000000..34be96b6 --- /dev/null +++ b/docs/generators/index.md @@ -0,0 +1,29 @@ +# PatternKit Generators + +PatternKit includes a Roslyn incremental generator package (`PatternKit.Generators`) that emits factory code at compile time. Use it when you want predictable, allocation-light factories without writing the boilerplate by hand. + +## When to use + +- You already express intent with attributes and want the compiler to write the factory. +- You need both synchronous and `ValueTask`-first async entry points. +- You want deterministic codegen with no runtime dependency on PatternKit. + +## Package & setup + +1. Add the analyzer package to your project: + + ```bash + dotnet add package PatternKit.Generators + ``` + +2. Ensure the project targets `netstandard2.0+` (for libraries) or any modern .NET target (for apps). No runtime references are required. + +3. Mark your types with the attributes below; the generator produces partial classes at compile time. + +## Available generators + +- **Factory Method** — Turn a `static partial` class into a keyed dispatcher with optional default behavior. +- **Factory Class** — GoF-style factory for an abstract base or interface, mapping keys to concrete products (with optional enum keys and async creation). +- **Examples** — See the samples in `PatternKit.Examples/Generators` for DI module wiring and orchestrated application steps using the generators. + +Use the pages in this section for usage, generated API shape, async rules, and diagnostics. diff --git a/docs/generators/toc.yml b/docs/generators/toc.yml new file mode 100644 index 00000000..dcad1aec --- /dev/null +++ b/docs/generators/toc.yml @@ -0,0 +1,14 @@ +- name: Generators Overview + href: index.md + +- name: Factory Method + href: factory-method.md + +- name: Factory Class + href: factory-class.md + +- name: Examples + href: examples.md + +- name: Troubleshooting + href: troubleshooting.md diff --git a/docs/generators/troubleshooting.md b/docs/generators/troubleshooting.md new file mode 100644 index 00000000..1f1cd753 --- /dev/null +++ b/docs/generators/troubleshooting.md @@ -0,0 +1,39 @@ +# Generator Troubleshooting + +Common issues and how to resolve them when using the PatternKit factory generators. + +## Build errors + +- **PKKF001 / static partial** + Make the `[FactoryMethod]` type `public static partial class Foo { }`. + +- **PKKF002 / signature mismatch** + Align return type and parameter list across all `[FactoryCase]` and `[FactoryDefault]` methods. + +- **PKKF003 / duplicate key** or **PKCF004** + Ensure each key literal is unique per factory/base. + +- **PKKF005 / PKCF005 invalid key value** + The key must convert implicitly to `KeyType`. Fix the literal or adjust `KeyType`. + +- **PKKF006 / PKCF003 / PKCF006 static/ctor checks** + Mark factory methods `static`; add a public parameterless constructor or a `static CreateAsync()` on products. + +## Async behavior surprises + +- Async factory methods always surface as `ValueTask` in generated APIs. If your implementation returns `Task`, it is wrapped. +- Sync factory methods are awaited in async paths using `ValueTask.FromResult`; long-running work should be async to avoid blocking. + +## Enum key generation + +- `GenerateEnumKeys = true` creates a nested `Keys` enum and overloads that accept it. Identifier names are sanitized; if collisions occur (e.g., identical sanitized keys), adjust key values to be distinct. + +## How to inspect generated code + +- Build the project and look for `*.FactoryMethod.g.cs` or `*.FactoryClass.g.cs` in the `obj/` folder. These are readable partial classes—helpful when debugging behavior. + +## Still stuck? + +- Verify the analyzer package is referenced in the project being compiled (not only in a shared props file). +- Clean the solution (`dotnet clean`) and rebuild to force regeneration. +- Capture the exact diagnostic ID and message; these map directly to the tables on the generator pages. diff --git a/docs/index.md b/docs/index.md index a5480f73..885ea317 100644 --- a/docs/index.md +++ b/docs/index.md @@ -68,6 +68,10 @@ PatternKit will grow to cover **Creational**, **Structural**, and **Behavioral** | **Structural** | [Adapter](patterns/structural/adapter/fluent-adapter.md) • [Bridge](patterns/structural/bridge/bridge.md) • [Composite](patterns/structural/composite/composite.md) • [Decorator](patterns/structural/decorator/index.md) • [Facade](patterns/structural/facade/facade.md) • [Flyweight](patterns/structural/flyweight/index.md) • [Proxy](patterns/structural/proxy/index.md) | | **Behavioral** | [Strategy](patterns/behavioral/strategy/strategy.md) • [TryStrategy](patterns/behavioral/strategy/trystrategy.md) • [ActionStrategy](patterns/behavioral/strategy/actionstrategy.md) • [ActionChain](patterns/behavioral/chain/actionchain.md) • [ResultChain](patterns/behavioral/chain/resultchain.md) • [Command](patterns/behavioral/command/command.md) • [ReplayableSequence](patterns/behavioral/iterator/replayablesequence.md) • [WindowSequence](patterns/behavioral/iterator/windowsequence.md) • [Mediator](patterns/behavioral/mediator/mediator.md) • [Memento](patterns/behavioral/memento/memento.md) • [Observer](patterns/behavioral/observer/observer.md) • [AsyncObserver](patterns/behavioral/observer/asyncobserver.md) • [Visitor](patterns/behavioral/visitor/visitor.md) • [State](patterns/behavioral/state/state.md) • [Template Method](patterns/behavioral/template/template.md) | +## 🛠️ Source Generators + +Prefer compile-time factories over handwritten boilerplate? See the **Generators** section for Factory Method and Factory Class generators, including async rules, enum keys, and diagnostics. + Each pattern will ship with: diff --git a/docs/toc.yml b/docs/toc.yml index f8f69ac4..4b8e6375 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -7,6 +7,10 @@ - name: Patterns href: patterns/ homepage: patterns/ + +- name: Generators + href: generators/ + homepage: generators/ - name: Examples href: examples/ diff --git a/src/PatternKit.Core/Behavioral/Visitor/ActionVisitor.cs b/src/PatternKit.Core/Behavioral/Visitor/ActionVisitor.cs index a156112c..93e022c1 100644 --- a/src/PatternKit.Core/Behavioral/Visitor/ActionVisitor.cs +++ b/src/PatternKit.Core/Behavioral/Visitor/ActionVisitor.cs @@ -92,7 +92,7 @@ public sealed class Builder private readonly BranchBuilder _core = BranchBuilder.Create(); /// Registers an action for nodes of type . - /// A concrete type assignable to . + /// A concrete type assignable to the base type. /// The action invoked when the runtime type is . public Builder On(Action action) where T : TBase { diff --git a/src/PatternKit.Examples/Generators/FactoryGeneratorExamples.cs b/src/PatternKit.Examples/Generators/FactoryGeneratorExamples.cs new file mode 100644 index 00000000..facc4ce1 --- /dev/null +++ b/src/PatternKit.Examples/Generators/FactoryGeneratorExamples.cs @@ -0,0 +1,147 @@ +using Microsoft.Extensions.DependencyInjection; +using PatternKit.Generators.Factories; + +namespace PatternKit.Examples.Generators; + +// Example 1: Use FactoryMethod to map configuration keys to IServiceCollection wiring. +[FactoryMethod(typeof(string), CreateMethodName = "ConfigureModule")] +public static partial class ServiceModules +{ + [FactoryCase("metrics")] + public static IServiceCollection AddMetrics(IServiceCollection services) + { + services.AddSingleton(); + return services; + } + + [FactoryCase("caching")] + public static IServiceCollection AddCaching(IServiceCollection services) + { + services.AddSingleton(); + return services; + } + + [FactoryCase("workers")] + public static IServiceCollection AddWorkers(IServiceCollection services) + { + services.AddSingleton(); + return services; + } + + [FactoryDefault] + public static IServiceCollection AddDefaults(IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + return services; + } +} + +public static class ServiceModuleBootstrap +{ + public static IServiceProvider Build(string[] modules) + { + var services = new ServiceCollection(); + foreach (var module in modules) + { + ServiceModules.ConfigureModule(module, services); + } + + return services.BuildServiceProvider(); + } +} + +// Example 2: Use FactoryClass to orchestrate application steps from configuration keys. +[FactoryClass(typeof(string), GenerateEnumKeys = true)] +public interface IOrchestratorStep +{ + ValueTask ExecuteAsync(IServiceProvider services, CancellationToken cancellationToken = default); +} + +[FactoryClassKey("seed")] +public sealed class SeedDataStep : IOrchestratorStep +{ + public ValueTask ExecuteAsync(IServiceProvider services, CancellationToken cancellationToken = default) + { + var seeder = services.GetService(); + seeder?.Seed(); + return ValueTask.CompletedTask; + } +} + +[FactoryClassKey("warm-cache")] +public sealed class WarmCacheStep : IOrchestratorStep +{ + public async ValueTask ExecuteAsync(IServiceProvider services, CancellationToken cancellationToken = default) + { + if (services.GetService() is { } cache) + { + await cache.PrimeAsync(cancellationToken); + } + } +} + +[FactoryClassKey("start-workers")] +public sealed class StartWorkersStep : IOrchestratorStep +{ + public ValueTask ExecuteAsync(IServiceProvider services, CancellationToken cancellationToken = default) + { + if (services.GetService() is { } worker) + { + worker.Start(); + } + + return ValueTask.CompletedTask; + } +} + +public sealed class ApplicationOrchestrator(IServiceProvider services) +{ + private readonly OrchestratorStepFactory _factory = new(); + private readonly IServiceProvider _services = services; + + public async Task RunAsync(IEnumerable steps, CancellationToken cancellationToken = default) + { + foreach (var key in steps) + { + var step = _factory.Create(key); + await step.ExecuteAsync(_services, cancellationToken); + } + } +} + +// Lightweight sample service abstractions used in the demos. +public interface IMetricsSink +{ + void Write(string name, double value); +} + +public sealed class ConsoleMetricsSink : IMetricsSink +{ + public void Write(string name, double value) => Console.WriteLine($"{name}:{value}"); +} + +public interface ICacheProvider +{ + Task PrimeAsync(CancellationToken cancellationToken); +} + +public sealed class MemoryCacheProvider : ICacheProvider +{ + public Task PrimeAsync(CancellationToken cancellationToken) => Task.CompletedTask; +} + +public interface ISeeder +{ + void Seed(); +} + +public interface IWorker +{ + void Start(); +} + +public sealed class BackgroundWorker : IWorker +{ + public void Start() { } +} diff --git a/src/PatternKit.Examples/PatternKit.Examples.csproj b/src/PatternKit.Examples/PatternKit.Examples.csproj index b51e71f3..6cff25fe 100644 --- a/src/PatternKit.Examples/PatternKit.Examples.csproj +++ b/src/PatternKit.Examples/PatternKit.Examples.csproj @@ -7,17 +7,23 @@ + ReferenceOutputAssembly="true" + AdditionalProperties="TargetFramework=netstandard2.0" /> + - - - - - - + + + + + + + diff --git a/src/PatternKit.Examples/packages.lock.json b/src/PatternKit.Examples/packages.lock.json index f4befbe9..37078eb6 100644 --- a/src/PatternKit.Examples/packages.lock.json +++ b/src/PatternKit.Examples/packages.lock.json @@ -4,75 +4,94 @@ "net10.0": { "JetBrains.Annotations": { "type": "Direct", - "requested": "[2025.2.2, )", - "resolved": "2025.2.2", - "contentHash": "0X56ZRizuHdrnPpgXjWV7f2tQO1FlQg5O1967OGKnI/4ZRNOK642J8L7brM1nYvrxTTU5TP1yRyXLRLaXLPQ8A==" + "requested": "[2025.2.4, )", + "resolved": "2025.2.4", + "contentHash": "TwbgxAkXxY+vNEhNVx/QXjJ4vqxmepOjsgRvvImQPbHkHMMb4W+ahL3laMsxXKtNT7iMy+E1B3xkqao2hf1n3A==" }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "p5RKAY9POvs3axwA/AQRuJeM8AHuE8h4qbP1NxQeGm0ep46aXz1oCLAp/oOYxX1GsjStgdhHrN3XXLLXr0+b3w==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "6SIp/6Bngk4jm2W36JekZbiIbFPdE/eMUtrJEqIqHGpd1zar3jvgnwxnpWQfzUiGrkyY8q8s6V82zkkEZozghA==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9" + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" } }, "Microsoft.Extensions.Options": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "loxGGHE1FC2AefwPHzrjPq7X92LQm64qnU/whKfo6oWaceewPUVYQJBJs3S3E2qlWwnCpeZ+dGCPTX+5dgVAuQ==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "n4DCdnn2qs6V5U06Sx62FySEAZsJiJJgOzrPHDh9hPK7c2W8hEabC76F3Re3tGPjpiKa02RvB6FxZyxo8iICzg==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Configuration.Binder": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.DataAnnotations": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "Al+1FXnKKFygTXz0Zsa1+jYEPvsx5dKavlJxMXjRbrL6lmBhQZsVMhjuNB5lWvdRhdoxt5y/Q3v5kbLZLsXWdA==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "gwHO+zpVQGKK9KB3yen82Tff2zdLHHGIJzTut9L8RvoOO2RMSyYZrOmElvxu0lA4ZyaSxy8I0oNw1S/O/vkvFg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "/hymojfWbE9AlDOa0mczR44m00Jj+T3+HZO0ZnVTI032fVycI0ZbNOVFP6kqZMcXiLSYXzR2ilcwaRi6dzeGyA==" + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "z4pyMePOrl733ltTowbN565PxBw1oAr8IHmIXNDiDqd22nFpYltX9KhrNC/qBWAG1/Zx5MHX+cOYhWJQYCO/iw==" + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" }, "System.Collections.Immutable": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "/kpkgDxH984e3J3z5v/DIFi+0TWbUJXS8HNKUYBy3YnXtK09JVGs3cw5aOV6fDSw5NxbWLWlGrYjRteu6cjX3w==" + "resolved": "10.0.1", + "contentHash": "kdTe61B8P7i2M1pODC3MLbZ/CfFGjpC6c6jzxjQoB5DHZNewayCRqgFUmx3JKB6vLQtozpMQEiw+R5fO32Jv4g==" }, "patternkit.core": { "type": "Project" @@ -80,82 +99,101 @@ "patternkit.generators": { "type": "Project", "dependencies": { - "System.Collections.Immutable": "[9.0.9, )" + "System.Collections.Immutable": "[10.0.1, )" } } }, "net8.0": { "JetBrains.Annotations": { "type": "Direct", - "requested": "[2025.2.2, )", - "resolved": "2025.2.2", - "contentHash": "0X56ZRizuHdrnPpgXjWV7f2tQO1FlQg5O1967OGKnI/4ZRNOK642J8L7brM1nYvrxTTU5TP1yRyXLRLaXLPQ8A==" + "requested": "[2025.2.4, )", + "resolved": "2025.2.4", + "contentHash": "TwbgxAkXxY+vNEhNVx/QXjJ4vqxmepOjsgRvvImQPbHkHMMb4W+ahL3laMsxXKtNT7iMy+E1B3xkqao2hf1n3A==" }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "p5RKAY9POvs3axwA/AQRuJeM8AHuE8h4qbP1NxQeGm0ep46aXz1oCLAp/oOYxX1GsjStgdhHrN3XXLLXr0+b3w==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "6SIp/6Bngk4jm2W36JekZbiIbFPdE/eMUtrJEqIqHGpd1zar3jvgnwxnpWQfzUiGrkyY8q8s6V82zkkEZozghA==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" } }, "Microsoft.Extensions.Options": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "loxGGHE1FC2AefwPHzrjPq7X92LQm64qnU/whKfo6oWaceewPUVYQJBJs3S3E2qlWwnCpeZ+dGCPTX+5dgVAuQ==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "n4DCdnn2qs6V5U06Sx62FySEAZsJiJJgOzrPHDh9hPK7c2W8hEabC76F3Re3tGPjpiKa02RvB6FxZyxo8iICzg==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Configuration.Binder": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.DataAnnotations": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "Al+1FXnKKFygTXz0Zsa1+jYEPvsx5dKavlJxMXjRbrL6lmBhQZsVMhjuNB5lWvdRhdoxt5y/Q3v5kbLZLsXWdA==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "gwHO+zpVQGKK9KB3yen82Tff2zdLHHGIJzTut9L8RvoOO2RMSyYZrOmElvxu0lA4ZyaSxy8I0oNw1S/O/vkvFg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "/hymojfWbE9AlDOa0mczR44m00Jj+T3+HZO0ZnVTI032fVycI0ZbNOVFP6kqZMcXiLSYXzR2ilcwaRi6dzeGyA==" + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "z4pyMePOrl733ltTowbN565PxBw1oAr8IHmIXNDiDqd22nFpYltX9KhrNC/qBWAG1/Zx5MHX+cOYhWJQYCO/iw==" + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" }, "System.Collections.Immutable": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "/kpkgDxH984e3J3z5v/DIFi+0TWbUJXS8HNKUYBy3YnXtK09JVGs3cw5aOV6fDSw5NxbWLWlGrYjRteu6cjX3w==" + "resolved": "10.0.1", + "contentHash": "kdTe61B8P7i2M1pODC3MLbZ/CfFGjpC6c6jzxjQoB5DHZNewayCRqgFUmx3JKB6vLQtozpMQEiw+R5fO32Jv4g==" }, "patternkit.core": { "type": "Project" @@ -163,82 +201,101 @@ "patternkit.generators": { "type": "Project", "dependencies": { - "System.Collections.Immutable": "[9.0.9, )" + "System.Collections.Immutable": "[10.0.1, )" } } }, "net9.0": { "JetBrains.Annotations": { "type": "Direct", - "requested": "[2025.2.2, )", - "resolved": "2025.2.2", - "contentHash": "0X56ZRizuHdrnPpgXjWV7f2tQO1FlQg5O1967OGKnI/4ZRNOK642J8L7brM1nYvrxTTU5TP1yRyXLRLaXLPQ8A==" + "requested": "[2025.2.4, )", + "resolved": "2025.2.4", + "contentHash": "TwbgxAkXxY+vNEhNVx/QXjJ4vqxmepOjsgRvvImQPbHkHMMb4W+ahL3laMsxXKtNT7iMy+E1B3xkqao2hf1n3A==" }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "p5RKAY9POvs3axwA/AQRuJeM8AHuE8h4qbP1NxQeGm0ep46aXz1oCLAp/oOYxX1GsjStgdhHrN3XXLLXr0+b3w==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "6SIp/6Bngk4jm2W36JekZbiIbFPdE/eMUtrJEqIqHGpd1zar3jvgnwxnpWQfzUiGrkyY8q8s6V82zkkEZozghA==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" } }, "Microsoft.Extensions.Options": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "loxGGHE1FC2AefwPHzrjPq7X92LQm64qnU/whKfo6oWaceewPUVYQJBJs3S3E2qlWwnCpeZ+dGCPTX+5dgVAuQ==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "n4DCdnn2qs6V5U06Sx62FySEAZsJiJJgOzrPHDh9hPK7c2W8hEabC76F3Re3tGPjpiKa02RvB6FxZyxo8iICzg==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Configuration.Binder": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.DataAnnotations": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "Al+1FXnKKFygTXz0Zsa1+jYEPvsx5dKavlJxMXjRbrL6lmBhQZsVMhjuNB5lWvdRhdoxt5y/Q3v5kbLZLsXWdA==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "gwHO+zpVQGKK9KB3yen82Tff2zdLHHGIJzTut9L8RvoOO2RMSyYZrOmElvxu0lA4ZyaSxy8I0oNw1S/O/vkvFg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "/hymojfWbE9AlDOa0mczR44m00Jj+T3+HZO0ZnVTI032fVycI0ZbNOVFP6kqZMcXiLSYXzR2ilcwaRi6dzeGyA==" + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "z4pyMePOrl733ltTowbN565PxBw1oAr8IHmIXNDiDqd22nFpYltX9KhrNC/qBWAG1/Zx5MHX+cOYhWJQYCO/iw==" + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" }, "System.Collections.Immutable": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "/kpkgDxH984e3J3z5v/DIFi+0TWbUJXS8HNKUYBy3YnXtK09JVGs3cw5aOV6fDSw5NxbWLWlGrYjRteu6cjX3w==" + "resolved": "10.0.1", + "contentHash": "kdTe61B8P7i2M1pODC3MLbZ/CfFGjpC6c6jzxjQoB5DHZNewayCRqgFUmx3JKB6vLQtozpMQEiw+R5fO32Jv4g==" }, "patternkit.core": { "type": "Project" @@ -246,7 +303,7 @@ "patternkit.generators": { "type": "Project", "dependencies": { - "System.Collections.Immutable": "[9.0.9, )" + "System.Collections.Immutable": "[10.0.1, )" } } } diff --git a/src/PatternKit.Generators/AnalyzerReleases.Shipped.md b/src/PatternKit.Generators/AnalyzerReleases.Shipped.md new file mode 100644 index 00000000..60b59dd9 --- /dev/null +++ b/src/PatternKit.Generators/AnalyzerReleases.Shipped.md @@ -0,0 +1,3 @@ +; Shipped analyzer releases +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + diff --git a/src/PatternKit.Generators/AnalyzerReleases.Unshipped.md b/src/PatternKit.Generators/AnalyzerReleases.Unshipped.md new file mode 100644 index 00000000..6d514d26 --- /dev/null +++ b/src/PatternKit.Generators/AnalyzerReleases.Unshipped.md @@ -0,0 +1,19 @@ +; Unshipped analyzer release +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +PKCF001 | PatternKit.FactoryClass | Error | Diagnostics +PKCF002 | PatternKit.FactoryClass | Error | Diagnostics +PKCF003 | PatternKit.FactoryClass | Error | Diagnostics +PKCF004 | PatternKit.FactoryClass | Error | Diagnostics +PKCF005 | PatternKit.FactoryClass | Error | Diagnostics +PKCF006 | PatternKit.FactoryClass | Error | Diagnostics +PKKF001 | PatternKit.FactoryMethod | Error | Diagnostics +PKKF002 | PatternKit.FactoryMethod | Error | Diagnostics +PKKF003 | PatternKit.FactoryMethod | Error | Diagnostics +PKKF004 | PatternKit.FactoryMethod | Error | Diagnostics +PKKF005 | PatternKit.FactoryMethod | Error | Diagnostics +PKKF006 | PatternKit.FactoryMethod | Error | Diagnostics diff --git a/src/PatternKit.Generators/Factories/FactoriesAttributes.cs b/src/PatternKit.Generators/Factories/FactoriesAttributes.cs new file mode 100644 index 00000000..0d218e7a --- /dev/null +++ b/src/PatternKit.Generators/Factories/FactoriesAttributes.cs @@ -0,0 +1,35 @@ +namespace PatternKit.Generators.Factories; + +[AttributeUsage(AttributeTargets.Class)] +public sealed class FactoryMethodAttribute(Type keyType) : Attribute +{ + public Type KeyType { get; } = keyType; + public string CreateMethodName { get; set; } = "Create"; + public bool CaseInsensitiveStrings { get; set; } = true; +} + +[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] +public sealed class FactoryCaseAttribute(object key) : Attribute +{ + public object Key { get; } = key; +} + +[AttributeUsage(AttributeTargets.Method)] +public sealed class FactoryDefaultAttribute : Attribute +{ +} + +[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class)] +public sealed class FactoryClassAttribute(Type keyType) : Attribute +{ + public Type KeyType { get; } = keyType; + public string? FactoryTypeName { get; set; } + public bool GenerateTryCreate { get; set; } = true; + public bool GenerateEnumKeys { get; set; } = false; +} + +[AttributeUsage(AttributeTargets.Class)] +public sealed class FactoryClassKeyAttribute(object key) : Attribute +{ + public object Key { get; } = key; +} diff --git a/src/PatternKit.Generators/Factories/FactoriesGenerator.cs b/src/PatternKit.Generators/Factories/FactoriesGenerator.cs new file mode 100644 index 00000000..1bb010c9 --- /dev/null +++ b/src/PatternKit.Generators/Factories/FactoriesGenerator.cs @@ -0,0 +1,1663 @@ +using System.Collections.Immutable; +using System.Globalization; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace PatternKit.Generators.Factories; + +[Generator] +public sealed class FactoriesGenerator : IIncrementalGenerator +{ + private static readonly SymbolDisplayFormat TypeFormat = new( + globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, + genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, + miscellaneousOptions: SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier | SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + private static readonly SymbolDisplayFormat ParameterFormat = new( + globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, + genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, + memberOptions: SymbolDisplayMemberOptions.IncludeParameters, + parameterOptions: SymbolDisplayParameterOptions.IncludeParamsRefOut | SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeDefaultValue | SymbolDisplayParameterOptions.IncludeName, + miscellaneousOptions: SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier | SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var keyedFactories = context.SyntaxProvider + .ForAttributeWithMetadataName( + "PatternKit.Generators.Factories.FactoryMethodAttribute", + predicate: static (node, _) => node is ClassDeclarationSyntax, + transform: static (ctx, _) => CreateKeyedFactoryModel(ctx)) + .Where(static r => r is not null); + + context.RegisterSourceOutput(keyedFactories, static (spc, result) => + { + if (result is null) + return; + + foreach (var diag in result.Diagnostics) + { + spc.ReportDiagnostic(diag); + } + + if (result.Model is { } model) + { + spc.AddSource($"{model.Type.Name}.FactoryMethod.g.cs", EmitKeyedFactory(model)); + } + }); + + var creatorBases = context.SyntaxProvider + .ForAttributeWithMetadataName( + "PatternKit.Generators.Factories.FactoryClassAttribute", + predicate: static (node, _) => node is TypeDeclarationSyntax, + transform: static (ctx, _) => CreateCreatorBase(ctx)) + .Where(static r => r is not null); + + var creatorKeys = context.SyntaxProvider + .ForAttributeWithMetadataName( + "PatternKit.Generators.Factories.FactoryClassKeyAttribute", + predicate: static (node, _) => node is ClassDeclarationSyntax, + transform: static (ctx, _) => CreateCreatorKey(ctx)) + .Where(static r => r is not null); + + var creatorPipeline = context.CompilationProvider.Combine(creatorBases.Collect()).Combine(creatorKeys.Collect()); + + context.RegisterSourceOutput(creatorPipeline, static (spc, tuple) => + { + var compilation = tuple.Left.Item1; + var bases = tuple.Left.Item2; + var keys = tuple.Item2; + + foreach (var b in bases) + foreach (var diag in b.Diagnostics) + spc.ReportDiagnostic(diag); + + foreach (var k in keys) + foreach (var diag in k.Diagnostics) + spc.ReportDiagnostic(diag); + + var validBases = bases.Where(static b => b.Model is not null).Select(static b => b.Model!).ToImmutableArray(); + if (validBases.IsDefaultOrEmpty) + return; + + foreach (var model in BuildCreatorFactories(compilation, validBases, keys, spc)) + { + spc.AddSource($"{model.FactoryName}.FactoryClass.g.cs", EmitCreatorFactory(model)); + } + }); + } + + private static KeyedFactoryResult CreateKeyedFactoryModel(GeneratorAttributeSyntaxContext ctx) + { + var symbol = (INamedTypeSymbol)ctx.TargetSymbol; + var attribute = ctx.Attributes[0]; + var diagnostics = ImmutableArray.CreateBuilder(); + var location = ctx.TargetNode.GetLocation(); + + var keyType = attribute.ConstructorArguments.Length > 0 + ? attribute.ConstructorArguments[0].Value as ITypeSymbol + : null; + + if (keyType is null) + { + return new KeyedFactoryResult(null, diagnostics.ToImmutable()); + } + + var createName = ReadNamedArgument(attribute, "CreateMethodName", "Create"); + var caseInsensitive = ReadNamedArgument(attribute, "CaseInsensitiveStrings", true); + + if (!IsStaticPartial(symbol)) + { + diagnostics.Add(Diagnostic.Create(Diagnostics.KeyedMustBeStaticPartial, location, symbol.Name)); + return new KeyedFactoryResult(null, diagnostics.ToImmutable()); + } + + var members = GatherKeyedMembers(symbol); + if (members.Cases.Length == 0 && members.DefaultMethods.Length == 0) + { + return new KeyedFactoryResult(null, diagnostics.ToImmutable()); + } + + var buildResult = BuildKeyedModel(ctx.SemanticModel.Compilation, symbol, keyType, caseInsensitive, members, location, diagnostics); + if (buildResult is null) + { + return new KeyedFactoryResult(null, diagnostics.ToImmutable()); + } + + var model = new KeyedFactoryModel( + symbol, + keyType, + createName, + caseInsensitive, + buildResult.Value.Signature, + buildResult.Value.Cases, + buildResult.Value.DefaultCase, + buildResult.Value.HasAsync); + + return new KeyedFactoryResult(model, diagnostics.ToImmutable()); + } + + private static CreatorBaseResult CreateCreatorBase(GeneratorAttributeSyntaxContext ctx) + { + var symbol = (INamedTypeSymbol)ctx.TargetSymbol; + var attribute = ctx.Attributes[0]; + var diagnostics = ImmutableArray.CreateBuilder(); + var location = ctx.TargetNode.GetLocation(); + + var keyType = attribute.ConstructorArguments.Length > 0 + ? attribute.ConstructorArguments[0].Value as ITypeSymbol + : null; + + if (keyType is null) + { + return new CreatorBaseResult(null, diagnostics.ToImmutable()); + } + + if (!(symbol.TypeKind == TypeKind.Interface || (symbol.TypeKind == TypeKind.Class && symbol.IsAbstract))) + { + diagnostics.Add(Diagnostic.Create(Diagnostics.CreatorMustBeInterfaceOrAbstract, location, symbol.Name)); + return new CreatorBaseResult(null, diagnostics.ToImmutable()); + } + + var factoryNameOverride = ReadOptionalNamedArgument(attribute, "FactoryTypeName"); + var generateTryCreate = ReadNamedArgument(attribute, "GenerateTryCreate", true); + var generateEnumKeys = ReadNamedArgument(attribute, "GenerateEnumKeys", false); + + var model = new CreatorFactoryBase( + symbol, + keyType, + factoryNameOverride, + generateTryCreate, + generateEnumKeys); + + return new CreatorBaseResult(model, diagnostics.ToImmutable()); + } + + private static CreatorKeyResult CreateCreatorKey(GeneratorAttributeSyntaxContext ctx) + { + var symbol = (INamedTypeSymbol)ctx.TargetSymbol; + var attribute = ctx.Attributes[0]; + var diagnostics = ImmutableArray.CreateBuilder(); + var location = ctx.TargetNode.GetLocation(); + + if (!TryReadKey(attribute, out var keyConstant)) + { + diagnostics.Add(Diagnostic.Create(Diagnostics.CreatorInvalidKeyValue, location, symbol.Name)); + return new CreatorKeyResult(null, diagnostics.ToImmutable()); + } + + var model = new CreatorKeyModel(symbol, keyConstant); + return new CreatorKeyResult(model, diagnostics.ToImmutable()); + } + + private static ImmutableArray BuildCreatorFactories( + Compilation compilation, + ImmutableArray bases, + ImmutableArray keys, + SourceProductionContext context) + { + var keyMap = keys.Where(static k => k?.Model is not null).Select(static k => k!.Model!).ToImmutableArray(); + var result = ImmutableArray.CreateBuilder(); + + var validKeys = ImmutableArray.CreateBuilder(); + foreach (var key in keyMap) + { + var matches = bases.Count(b => Implements(key.Type, b.Type)); + if (matches != 1) + { + context.ReportDiagnostic(Diagnostic.Create(Diagnostics.CreatorMultipleBases, key.Type.Locations.FirstOrDefault() ?? Location.None, key.Type.Name)); + continue; + } + + validKeys.Add(key); + } + + foreach (var creatorBase in bases) + { + var baseType = creatorBase.Type; + var matchingKeys = validKeys.Where(k => Implements(k.Type, baseType)).ToImmutableArray(); + + ReportMultiBaseKeys(bases, keyMap, context); + var enumNames = creatorBase.GenerateEnumKeys ? new HashSet(StringComparer.Ordinal) : null; + var products = BuildCreatorProducts(compilation, creatorBase, baseType, matchingKeys, enumNames, context); + + if (products.IsDefaultOrEmpty) + { + continue; + } + + var factoryName = creatorBase.FactoryTypeName ?? BuildDefaultFactoryName(baseType); + var needsAsync = products.Any(static p => p.AsyncFactoryMethod is not null); + + var model = new CreatorFactoryModel( + creatorBase.Type, + creatorBase.KeyType, + factoryName, + creatorBase.GenerateTryCreate, + creatorBase.GenerateEnumKeys, + products, + needsAsync); + + result.Add(model); + } + + return result.ToImmutable(); + } + + private static KeyedMembers GatherKeyedMembers(INamedTypeSymbol symbol) + { + var caseMethods = ImmutableArray.CreateBuilder(); + var defaultMethods = ImmutableArray.CreateBuilder(); + + foreach (var method in symbol.GetMembers().OfType().Where(static m => m.MethodKind == MethodKind.Ordinary)) + { + foreach (var attr in method.GetAttributes()) + { + var name = attr.AttributeClass?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + if (name is "global::PatternKit.Generators.Factories.FactoryCaseAttribute") + { + caseMethods.Add(new FactoryCaseInfo(method, attr)); + } + else if (name is "global::PatternKit.Generators.Factories.FactoryDefaultAttribute") + { + defaultMethods.Add(method); + } + } + } + + return new KeyedMembers(caseMethods.ToImmutable(), defaultMethods.ToImmutable()); + } + + private static KeyedBuildResult? BuildKeyedModel( + Compilation compilation, + INamedTypeSymbol symbol, + ITypeSymbol keyType, + bool caseInsensitive, + KeyedMembers members, + Location location, + ImmutableArray.Builder diagnostics) + { + var signature = default(MethodSignature?); + var keyComparer = BuildKeyComparer(caseInsensitive); + var seenKeys = new Dictionary(keyComparer); + var processedCases = ImmutableArray.CreateBuilder(); + MethodInfo? defaultInfo = null; + + foreach (var entry in members.Cases) + { + if (!entry.Method.IsStatic) + { + diagnostics.Add(Diagnostic.Create(Diagnostics.KeyedMethodsMustBeStatic, entry.Method.Locations.FirstOrDefault() ?? location, entry.Method.Name)); + continue; + } + + if (!TryReadKey(entry.Attribute, out var keyConstant) || !IsKeyCompatible(compilation, keyType, keyConstant)) + { + diagnostics.Add(Diagnostic.Create(Diagnostics.KeyedInvalidKeyValue, entry.Method.Locations.FirstOrDefault() ?? location, entry.Method.Name, keyType.ToDisplayString(TypeFormat))); + continue; + } + + var methodInfo = BuildFactoryMethodInfo(entry.Method, compilation); + signature ??= methodInfo.Signature; + + if (!SignatureEquals(signature, methodInfo.Signature)) + { + diagnostics.Add(Diagnostic.Create(Diagnostics.KeyedSignatureMismatch, entry.Method.Locations.FirstOrDefault() ?? location, symbol.Name)); + continue; + } + + var wrapper = new KeyWrapper(keyConstant, keyType); + var literal = ToLiteral(keyConstant); + if (seenKeys.ContainsKey(wrapper)) + { + diagnostics.Add(Diagnostic.Create(Diagnostics.KeyedDuplicateKey, entry.Method.Locations.FirstOrDefault() ?? location, literal, symbol.Name)); + continue; + } + + seenKeys[wrapper] = entry.Method.Locations.FirstOrDefault(); + processedCases.Add(new KeyedCase(literal, methodInfo.MethodSymbol, methodInfo.AsyncKind)); + } + + if (members.DefaultMethods.Length > 1) + { + foreach (var method in members.DefaultMethods.Skip(1)) + { + diagnostics.Add(Diagnostic.Create(Diagnostics.KeyedMultipleDefaults, method.Locations.FirstOrDefault() ?? location, symbol.Name)); + } + } + + var primaryDefault = members.DefaultMethods.FirstOrDefault(); + if (primaryDefault is not null) + { + if (!primaryDefault.IsStatic) + { + diagnostics.Add(Diagnostic.Create(Diagnostics.KeyedMethodsMustBeStatic, primaryDefault.Locations.FirstOrDefault() ?? location, primaryDefault.Name)); + } + else + { + var methodInfo = BuildFactoryMethodInfo(primaryDefault, compilation); + if (signature != null && !SignatureEquals(signature, methodInfo.Signature)) + { + diagnostics.Add(Diagnostic.Create(Diagnostics.KeyedSignatureMismatch, primaryDefault.Locations.FirstOrDefault() ?? location, symbol.Name)); + } + else + { + signature ??= methodInfo.Signature; + defaultInfo = methodInfo; + } + } + } + + if (signature is null || diagnostics.Count > 0) + { + return null; + } + + var cases = processedCases.ToImmutable(); + var hasAsync = cases.Any(static c => c.AsyncKind != AsyncKind.Sync) || (defaultInfo is { AsyncKind: not AsyncKind.Sync }); + + return new KeyedBuildResult(signature, cases, defaultInfo, hasAsync); + } + + private static void ReportMultiBaseKeys( + ImmutableArray bases, + ImmutableArray keyMap, + SourceProductionContext context) + { + foreach (var key in keyMap) + { + var candidates = bases.Count(b => Implements(key.Type, b.Type)); + if (candidates > 1) + { + context.ReportDiagnostic(Diagnostic.Create(Diagnostics.CreatorMultipleBases, key.Type.Locations.FirstOrDefault() ?? Location.None, key.Type.Name)); + } + } + } + + private static ImmutableArray BuildCreatorProducts( + Compilation compilation, + CreatorFactoryBase creatorBase, + INamedTypeSymbol baseType, + ImmutableArray matchingKeys, + HashSet? enumNames, + SourceProductionContext context) + { + var seen = new Dictionary(BuildKeyComparer(false)); + var products = ImmutableArray.CreateBuilder(); + + foreach (var keyModel in matchingKeys) + { + if (keyModel.Type.IsAbstract) + { + context.ReportDiagnostic(Diagnostic.Create(Diagnostics.CreatorMustBeConcrete, keyModel.Type.Locations.FirstOrDefault() ?? Location.None, keyModel.Type.Name)); + continue; + } + + var ctor = keyModel.Type.InstanceConstructors.FirstOrDefault(c => c.Parameters.Length == 0 && c.DeclaredAccessibility == Accessibility.Public); + var asyncMethod = FindAsyncFactory(compilation, keyModel.Type, baseType); + if (ctor is null && asyncMethod is null) + { + context.ReportDiagnostic(Diagnostic.Create(Diagnostics.CreatorMissingCtor, keyModel.Type.Locations.FirstOrDefault() ?? Location.None, keyModel.Type.Name)); + continue; + } + + if (!IsKeyCompatible(compilation, creatorBase.KeyType, keyModel.Key)) + { + context.ReportDiagnostic(Diagnostic.Create(Diagnostics.CreatorInvalidKeyValue, keyModel.Type.Locations.FirstOrDefault() ?? Location.None, keyModel.Type.Name)); + continue; + } + + var literal = ToLiteral(keyModel.Key); + var wrapper = new KeyWrapper(keyModel.Key, creatorBase.KeyType); + if (seen.ContainsKey(wrapper)) + { + context.ReportDiagnostic(Diagnostic.Create(Diagnostics.CreatorDuplicateKey, keyModel.Type.Locations.FirstOrDefault() ?? Location.None, literal, creatorBase.Type.Name)); + continue; + } + + seen[wrapper] = keyModel.Type; + var enumMemberName = creatorBase.GenerateEnumKeys + ? BuildEnumMemberName(keyModel.Key, creatorBase.KeyType, enumNames!) + : null; + var creationKind = asyncMethod switch + { + null => CreationKind.Sync, + { } m when IsValueTask(compilation, m.ReturnType, out _) => CreationKind.ValueTask, + _ => CreationKind.Task + }; + + products.Add(new CreatorProduct(keyModel.Type, literal, creationKind, ctor, asyncMethod, enumMemberName)); + } + + return products.ToImmutable(); + } + + private static string EmitKeyedFactory(KeyedFactoryModel model) + { + var sb = new StringBuilder(); + sb.AppendLine("#nullable enable"); + sb.AppendLine("// "); + + var ns = model.Type.ContainingNamespace.IsGlobalNamespace + ? null + : model.Type.ContainingNamespace.ToDisplayString(); + + if (!string.IsNullOrWhiteSpace(ns)) + { + sb.Append("namespace ").Append(ns).AppendLine(";"); + sb.AppendLine(); + } + + var accessibility = AccessibilityToString(model.Type.DeclaredAccessibility); + var typeName = model.Type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); + + sb.Append(accessibility).Append(" static partial class ").Append(typeName).AppendLine(); + sb.AppendLine("{"); + + var keyTypeName = model.KeyType.ToDisplayString(TypeFormat); + var parameterList = BuildParameterList(model.Signature.Parameters); + var argumentList = BuildArgumentList(model.Signature.Parameters); + var resultTypeName = model.Signature.ResultTypeName; + + EmitKeyedCreateBody(sb, model, keyTypeName, parameterList, argumentList, resultTypeName); + EmitKeyedTryCreateBody(sb, model, keyTypeName, parameterList, argumentList, resultTypeName); + + if (model.HasAsync) + { + EmitKeyedCreateAsync(sb, model, keyTypeName, parameterList, argumentList, resultTypeName); + EmitKeyedTryCreateAsync(sb, model, keyTypeName, parameterList, argumentList, resultTypeName); + } + + sb.AppendLine("}"); + return sb.ToString(); + } + + private static void EmitKeyedCreateBody( + StringBuilder sb, + KeyedFactoryModel model, + string keyTypeName, + string parameterList, + string argumentList, + string resultTypeName) + { + sb.Append(" public static ").Append(resultTypeName).Append(' ').Append(model.CreateMethodName) + .Append('(').Append(keyTypeName).Append(" key"); + if (parameterList.Length > 0) + { + sb.Append(", ").Append(parameterList); + } + sb.AppendLine(")"); + sb.AppendLine(" {"); + + if (NeedsNullCheck(model.KeyType)) + { + sb.AppendLine(" if (key is null) throw new global::System.ArgumentNullException(nameof(key));"); + sb.AppendLine(); + } + + if (IsStringType(model.KeyType)) + { + foreach (var c in model.Cases) + { + sb.Append(" if (global::System.String.Equals(key, ").Append(c.KeyLiteral) + .Append(", global::System.StringComparison.") + .Append(model.CaseInsensitiveStrings ? "OrdinalIgnoreCase" : "Ordinal").AppendLine("))"); + sb.Append(" return ").Append(BuildSyncValue(c.AsyncKind, $"{c.Method.Name}({argumentList})")).AppendLine(";"); + sb.AppendLine(); + } + + if (model.DefaultCase is not null) + { + sb.Append(" return ").Append(BuildSyncValue(model.DefaultCase.AsyncKind, $"{model.DefaultCase.MethodSymbol.Name}({argumentList})")).AppendLine(";"); + } + else + { + sb.AppendLine(" throw new global::System.ArgumentOutOfRangeException(nameof(key));"); + } + } + else + { + sb.AppendLine(" switch (key)"); + sb.AppendLine(" {"); + foreach (var c in model.Cases) + { + sb.Append(" case ").Append(c.KeyLiteral).AppendLine(":"); + sb.Append(" return ").Append(BuildSyncValue(c.AsyncKind, $"{c.Method.Name}({argumentList})")).AppendLine(";"); + } + + if (model.DefaultCase is not null) + { + sb.AppendLine(" default:"); + sb.Append(" return ").Append(BuildSyncValue(model.DefaultCase.AsyncKind, $"{model.DefaultCase.MethodSymbol.Name}({argumentList})")).AppendLine(";"); + } + else + { + sb.AppendLine(" default:"); + sb.AppendLine(" throw new global::System.ArgumentOutOfRangeException(nameof(key));"); + } + + sb.AppendLine(" }"); + } + + sb.AppendLine(" }"); + sb.AppendLine(); + } + + private static void EmitKeyedTryCreateBody( + StringBuilder sb, + KeyedFactoryModel model, + string keyTypeName, + string parameterList, + string argumentList, + string resultTypeName) + { + sb.Append(" public static bool TryCreate(").Append(keyTypeName).Append(" key, out ").Append(resultTypeName).Append(" value"); + if (parameterList.Length > 0) + { + sb.Append(", ").Append(parameterList); + } + sb.AppendLine(")"); + sb.AppendLine(" {"); + + if (NeedsNullCheck(model.KeyType)) + { + sb.AppendLine(" if (key is null)"); + sb.AppendLine(" {"); + sb.AppendLine(" value = default!;"); + sb.AppendLine(" return false;"); + sb.AppendLine(" }"); + sb.AppendLine(); + } + + if (IsStringType(model.KeyType)) + { + foreach (var c in model.Cases) + { + sb.Append(" if (global::System.String.Equals(key, ").Append(c.KeyLiteral) + .Append(", global::System.StringComparison.") + .Append(model.CaseInsensitiveStrings ? "OrdinalIgnoreCase" : "Ordinal").AppendLine("))"); + sb.Append(" {").AppendLine(); + sb.Append(" value = ").Append(BuildSyncValue(c.AsyncKind, $"{c.Method.Name}({argumentList})")).AppendLine(";"); + sb.AppendLine(" return true;"); + sb.AppendLine(" }"); + sb.AppendLine(); + } + + if (model.DefaultCase is not null) + { + sb.Append(" value = ").Append(BuildSyncValue(model.DefaultCase.AsyncKind, $"{model.DefaultCase.MethodSymbol.Name}({argumentList})")).AppendLine(";"); + sb.AppendLine(" return false;"); + } + else + { + sb.AppendLine(" value = default!;"); + sb.AppendLine(" return false;"); + } + } + else + { + sb.AppendLine(" switch (key)"); + sb.AppendLine(" {"); + foreach (var c in model.Cases) + { + sb.Append(" case ").Append(c.KeyLiteral).AppendLine(":"); + sb.Append(" value = ").Append(BuildSyncValue(c.AsyncKind, $"{c.Method.Name}({argumentList})")).AppendLine(";"); + sb.AppendLine(" return true;"); + } + + if (model.DefaultCase is not null) + { + sb.AppendLine(" default:"); + sb.Append(" value = ").Append(BuildSyncValue(model.DefaultCase.AsyncKind, $"{model.DefaultCase.MethodSymbol.Name}({argumentList})")).AppendLine(";"); + sb.AppendLine(" return false;"); + } + else + { + sb.AppendLine(" default:"); + sb.AppendLine(" value = default!;"); + sb.AppendLine(" return false;"); + } + + sb.AppendLine(" }"); + } + + sb.AppendLine(" }"); + sb.AppendLine(); + } + + private static void EmitKeyedCreateAsync( + StringBuilder sb, + KeyedFactoryModel model, + string keyTypeName, + string parameterList, + string argumentList, + string resultTypeName) + { + var asyncReturn = $"global::System.Threading.Tasks.ValueTask<{resultTypeName}>"; + + sb.Append(" public static ").Append(asyncReturn).Append(' ').Append(model.CreateMethodName).Append("Async(").Append(keyTypeName).Append(" key"); + if (parameterList.Length > 0) + { + sb.Append(", ").Append(parameterList); + } + sb.AppendLine(")"); + sb.AppendLine(" {"); + + if (NeedsNullCheck(model.KeyType)) + { + sb.AppendLine(" if (key is null) throw new global::System.ArgumentNullException(nameof(key));"); + sb.AppendLine(); + } + + if (IsStringType(model.KeyType)) + { + foreach (var c in model.Cases) + { + sb.Append(" if (global::System.String.Equals(key, ").Append(c.KeyLiteral) + .Append(", global::System.StringComparison.") + .Append(model.CaseInsensitiveStrings ? "OrdinalIgnoreCase" : "Ordinal").AppendLine("))"); + sb.Append(" ").Append(BuildAsyncReturn(c.AsyncKind, resultTypeName, $"{c.Method.Name}({argumentList})")).AppendLine(); + sb.AppendLine(); + } + + if (model.DefaultCase is not null) + { + sb.Append(" ").Append(BuildAsyncReturn(model.DefaultCase.AsyncKind, resultTypeName, $"{model.DefaultCase.MethodSymbol.Name}({argumentList})")).AppendLine(); + } + else + { + sb.AppendLine(" throw new global::System.ArgumentOutOfRangeException(nameof(key));"); + } + } + else + { + sb.AppendLine(" switch (key)"); + sb.AppendLine(" {"); + foreach (var c in model.Cases) + { + sb.Append(" case ").Append(c.KeyLiteral).AppendLine(":"); + sb.Append(" ").Append(BuildAsyncReturn(c.AsyncKind, resultTypeName, $"{c.Method.Name}({argumentList})")).AppendLine(); + } + + if (model.DefaultCase is not null) + { + sb.AppendLine(" default:"); + sb.Append(" ").Append(BuildAsyncReturn(model.DefaultCase.AsyncKind, resultTypeName, $"{model.DefaultCase.MethodSymbol.Name}({argumentList})")).AppendLine(); + } + else + { + sb.AppendLine(" default:"); + sb.AppendLine(" throw new global::System.ArgumentOutOfRangeException(nameof(key));"); + } + + sb.AppendLine(" }"); + } + + sb.AppendLine(" }"); + sb.AppendLine(); + } + + private static void EmitKeyedTryCreateAsync( + StringBuilder sb, + KeyedFactoryModel model, + string keyTypeName, + string parameterList, + string argumentList, + string resultTypeName) + { + var asyncReturn = $"global::System.Threading.Tasks.ValueTask<(bool Success, {resultTypeName} Result)>"; + sb.Append(" public static async ").Append(asyncReturn).Append(" TryCreateAsync(").Append(keyTypeName).Append(" key"); + if (parameterList.Length > 0) + { + sb.Append(", ").Append(parameterList); + } + sb.AppendLine(")"); + sb.AppendLine(" {"); + + if (NeedsNullCheck(model.KeyType)) + { + sb.AppendLine(" if (key is null) return (false, default!);"); + sb.AppendLine(); + } + + if (IsStringType(model.KeyType)) + { + foreach (var c in model.Cases) + { + sb.Append(" if (global::System.String.Equals(key, ").Append(c.KeyLiteral) + .Append(", global::System.StringComparison.") + .Append(model.CaseInsensitiveStrings ? "OrdinalIgnoreCase" : "Ordinal").AppendLine("))"); + sb.AppendLine(" {"); + sb.Append(" return (true, ").Append(BuildAwaitedValue(c.AsyncKind, $"{c.Method.Name}({argumentList})")).AppendLine(");"); + sb.AppendLine(" }"); + sb.AppendLine(); + } + + if (model.DefaultCase is not null) + { + sb.Append(" return (false, ").Append(BuildAwaitedValue(model.DefaultCase.AsyncKind, $"{model.DefaultCase.MethodSymbol.Name}({argumentList})")).AppendLine(");"); + } + else + { + sb.AppendLine(" return (false, default!);"); + } + } + else + { + sb.AppendLine(" switch (key)"); + sb.AppendLine(" {"); + foreach (var c in model.Cases) + { + sb.Append(" case ").Append(c.KeyLiteral).AppendLine(":"); + sb.Append(" return (true, ").Append(BuildAwaitedValue(c.AsyncKind, $"{c.Method.Name}({argumentList})")).AppendLine(");"); + } + + if (model.DefaultCase is not null) + { + sb.AppendLine(" default:"); + sb.Append(" return (false, ").Append(BuildAwaitedValue(model.DefaultCase.AsyncKind, $"{model.DefaultCase.MethodSymbol.Name}({argumentList})")).AppendLine(");"); + } + else + { + sb.AppendLine(" default:"); + sb.AppendLine(" return (false, default!);"); + } + + sb.AppendLine(" }"); + } + + sb.AppendLine(" }"); + } + + private static string EmitCreatorFactory(CreatorFactoryModel model) + { + var sb = new StringBuilder(); + sb.AppendLine("#nullable enable"); + sb.AppendLine("// "); + + var ns = model.BaseType.ContainingNamespace.IsGlobalNamespace + ? null + : model.BaseType.ContainingNamespace.ToDisplayString(); + + if (!string.IsNullOrWhiteSpace(ns)) + { + sb.Append("namespace ").Append(ns).AppendLine(";"); + sb.AppendLine(); + } + + var accessibility = AccessibilityToString(model.BaseType.DeclaredAccessibility); + sb.Append(accessibility).Append(" sealed partial class ").Append(model.FactoryName).AppendLine(); + sb.AppendLine("{"); + + var keyTypeName = model.KeyType.ToDisplayString(TypeFormat); + var baseTypeName = model.BaseType.ToDisplayString(TypeFormat); + + if (model.GenerateEnumKeys) + { + EmitCreatorEnum(sb, model, keyTypeName); + } + + EmitCreatorCreate(sb, model, keyTypeName, baseTypeName); + + if (model.GenerateEnumKeys) + { + EmitCreatorCreateEnum(sb, baseTypeName); + } + + if (model.GenerateTryCreate) + { + EmitCreatorTryCreate(sb, model, keyTypeName, baseTypeName); + + if (model.GenerateEnumKeys) + { + EmitCreatorTryCreateEnum(sb, baseTypeName); + } + } + + if (model.NeedsAsync) + { + EmitCreatorCreateAsync(sb, model, keyTypeName, baseTypeName); + + if (model.GenerateEnumKeys) + { + EmitCreatorCreateAsyncEnum(sb, baseTypeName); + } + + if (model.GenerateTryCreate) + { + EmitCreatorTryCreateAsync(sb, model, keyTypeName, baseTypeName); + + if (model.GenerateEnumKeys) + { + EmitCreatorTryCreateAsyncEnum(sb, baseTypeName); + } + } + } + + sb.AppendLine("}"); + return sb.ToString(); + } + + private static void EmitCreatorCreate( + StringBuilder sb, + CreatorFactoryModel model, + string keyTypeName, + string baseTypeName) + { + sb.Append(" public ").Append(baseTypeName).Append(" Create(").Append(keyTypeName).AppendLine(" key)"); + sb.AppendLine(" {"); + + if (NeedsNullCheck(model.KeyType)) + { + sb.AppendLine(" if (key is null) throw new global::System.ArgumentNullException(nameof(key));"); + sb.AppendLine(); + } + + sb.AppendLine(" return key switch"); + sb.AppendLine(" {"); + foreach (var product in model.Products) + { + sb.Append(" ").Append(product.KeyLiteral).Append(" => ").Append(BuildCreatorCreation(product)).AppendLine(","); + } + + sb.AppendLine(" _ => throw new global::System.ArgumentOutOfRangeException(nameof(key))"); + sb.AppendLine(" };"); + sb.AppendLine(" }"); + sb.AppendLine(); + } + + private static void EmitCreatorTryCreate( + StringBuilder sb, + CreatorFactoryModel model, + string keyTypeName, + string baseTypeName) + { + sb.Append(" public bool TryCreate(").Append(keyTypeName).Append(" key, out ").Append(baseTypeName).AppendLine(" result)"); + sb.AppendLine(" {"); + + if (NeedsNullCheck(model.KeyType)) + { + sb.AppendLine(" if (key is null)"); + sb.AppendLine(" {"); + sb.AppendLine(" result = default!;"); + sb.AppendLine(" return false;"); + sb.AppendLine(" }"); + sb.AppendLine(); + } + + sb.AppendLine(" switch (key)"); + sb.AppendLine(" {"); + foreach (var product in model.Products) + { + sb.Append(" case ").Append(product.KeyLiteral).AppendLine(":"); + sb.Append(" result = ").Append(BuildCreatorCreation(product)).AppendLine(";"); + sb.AppendLine(" return true;"); + } + + sb.AppendLine(" default:"); + sb.AppendLine(" result = default!;"); + sb.AppendLine(" return false;"); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine(); + } + + private static void EmitCreatorCreateAsync( + StringBuilder sb, + CreatorFactoryModel model, + string keyTypeName, + string baseTypeName) + { + sb.Append(" public ").Append("global::System.Threading.Tasks.ValueTask<").Append(baseTypeName).Append("> CreateAsync(").Append(keyTypeName).AppendLine(" key)"); + sb.AppendLine(" {"); + + if (NeedsNullCheck(model.KeyType)) + { + sb.AppendLine(" if (key is null) throw new global::System.ArgumentNullException(nameof(key));"); + sb.AppendLine(); + } + + sb.AppendLine(" return key switch"); + sb.AppendLine(" {"); + foreach (var product in model.Products) + { + sb.Append(" ").Append(product.KeyLiteral).Append(" => ").Append(BuildCreatorAsyncReturn(product, baseTypeName)).AppendLine(","); + } + sb.AppendLine(" _ => throw new global::System.ArgumentOutOfRangeException(nameof(key))"); + sb.AppendLine(" };"); + sb.AppendLine(" }"); + sb.AppendLine(); + } + + private static void EmitCreatorTryCreateAsync( + StringBuilder sb, + CreatorFactoryModel model, + string keyTypeName, + string baseTypeName) + { + sb.Append(" public async global::System.Threading.Tasks.ValueTask<(bool Success, ").Append(baseTypeName).Append(" Result)> TryCreateAsync(").Append(keyTypeName).AppendLine(" key)"); + sb.AppendLine(" {"); + + if (NeedsNullCheck(model.KeyType)) + { + sb.AppendLine(" if (key is null) return (false, default!);"); + sb.AppendLine(); + } + + sb.AppendLine(" switch (key)"); + sb.AppendLine(" {"); + foreach (var product in model.Products) + { + sb.Append(" case ").Append(product.KeyLiteral).AppendLine(":"); + sb.Append(" return (true, ").Append(BuildCreatorAwaited(product)).AppendLine(");"); + } + sb.AppendLine(" default:"); + sb.AppendLine(" return (false, default!);"); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + } + + private static void EmitCreatorEnum( + StringBuilder sb, + CreatorFactoryModel model, + string keyTypeName) + { + sb.AppendLine(" public enum Keys"); + sb.AppendLine(" {"); + foreach (var product in model.Products) + { + sb.Append(" ").Append(product.EnumMemberName).Append(',').AppendLine(); + } + sb.AppendLine(" }"); + sb.AppendLine(); + + sb.Append(" private static ").Append(keyTypeName).Append(" MapKey(Keys key) => key switch").AppendLine(); + sb.AppendLine(" {"); + foreach (var product in model.Products) + { + sb.Append(" Keys.").Append(product.EnumMemberName).Append(" => ").Append(product.KeyLiteral).AppendLine(","); + } + sb.AppendLine(" _ => throw new global::System.ArgumentOutOfRangeException(nameof(key))"); + sb.AppendLine(" };"); + sb.AppendLine(); + } + + private static void EmitCreatorCreateEnum( + StringBuilder sb, + string baseTypeName) + { + sb.Append(" public ").Append(baseTypeName).AppendLine(" Create(Keys key)"); + sb.AppendLine(" {"); + sb.AppendLine(" return Create(MapKey(key));"); + sb.AppendLine(" }"); + sb.AppendLine(); + } + + private static void EmitCreatorTryCreateEnum( + StringBuilder sb, + string baseTypeName) + { + sb.Append(" public bool TryCreate(Keys key, out ").Append(baseTypeName).AppendLine(" result)"); + sb.AppendLine(" {"); + sb.AppendLine(" return TryCreate(MapKey(key), out result);"); + sb.AppendLine(" }"); + sb.AppendLine(); + } + + private static void EmitCreatorCreateAsyncEnum( + StringBuilder sb, + string baseTypeName) + { + sb.Append(" public ").Append("global::System.Threading.Tasks.ValueTask<").Append(baseTypeName).AppendLine("> CreateAsync(Keys key)"); + sb.AppendLine(" {"); + sb.AppendLine(" return CreateAsync(MapKey(key));"); + sb.AppendLine(" }"); + sb.AppendLine(); + } + + private static void EmitCreatorTryCreateAsyncEnum( + StringBuilder sb, + string baseTypeName) + { + sb.Append(" public ").Append("global::System.Threading.Tasks.ValueTask<(bool Success, ").Append(baseTypeName).AppendLine(" Result)> TryCreateAsync(Keys key)"); + sb.AppendLine(" {"); + sb.AppendLine(" return TryCreateAsync(MapKey(key));"); + sb.AppendLine(" }"); + sb.AppendLine(); + } + + private static bool IsStaticPartial(INamedTypeSymbol typeSymbol) + { + if (!typeSymbol.IsStatic) + return false; + + foreach (var syntaxRef in typeSymbol.DeclaringSyntaxReferences) + { + if (syntaxRef.GetSyntax() is TypeDeclarationSyntax typeDecl && + typeDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword))) + { + return true; + } + } + + return false; + } + + private static bool TryReadKey(AttributeData attribute, out TypedConstant constant) + { + if (attribute.ConstructorArguments.Length == 1) + { + constant = attribute.ConstructorArguments[0]; + return true; + } + + constant = default; + return false; + } + + private static MethodInfo BuildFactoryMethodInfo(IMethodSymbol method, Compilation compilation) + { + var signature = BuildSignature(method, compilation); + var asyncKind = signature.AsyncKind; + return new MethodInfo(method, signature, asyncKind); + } + + private static MethodSignature BuildSignature(IMethodSymbol method, Compilation compilation) + { + var asyncKind = AsyncKind.Sync; + var returnType = method.ReturnType; + var resultType = returnType; + + if (IsValueTask(compilation, method.ReturnType, out var vt)) + { + asyncKind = AsyncKind.ValueTask; + resultType = vt!; + } + else if (IsTask(compilation, method.ReturnType, out var taskType)) + { + asyncKind = AsyncKind.Task; + resultType = taskType!; + } + + return new MethodSignature(resultType, resultType.ToDisplayString(TypeFormat), method.Parameters, asyncKind); + } + + private static bool SignatureEquals(MethodSignature left, MethodSignature right) + { + if (!SymbolEqualityComparer.Default.Equals(left.ResultType, right.ResultType)) + return false; + + if (left.Parameters.Length != right.Parameters.Length) + return false; + + for (var i = 0; i < left.Parameters.Length; i++) + { + if (!SymbolEqualityComparer.Default.Equals(left.Parameters[i].Type, right.Parameters[i].Type) || + left.Parameters[i].RefKind != right.Parameters[i].RefKind) + { + return false; + } + } + + return true; + } + + private static string BuildParameterList(ImmutableArray parameters) + { + if (parameters.Length == 0) + return string.Empty; + + var builder = new StringBuilder(); + for (var i = 0; i < parameters.Length; i++) + { + if (i > 0) + builder.Append(", "); + builder.Append(parameters[i].ToDisplayString(ParameterFormat)); + } + + return builder.ToString(); + } + + private static string BuildArgumentList(ImmutableArray parameters) + { + if (parameters.Length == 0) + return string.Empty; + + var builder = new StringBuilder(); + for (var i = 0; i < parameters.Length; i++) + { + if (i > 0) + builder.Append(", "); + + var prefix = parameters[i].RefKind switch + { + RefKind.Out => "out ", + RefKind.Ref => "ref ", + RefKind.In => "in ", + _ => string.Empty + }; + builder.Append(prefix).Append(parameters[i].Name); + } + + return builder.ToString(); + } + + private static bool IsTask(Compilation compilation, ITypeSymbol type, out ITypeSymbol? result) + { + if (type is INamedTypeSymbol named && + named.IsGenericType && + SymbolEqualityComparer.Default.Equals(named.ConstructedFrom, compilation.GetTypeByMetadataName("System.Threading.Tasks.Task`1"))) + { + result = named.TypeArguments[0]; + return true; + } + + result = null; + return false; + } + + private static bool IsValueTask(Compilation compilation, ITypeSymbol type, out ITypeSymbol? result) + { + if (type is INamedTypeSymbol named && + named.IsGenericType && + SymbolEqualityComparer.Default.Equals(named.ConstructedFrom, compilation.GetTypeByMetadataName("System.Threading.Tasks.ValueTask`1"))) + { + result = named.TypeArguments[0]; + return true; + } + + result = null; + return false; + } + + private static bool NeedsNullCheck(ITypeSymbol keyType) + { + return keyType.NullableAnnotation == NullableAnnotation.Annotated || keyType.IsReferenceType; + } + + private static bool IsStringType(ITypeSymbol type) + { + return type.SpecialType == SpecialType.System_String; + } + + private static bool IsKeyCompatible(Compilation compilation, ITypeSymbol keyType, TypedConstant key) + { + if (key.IsNull) + { + return keyType.IsReferenceType || keyType.NullableAnnotation == NullableAnnotation.Annotated; + } + + if (key.Type is null) + return false; + + var conversion = compilation.ClassifyConversion(key.Type, keyType); + return conversion.Exists && conversion.IsImplicit; + } + + private static string ToLiteral(TypedConstant constant) + { + if (constant.IsNull) + return "null"; + + if (constant.Kind == TypedConstantKind.Enum && constant.Type is not null && constant.Value is not null) + { + var enumType = constant.Type.ToDisplayString(TypeFormat); + var field = constant.Type.GetMembers().OfType().FirstOrDefault(f => f.HasConstantValue && Equals(f.ConstantValue, constant.Value)); + if (field is not null) + { + return $"{enumType}.{field.Name}"; + } + + return $"({enumType}){constant.ToCSharpString()}"; + } + + return constant.ToCSharpString(); + } + + private static string AccessibilityToString(Accessibility accessibility) + { + return accessibility switch + { + Accessibility.Private => "private", + Accessibility.Internal => "internal", + Accessibility.Protected => "protected", + Accessibility.ProtectedAndInternal => "private protected", + Accessibility.ProtectedOrInternal => "protected internal", + _ => "public" + }; + } + + private static IEqualityComparer BuildKeyComparer(bool caseInsensitiveStrings) + { + return new KeyWrapperComparer(caseInsensitiveStrings); + } + + private static bool Implements(INamedTypeSymbol type, INamedTypeSymbol target) + { + if (SymbolEqualityComparer.Default.Equals(type, target)) + return true; + + if (type.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, target))) + return true; + + var current = type.BaseType; + while (current is not null) + { + if (SymbolEqualityComparer.Default.Equals(current, target)) + return true; + current = current.BaseType; + } + + return false; + } + + private static string BuildDefaultFactoryName(INamedTypeSymbol baseType) + { + var name = baseType.Name; + if (baseType.TypeKind == TypeKind.Interface && name.StartsWith("I", StringComparison.Ordinal) && name.Length > 1 && char.IsUpper(name[1])) + { + name = name.Substring(1); + } + return name + "Factory"; + } + + private static string BuildEnumMemberName(TypedConstant key, ITypeSymbol keyType, HashSet existing) + { + var raw = key.IsNull ? "Null" : key.Kind switch + { + TypedConstantKind.Enum when key.Type is not null && key.Value is not null => key.Type + .GetMembers() + .OfType() + .FirstOrDefault(f => f.HasConstantValue && Equals(f.ConstantValue, key.Value)) + ?.Name ?? key.ToCSharpString(), + TypedConstantKind.Primitive when key.Value is string s => s, + TypedConstantKind.Primitive when key.Value is bool b => b ? "True" : "False", + TypedConstantKind.Primitive when key.Value is IFormattable f => f.ToString(null, CultureInfo.InvariantCulture), + _ => key.ToCSharpString() + }; + + var builder = new StringBuilder(); + var capitalize = true; + foreach (var ch in raw) + { + if (char.IsLetterOrDigit(ch)) + { + builder.Append(capitalize ? char.ToUpperInvariant(ch) : ch); + capitalize = false; + } + else + { + capitalize = true; + } + } + + var candidate = builder.Length == 0 ? "Key" : builder.ToString(); + if (!SyntaxFacts.IsIdentifierStartCharacter(candidate[0])) + { + candidate = "Key" + candidate; + } + + var unique = candidate; + var suffix = 1; + while (existing.Contains(unique)) + { + unique = candidate + (++suffix).ToString(CultureInfo.InvariantCulture); + } + + existing.Add(unique); + return unique; + } + + private static string BuildAsyncReturn(AsyncKind asyncKind, string resultTypeName, string invocation) + { + return asyncKind switch + { + AsyncKind.ValueTask => $"return {invocation};", + AsyncKind.Task => $"return new global::System.Threading.Tasks.ValueTask<{resultTypeName}>({invocation});", + _ => $"return global::System.Threading.Tasks.ValueTask.FromResult<{resultTypeName}>({invocation});" + }; + } + + private static string BuildSyncValue(AsyncKind asyncKind, string invocation) + { + return asyncKind switch + { + AsyncKind.Sync => invocation, + _ => $"{invocation}.GetAwaiter().GetResult()" + }; + } + + private static string BuildAwaitedValue(AsyncKind asyncKind, string invocation) + { + return asyncKind switch + { + AsyncKind.Sync => invocation, + _ => $"await {invocation}" + }; + } + + private static string BuildCreatorCreation(CreatorProduct product) + { + if (product.Constructor is not null) + { + return $"new {product.Type.ToDisplayString(TypeFormat)}()"; + } + + if (product.AsyncFactoryMethod is not null) + { + var call = $"{product.AsyncFactoryMethod.ContainingType.ToDisplayString(TypeFormat)}.{product.AsyncFactoryMethod.Name}()"; + return $"{call}.GetAwaiter().GetResult()"; + } + + return "default!"; + } + + private static string BuildCreatorAsyncReturn(CreatorProduct product, string baseTypeName) + { + if (product.AsyncFactoryMethod is not null) + { + var call = $"{product.AsyncFactoryMethod.ContainingType.ToDisplayString(TypeFormat)}.{product.AsyncFactoryMethod.Name}()"; + return product.Kind switch + { + CreationKind.ValueTask => call, + CreationKind.Task => $"new global::System.Threading.Tasks.ValueTask<{baseTypeName}>({call})", + _ => call + }; + } + + return $"global::System.Threading.Tasks.ValueTask.FromResult<{baseTypeName}>(new {product.Type.ToDisplayString(TypeFormat)}())"; + } + + private static string BuildCreatorAwaited(CreatorProduct product) + { + if (product.AsyncFactoryMethod is not null) + { + var call = $"{product.AsyncFactoryMethod.ContainingType.ToDisplayString(TypeFormat)}.{product.AsyncFactoryMethod.Name}()"; + return $"await {call}"; + } + + return $"new {product.Type.ToDisplayString(TypeFormat)}()"; + } + + private static string ReadNamedArgument(AttributeData attribute, string name, string fallback) + { + foreach (var arg in attribute.NamedArguments) + { + if (arg.Key == name && arg.Value.Value is string s) + { + return s; + } + } + return fallback; + } + + private static bool ReadNamedArgument(AttributeData attribute, string name, bool fallback) + { + foreach (var arg in attribute.NamedArguments) + { + if (arg.Key == name && arg.Value.Value is bool b) + { + return b; + } + } + return fallback; + } + + private static string? ReadOptionalNamedArgument(AttributeData attribute, string name) + { + foreach (var arg in attribute.NamedArguments) + { + if (arg.Key == name && arg.Value.Value is string s) + { + return s; + } + } + return null; + } + + private static IMethodSymbol? FindAsyncFactory(Compilation compilation, INamedTypeSymbol type, INamedTypeSymbol baseType) + { + foreach (var method in type.GetMembers().OfType()) + { + if (!method.IsStatic || method.Name != "CreateAsync") + continue; + if (method.Parameters.Length != 0) + continue; + + var returnType = method.ReturnType; + if (returnType is not INamedTypeSymbol named || !named.IsGenericType) + continue; + + var targetType = named.TypeArguments[0]; + var conversion = compilation.ClassifyConversion(targetType, baseType); + if (!conversion.Exists) + continue; + + return method; + } + + return null; + } + + private static class Diagnostics + { + public static readonly DiagnosticDescriptor KeyedMustBeStaticPartial = new( + "PKKF001", + "Factory method host must be static partial", + "Type '{0}' must be a static partial class to use [FactoryMethod]", + "PatternKit.FactoryMethod", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor KeyedSignatureMismatch = new( + "PKKF002", + "Factory methods must share the same signature", + "All [FactoryCase] and [FactoryDefault] methods for '{0}' must have matching signatures", + "PatternKit.FactoryMethod", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor KeyedDuplicateKey = new( + "PKKF003", + "Duplicate factory key", + "Key '{0}' is already defined for factory '{1}'", + "PatternKit.FactoryMethod", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor KeyedMultipleDefaults = new( + "PKKF004", + "Multiple default factory methods", + "Only one [FactoryDefault] method may be declared for '{0}'", + "PatternKit.FactoryMethod", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor KeyedInvalidKeyValue = new( + "PKKF005", + "Invalid factory key value", + "Factory key for '{0}' is not compatible with the declared KeyType '{1}'", + "PatternKit.FactoryMethod", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor KeyedMethodsMustBeStatic = new( + "PKKF006", + "Factory methods must be static", + "Factory method '{0}' must be static", + "PatternKit.FactoryMethod", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor CreatorMustBeInterfaceOrAbstract = new( + "PKCF001", + "Factory class base must be abstract", + "[FactoryClass] can only be applied to an interface or abstract class ('{0}')", + "PatternKit.FactoryClass", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor CreatorMultipleBases = new( + "PKCF002", + "FactoryClassKey type maps to multiple bases", + "Type '{0}' must implement exactly one [FactoryClass] base type", + "PatternKit.FactoryClass", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor CreatorMustBeConcrete = new( + "PKCF003", + "FactoryClassKey type must be concrete", + "Type '{0}' must be non-abstract and supply an accessible constructor", + "PatternKit.FactoryClass", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor CreatorDuplicateKey = new( + "PKCF004", + "Duplicate factory key", + "Key '{0}' is already defined for factory '{1}'", + "PatternKit.FactoryClass", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor CreatorInvalidKeyValue = new( + "PKCF005", + "Invalid factory key value", + "Key value for '{0}' is not compatible with the declared KeyType", + "PatternKit.FactoryClass", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor CreatorMissingCtor = new( + "PKCF006", + "Missing accessible constructor", + "Type '{0}' must expose a parameterless constructor or static CreateAsync method", + "PatternKit.FactoryClass", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + } + + private sealed record FactoryCaseInfo(IMethodSymbol Method, AttributeData Attribute); + + private sealed record MethodInfo(IMethodSymbol MethodSymbol, MethodSignature Signature, AsyncKind AsyncKind); + + private sealed record MethodSignature(ITypeSymbol ResultType, string ResultTypeName, ImmutableArray Parameters, AsyncKind AsyncKind); + + private sealed record KeyedFactoryResult(KeyedFactoryModel? Model, ImmutableArray Diagnostics); + + private sealed record KeyedMembers(ImmutableArray Cases, ImmutableArray DefaultMethods); + + private readonly record struct KeyedBuildResult( + MethodSignature Signature, + ImmutableArray Cases, + MethodInfo? DefaultCase, + bool HasAsync); + + private sealed record KeyedFactoryModel( + INamedTypeSymbol Type, + ITypeSymbol KeyType, + string CreateMethodName, + bool CaseInsensitiveStrings, + MethodSignature Signature, + ImmutableArray Cases, + MethodInfo? DefaultCase, + bool HasAsync); + + private sealed record KeyedCase(string KeyLiteral, IMethodSymbol Method, AsyncKind AsyncKind); + + private sealed record CreatorBaseResult(CreatorFactoryBase? Model, ImmutableArray Diagnostics); + + private sealed record CreatorFactoryBase( + INamedTypeSymbol Type, + ITypeSymbol KeyType, + string? FactoryTypeName, + bool GenerateTryCreate, + bool GenerateEnumKeys); + + private sealed record CreatorKeyResult(CreatorKeyModel? Model, ImmutableArray Diagnostics); + + private sealed record CreatorKeyModel(INamedTypeSymbol Type, TypedConstant Key); + + private sealed record CreatorFactoryModel( + INamedTypeSymbol BaseType, + ITypeSymbol KeyType, + string FactoryName, + bool GenerateTryCreate, + bool GenerateEnumKeys, + ImmutableArray Products, + bool NeedsAsync); + + private sealed record CreatorProduct( + INamedTypeSymbol Type, + string KeyLiteral, + CreationKind Kind, + IMethodSymbol? Constructor, + IMethodSymbol? AsyncFactoryMethod, + string? EnumMemberName); + + private sealed record KeyWrapper(TypedConstant Constant, ITypeSymbol KeyType); + + private sealed class KeyWrapperComparer : IEqualityComparer + { + private readonly bool _caseInsensitive; + + public KeyWrapperComparer(bool caseInsensitive) + { + _caseInsensitive = caseInsensitive; + } + + public bool Equals(KeyWrapper? x, KeyWrapper? y) + { + if (ReferenceEquals(x, y)) + return true; + if (x is null || y is null) + return false; + + if (x.Constant.IsNull && y.Constant.IsNull) + return true; + + if (IsStringType(x.KeyType)) + { + var left = x.Constant.Value?.ToString() ?? string.Empty; + var right = y.Constant.Value?.ToString() ?? string.Empty; + return string.Equals(left, right, _caseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); + } + + return Equals(x.Constant.Value, y.Constant.Value); + } + + public int GetHashCode(KeyWrapper obj) + { + if (obj.Constant.IsNull) + return 0; + + if (IsStringType(obj.KeyType)) + { + var value = obj.Constant.Value?.ToString() ?? string.Empty; + return _caseInsensitive + ? StringComparer.OrdinalIgnoreCase.GetHashCode(value) + : StringComparer.Ordinal.GetHashCode(value); + } + + return obj.Constant.Value?.GetHashCode() ?? 0; + } + } + + private enum AsyncKind + { + Sync, + Task, + ValueTask + } + + private enum CreationKind + { + Sync, + Task, + ValueTask + } +} diff --git a/src/PatternKit.Generators/PatternKit.Generators.csproj b/src/PatternKit.Generators/PatternKit.Generators.csproj index 6b963b7b..e556d790 100644 --- a/src/PatternKit.Generators/PatternKit.Generators.csproj +++ b/src/PatternKit.Generators/PatternKit.Generators.csproj @@ -8,6 +8,7 @@ true true true + @@ -17,8 +18,13 @@ - - + + + + + + + diff --git a/src/PatternKit.Generators/Shims/NetStandard20.cs b/src/PatternKit.Generators/Shims/NetStandard20.cs new file mode 100644 index 00000000..254d2b93 --- /dev/null +++ b/src/PatternKit.Generators/Shims/NetStandard20.cs @@ -0,0 +1,6 @@ +#if NETSTANDARD2_0 +namespace System.Runtime.CompilerServices +{ + internal class IsExternalInit { } +} +#endif diff --git a/src/PatternKit.Generators/packages.lock.json b/src/PatternKit.Generators/packages.lock.json index 4eb9db90..baf8f1c8 100644 --- a/src/PatternKit.Generators/packages.lock.json +++ b/src/PatternKit.Generators/packages.lock.json @@ -10,20 +10,20 @@ }, "Microsoft.CodeAnalysis.CSharp": { "type": "Direct", - "requested": "[4.14.0, )", - "resolved": "4.14.0", - "contentHash": "568a6wcTivauIhbeWcCwfWwIn7UV7MeHEBvFB2uzGIpM2OhJ4eM/FZ8KS0yhPoNxnSpjGzz7x7CIjTxhslojQA==", + "requested": "[5.0.0, )", + "resolved": "5.0.0", + "contentHash": "5DSyJ9bk+ATuDy7fp2Zt0mJStDVKbBoiz1DyfAwSa+k4H4IwykAUcV3URelw5b8/iVbfSaOwkwmPUZH6opZKCw==", "dependencies": { "Microsoft.CodeAnalysis.Analyzers": "3.11.0", - "Microsoft.CodeAnalysis.Common": "[4.14.0]", - "System.Buffers": "4.5.1", + "Microsoft.CodeAnalysis.Common": "[5.0.0]", + "System.Buffers": "4.6.0", "System.Collections.Immutable": "9.0.0", - "System.Memory": "4.5.5", - "System.Numerics.Vectors": "4.5.0", + "System.Memory": "4.6.0", + "System.Numerics.Vectors": "4.6.0", "System.Reflection.Metadata": "9.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encoding.CodePages": "7.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" + "System.Runtime.CompilerServices.Unsafe": "6.1.0", + "System.Text.Encoding.CodePages": "8.0.0", + "System.Threading.Tasks.Extensions": "4.6.0" } }, "NETStandard.Library": { @@ -37,28 +37,28 @@ }, "System.Collections.Immutable": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "/kpkgDxH984e3J3z5v/DIFi+0TWbUJXS8HNKUYBy3YnXtK09JVGs3cw5aOV6fDSw5NxbWLWlGrYjRteu6cjX3w==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "kdTe61B8P7i2M1pODC3MLbZ/CfFGjpC6c6jzxjQoB5DHZNewayCRqgFUmx3JKB6vLQtozpMQEiw+R5fO32Jv4g==", "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "Microsoft.CodeAnalysis.Common": { "type": "Transitive", - "resolved": "4.14.0", - "contentHash": "PC3tuwZYnC+idaPuoC/AZpEdwrtX7qFpmnrfQkgobGIWiYmGi5MCRtl5mx6QrfMGQpK78X2lfIEoZDLg/qnuHg==", + "resolved": "5.0.0", + "contentHash": "ZXRAdvH6GiDeHRyd3q/km8Z44RoM6FBWHd+gen/la81mVnAdHTEsEkO5J0TCNXBymAcx5UYKt5TvgKBhaLJEow==", "dependencies": { "Microsoft.CodeAnalysis.Analyzers": "3.11.0", - "System.Buffers": "4.5.1", + "System.Buffers": "4.6.0", "System.Collections.Immutable": "9.0.0", - "System.Memory": "4.5.5", - "System.Numerics.Vectors": "4.5.0", + "System.Memory": "4.6.0", + "System.Numerics.Vectors": "4.6.0", "System.Reflection.Metadata": "9.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encoding.CodePages": "7.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" + "System.Runtime.CompilerServices.Unsafe": "6.1.0", + "System.Text.Encoding.CodePages": "8.0.0", + "System.Threading.Tasks.Extensions": "4.6.0" } }, "Microsoft.NETCore.Platforms": { @@ -68,23 +68,23 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Reflection.Metadata": { "type": "Transitive", @@ -97,13 +97,13 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "LSyCblMpvOe0N3E+8e0skHcrIhgV2huaNcjUUEa8hRtgEAm36aGkRoC8Jxlb6Ra6GSfF29ftduPNywin8XolzQ==", + "resolved": "8.0.0", + "contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==", "dependencies": { "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "6.0.0" @@ -111,10 +111,10 @@ }, "System.Threading.Tasks.Extensions": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "resolved": "4.6.0", + "contentHash": "I5G6Y8jb0xRtGUC9Lahy7FUvlYlnGMMkbuKAQBy8Jb7Y6Yn8OlBEiUOY0PqZ0hy6Ua8poVA1ui1tAIiXNxGdsg==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Runtime.CompilerServices.Unsafe": "6.1.0" } } } diff --git a/test/PatternKit.Examples.Tests/PatternKit.Examples.Tests.csproj b/test/PatternKit.Examples.Tests/PatternKit.Examples.Tests.csproj index 4594cc4e..e76614b3 100644 --- a/test/PatternKit.Examples.Tests/PatternKit.Examples.Tests.csproj +++ b/test/PatternKit.Examples.Tests/PatternKit.Examples.Tests.csproj @@ -10,16 +10,16 @@ - - - - - - - + + + + + + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/PatternKit.Examples.Tests/packages.lock.json b/test/PatternKit.Examples.Tests/packages.lock.json index d3a28156..9678fd9c 100644 --- a/test/PatternKit.Examples.Tests/packages.lock.json +++ b/test/PatternKit.Examples.Tests/packages.lock.json @@ -10,65 +10,65 @@ }, "JetBrains.Annotations": { "type": "Direct", - "requested": "[2025.2.2, )", - "resolved": "2025.2.2", - "contentHash": "0X56ZRizuHdrnPpgXjWV7f2tQO1FlQg5O1967OGKnI/4ZRNOK642J8L7brM1nYvrxTTU5TP1yRyXLRLaXLPQ8A==" + "requested": "[2025.2.4, )", + "resolved": "2025.2.4", + "contentHash": "TwbgxAkXxY+vNEhNVx/QXjJ4vqxmepOjsgRvvImQPbHkHMMb4W+ahL3laMsxXKtNT7iMy+E1B3xkqao2hf1n3A==" }, "Microsoft.Extensions.Configuration": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "w87wF/90/VI0ZQBhf4rbMEeyEy0vi2WKjFmACsNAKNaorY+ZlVz7ddyXkbADvaWouMKffNmR0yQOGcrvSSvKGg==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "zQV2WOSP+3z1EuK91ULxfGgo2Y75bTRnmJHp08+w/YXAyekZutX/qCd88/HOMNh35MDW9mJJJxPpMPS+1Rww8A==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" } }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "ORA4dICNz7cuwupPkjXpSuoiK6GMg0aygInBIQCCFEimwoHntRKdJqB59faxq2HHJuTPW3NsZm5EjN5P5Zh6nQ==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "qmoQkVZcbm4/gFpted3W3Y+1kTATZTcUhV3mRkbtpfBXlxWCHwh/2oMffVcCruaGOfJuEnyAsGyaSUouSdECOw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.9", - "Microsoft.Extensions.FileProviders.Abstractions": "9.0.9", - "Microsoft.Extensions.Logging.Abstractions": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1" } }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.14.1, )", - "resolved": "17.14.1", - "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "requested": "[18.0.1, )", + "resolved": "18.0.1", + "contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==", "dependencies": { - "Microsoft.CodeCoverage": "17.14.1", - "Microsoft.TestPlatform.TestHost": "17.14.1" + "Microsoft.CodeCoverage": "18.0.1", + "Microsoft.TestPlatform.TestHost": "18.0.1" } }, "System.Collections.Immutable": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "/kpkgDxH984e3J3z5v/DIFi+0TWbUJXS8HNKUYBy3YnXtK09JVGs3cw5aOV6fDSw5NxbWLWlGrYjRteu6cjX3w==" + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "kdTe61B8P7i2M1pODC3MLbZ/CfFGjpC6c6jzxjQoB5DHZNewayCRqgFUmx3JKB6vLQtozpMQEiw+R5fO32Jv4g==" }, "TinyBDD.Xunit": { "type": "Direct", - "requested": "[0.11.0, )", - "resolved": "0.11.0", - "contentHash": "gJu6gSe4sp24+ie+dQ+6bY9SrauQk1UcV02qQsucYcqajwM8B8TCUTwsFjt8ns1xtbOVPRZOlyoWEOBP6NR/Pw==", + "requested": "[0.12.1, )", + "resolved": "0.12.1", + "contentHash": "1V1RAF1OGY7m9kGzhhFpe4NzZO2bd8vSEoL9AlFhEWQ0GIeCCJ/a5Bq4Eqw00n9op/ZHUtb9Retk9XfQSkvKFw==", "dependencies": { - "TinyBDD": "0.11.0", + "TinyBDD": "0.12.1", "xunit.abstractions": "2.0.3", "xunit.extensibility.core": "2.9.3" } @@ -95,110 +95,111 @@ }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[3.1.4, )", - "resolved": "3.1.4", - "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + "requested": "[3.1.5, )", + "resolved": "3.1.5", + "contentHash": "tKi7dSTwP4m5m9eXPM2Ime4Kn7xNf4x4zT9sdLO/G4hZVnQCRiMTWoSZqI/pYTVeI27oPPqHBKYI/DjJ9GsYgA==" }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + "resolved": "18.0.1", + "contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA==" }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "p5RKAY9POvs3axwA/AQRuJeM8AHuE8h4qbP1NxQeGm0ep46aXz1oCLAp/oOYxX1GsjStgdhHrN3XXLLXr0+b3w==", + "resolved": "10.0.1", + "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "6SIp/6Bngk4jm2W36JekZbiIbFPdE/eMUtrJEqIqHGpd1zar3jvgnwxnpWQfzUiGrkyY8q8s6V82zkkEZozghA==", + "resolved": "10.0.1", + "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9" + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "/hymojfWbE9AlDOa0mczR44m00Jj+T3+HZO0ZnVTI032fVycI0ZbNOVFP6kqZMcXiLSYXzR2ilcwaRi6dzeGyA==" + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" }, "Microsoft.Extensions.Diagnostics.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "YHGmxccrVZ2Ar3eI+/NdbOHkd1/HzrHvmQ5yBsp0Gl7jTyBe6qcXNYjUt9v9JIO+Z14la44+YYEe63JSqs1fYg==", + "resolved": "10.0.1", + "contentHash": "QMoMrkNpnQym5mpfdxfxpRDuqLpsOuztguFvzH9p+Ex+do+uLFoi7UkAsBO4e9/tNR3eMFraFf2fOAi2cp3jjA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" } }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "M1ZhL9QkBQ/k6l/Wjgcli5zrV86HzytQ+gQiNtk9vs9Ge1fb17KKZil9T6jd15p2x/BGfXpup7Hg55CC0kkfig==", + "resolved": "10.0.1", + "contentHash": "+b3DligYSZuoWltU5YdbMpIEUHNZPgPrzWfNiIuDkMdqOl93UxYB5KzS3lgpRfTXJhTNpo/CZ8w/sTkDTPDdxQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "FEgpSF+Z9StMvrsSViaybOBwR0f0ZZxDm8xV5cSOFiXN/t+ys+rwAlTd/6yG7Ld1gfppgvLcMasZry3GsI9lGA==", + "resolved": "10.0.1", + "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "loxGGHE1FC2AefwPHzrjPq7X92LQm64qnU/whKfo6oWaceewPUVYQJBJs3S3E2qlWwnCpeZ+dGCPTX+5dgVAuQ==", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "n4DCdnn2qs6V5U06Sx62FySEAZsJiJJgOzrPHDh9hPK7c2W8hEabC76F3Re3tGPjpiKa02RvB6FxZyxo8iICzg==", + "resolved": "10.0.1", + "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Configuration.Binder": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.DataAnnotations": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "Al+1FXnKKFygTXz0Zsa1+jYEPvsx5dKavlJxMXjRbrL6lmBhQZsVMhjuNB5lWvdRhdoxt5y/Q3v5kbLZLsXWdA==", + "resolved": "10.0.1", + "contentHash": "gwHO+zpVQGKK9KB3yen82Tff2zdLHHGIJzTut9L8RvoOO2RMSyYZrOmElvxu0lA4ZyaSxy8I0oNw1S/O/vkvFg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "z4pyMePOrl733ltTowbN565PxBw1oAr8IHmIXNDiDqd22nFpYltX9KhrNC/qBWAG1/Zx5MHX+cOYhWJQYCO/iw==" + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==", + "resolved": "18.0.1", + "contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==", "dependencies": { "System.Reflection.Metadata": "8.0.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "resolved": "18.0.1", + "contentHash": "uDJKAEjFTaa2wHdWlfo6ektyoh+WD4/Eesrwb4FpBFKsLGehhACVnwwTI4qD3FrIlIEPlxdXg3SyrYRIcO+RRQ==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Microsoft.TestPlatform.ObjectModel": "18.0.1", "Newtonsoft.Json": "13.0.3" } }, @@ -217,8 +218,8 @@ }, "TinyBDD": { "type": "Transitive", - "resolved": "0.11.0", - "contentHash": "q953tZ+XQP9BHEiLbxa55PxhqMZ7Knb/US+NLYC4+kQ+l4ODih+T1O0Fgl34XinFOI2xKOZgQcYyTnFB+yTBxQ==" + "resolved": "0.12.1", + "contentHash": "pf5G0SU/Gl65OAQoPbZC8tlAOvLM6/WowdmhTVJv8eov8ywgGaQbM7Z3mpF64P+u4x/0HGKYuqcNlimGqoQbTw==" }, "xunit.abstractions": { "type": "Transitive", @@ -258,12 +259,13 @@ "patternkit.examples": { "type": "Project", "dependencies": { - "JetBrains.Annotations": "[2025.2.2, )", - "Microsoft.Extensions.Configuration.Abstractions": "[9.0.9, )", - "Microsoft.Extensions.Configuration.Binder": "[9.0.9, )", - "Microsoft.Extensions.Options": "[9.0.9, )", - "Microsoft.Extensions.Options.ConfigurationExtensions": "[9.0.9, )", - "Microsoft.Extensions.Options.DataAnnotations": "[9.0.9, )", + "JetBrains.Annotations": "[2025.2.4, )", + "Microsoft.Extensions.Configuration.Abstractions": "[10.0.1, )", + "Microsoft.Extensions.Configuration.Binder": "[10.0.1, )", + "Microsoft.Extensions.DependencyInjection": "[10.0.1, )", + "Microsoft.Extensions.Options": "[10.0.1, )", + "Microsoft.Extensions.Options.ConfigurationExtensions": "[10.0.1, )", + "Microsoft.Extensions.Options.DataAnnotations": "[10.0.1, )", "PatternKit.Core": "[1.0.0, )", "PatternKit.Generators": "[1.0.0, )" } @@ -271,7 +273,7 @@ "patternkit.generators": { "type": "Project", "dependencies": { - "System.Collections.Immutable": "[9.0.9, )" + "System.Collections.Immutable": "[10.0.1, )" } } }, @@ -284,65 +286,65 @@ }, "JetBrains.Annotations": { "type": "Direct", - "requested": "[2025.2.2, )", - "resolved": "2025.2.2", - "contentHash": "0X56ZRizuHdrnPpgXjWV7f2tQO1FlQg5O1967OGKnI/4ZRNOK642J8L7brM1nYvrxTTU5TP1yRyXLRLaXLPQ8A==" + "requested": "[2025.2.4, )", + "resolved": "2025.2.4", + "contentHash": "TwbgxAkXxY+vNEhNVx/QXjJ4vqxmepOjsgRvvImQPbHkHMMb4W+ahL3laMsxXKtNT7iMy+E1B3xkqao2hf1n3A==" }, "Microsoft.Extensions.Configuration": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "w87wF/90/VI0ZQBhf4rbMEeyEy0vi2WKjFmACsNAKNaorY+ZlVz7ddyXkbADvaWouMKffNmR0yQOGcrvSSvKGg==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "zQV2WOSP+3z1EuK91ULxfGgo2Y75bTRnmJHp08+w/YXAyekZutX/qCd88/HOMNh35MDW9mJJJxPpMPS+1Rww8A==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" } }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "ORA4dICNz7cuwupPkjXpSuoiK6GMg0aygInBIQCCFEimwoHntRKdJqB59faxq2HHJuTPW3NsZm5EjN5P5Zh6nQ==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "qmoQkVZcbm4/gFpted3W3Y+1kTATZTcUhV3mRkbtpfBXlxWCHwh/2oMffVcCruaGOfJuEnyAsGyaSUouSdECOw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.9", - "Microsoft.Extensions.FileProviders.Abstractions": "9.0.9", - "Microsoft.Extensions.Logging.Abstractions": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1" } }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.14.1, )", - "resolved": "17.14.1", - "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "requested": "[18.0.1, )", + "resolved": "18.0.1", + "contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==", "dependencies": { - "Microsoft.CodeCoverage": "17.14.1", - "Microsoft.TestPlatform.TestHost": "17.14.1" + "Microsoft.CodeCoverage": "18.0.1", + "Microsoft.TestPlatform.TestHost": "18.0.1" } }, "System.Collections.Immutable": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "/kpkgDxH984e3J3z5v/DIFi+0TWbUJXS8HNKUYBy3YnXtK09JVGs3cw5aOV6fDSw5NxbWLWlGrYjRteu6cjX3w==" + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "kdTe61B8P7i2M1pODC3MLbZ/CfFGjpC6c6jzxjQoB5DHZNewayCRqgFUmx3JKB6vLQtozpMQEiw+R5fO32Jv4g==" }, "TinyBDD.Xunit": { "type": "Direct", - "requested": "[0.11.0, )", - "resolved": "0.11.0", - "contentHash": "gJu6gSe4sp24+ie+dQ+6bY9SrauQk1UcV02qQsucYcqajwM8B8TCUTwsFjt8ns1xtbOVPRZOlyoWEOBP6NR/Pw==", + "requested": "[0.12.1, )", + "resolved": "0.12.1", + "contentHash": "1V1RAF1OGY7m9kGzhhFpe4NzZO2bd8vSEoL9AlFhEWQ0GIeCCJ/a5Bq4Eqw00n9op/ZHUtb9Retk9XfQSkvKFw==", "dependencies": { - "TinyBDD": "0.11.0", + "TinyBDD": "0.12.1", "xunit.abstractions": "2.0.3", "xunit.extensibility.core": "2.9.3" } @@ -369,112 +371,113 @@ }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[3.1.4, )", - "resolved": "3.1.4", - "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + "requested": "[3.1.5, )", + "resolved": "3.1.5", + "contentHash": "tKi7dSTwP4m5m9eXPM2Ime4Kn7xNf4x4zT9sdLO/G4hZVnQCRiMTWoSZqI/pYTVeI27oPPqHBKYI/DjJ9GsYgA==" }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + "resolved": "18.0.1", + "contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA==" }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "p5RKAY9POvs3axwA/AQRuJeM8AHuE8h4qbP1NxQeGm0ep46aXz1oCLAp/oOYxX1GsjStgdhHrN3XXLLXr0+b3w==", + "resolved": "10.0.1", + "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "6SIp/6Bngk4jm2W36JekZbiIbFPdE/eMUtrJEqIqHGpd1zar3jvgnwxnpWQfzUiGrkyY8q8s6V82zkkEZozghA==", + "resolved": "10.0.1", + "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9" + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "/hymojfWbE9AlDOa0mczR44m00Jj+T3+HZO0ZnVTI032fVycI0ZbNOVFP6kqZMcXiLSYXzR2ilcwaRi6dzeGyA==" + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" }, "Microsoft.Extensions.Diagnostics.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "YHGmxccrVZ2Ar3eI+/NdbOHkd1/HzrHvmQ5yBsp0Gl7jTyBe6qcXNYjUt9v9JIO+Z14la44+YYEe63JSqs1fYg==", + "resolved": "10.0.1", + "contentHash": "QMoMrkNpnQym5mpfdxfxpRDuqLpsOuztguFvzH9p+Ex+do+uLFoi7UkAsBO4e9/tNR3eMFraFf2fOAi2cp3jjA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9", - "System.Diagnostics.DiagnosticSource": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "System.Diagnostics.DiagnosticSource": "10.0.1" } }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "M1ZhL9QkBQ/k6l/Wjgcli5zrV86HzytQ+gQiNtk9vs9Ge1fb17KKZil9T6jd15p2x/BGfXpup7Hg55CC0kkfig==", + "resolved": "10.0.1", + "contentHash": "+b3DligYSZuoWltU5YdbMpIEUHNZPgPrzWfNiIuDkMdqOl93UxYB5KzS3lgpRfTXJhTNpo/CZ8w/sTkDTPDdxQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "FEgpSF+Z9StMvrsSViaybOBwR0f0ZZxDm8xV5cSOFiXN/t+ys+rwAlTd/6yG7Ld1gfppgvLcMasZry3GsI9lGA==", + "resolved": "10.0.1", + "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "System.Diagnostics.DiagnosticSource": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "System.Diagnostics.DiagnosticSource": "10.0.1" } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "loxGGHE1FC2AefwPHzrjPq7X92LQm64qnU/whKfo6oWaceewPUVYQJBJs3S3E2qlWwnCpeZ+dGCPTX+5dgVAuQ==", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "n4DCdnn2qs6V5U06Sx62FySEAZsJiJJgOzrPHDh9hPK7c2W8hEabC76F3Re3tGPjpiKa02RvB6FxZyxo8iICzg==", + "resolved": "10.0.1", + "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Configuration.Binder": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.DataAnnotations": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "Al+1FXnKKFygTXz0Zsa1+jYEPvsx5dKavlJxMXjRbrL6lmBhQZsVMhjuNB5lWvdRhdoxt5y/Q3v5kbLZLsXWdA==", + "resolved": "10.0.1", + "contentHash": "gwHO+zpVQGKK9KB3yen82Tff2zdLHHGIJzTut9L8RvoOO2RMSyYZrOmElvxu0lA4ZyaSxy8I0oNw1S/O/vkvFg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "z4pyMePOrl733ltTowbN565PxBw1oAr8IHmIXNDiDqd22nFpYltX9KhrNC/qBWAG1/Zx5MHX+cOYhWJQYCO/iw==" + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==", + "resolved": "18.0.1", + "contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==", "dependencies": { "System.Reflection.Metadata": "8.0.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "resolved": "18.0.1", + "contentHash": "uDJKAEjFTaa2wHdWlfo6ektyoh+WD4/Eesrwb4FpBFKsLGehhACVnwwTI4qD3FrIlIEPlxdXg3SyrYRIcO+RRQ==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Microsoft.TestPlatform.ObjectModel": "18.0.1", "Newtonsoft.Json": "13.0.3" } }, @@ -485,8 +488,8 @@ }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "8hy61dsFYYSDjT9iTAfygGMU3A0EAnG69x5FUXeKsCjMhBmtTBt4UMUEW3ipprFoorOW6Jw/7hDMjXtlrsOvVQ==" + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==" }, "System.Reflection.Metadata": { "type": "Transitive", @@ -498,8 +501,8 @@ }, "TinyBDD": { "type": "Transitive", - "resolved": "0.11.0", - "contentHash": "q953tZ+XQP9BHEiLbxa55PxhqMZ7Knb/US+NLYC4+kQ+l4ODih+T1O0Fgl34XinFOI2xKOZgQcYyTnFB+yTBxQ==" + "resolved": "0.12.1", + "contentHash": "pf5G0SU/Gl65OAQoPbZC8tlAOvLM6/WowdmhTVJv8eov8ywgGaQbM7Z3mpF64P+u4x/0HGKYuqcNlimGqoQbTw==" }, "xunit.abstractions": { "type": "Transitive", @@ -539,12 +542,13 @@ "patternkit.examples": { "type": "Project", "dependencies": { - "JetBrains.Annotations": "[2025.2.2, )", - "Microsoft.Extensions.Configuration.Abstractions": "[9.0.9, )", - "Microsoft.Extensions.Configuration.Binder": "[9.0.9, )", - "Microsoft.Extensions.Options": "[9.0.9, )", - "Microsoft.Extensions.Options.ConfigurationExtensions": "[9.0.9, )", - "Microsoft.Extensions.Options.DataAnnotations": "[9.0.9, )", + "JetBrains.Annotations": "[2025.2.4, )", + "Microsoft.Extensions.Configuration.Abstractions": "[10.0.1, )", + "Microsoft.Extensions.Configuration.Binder": "[10.0.1, )", + "Microsoft.Extensions.DependencyInjection": "[10.0.1, )", + "Microsoft.Extensions.Options": "[10.0.1, )", + "Microsoft.Extensions.Options.ConfigurationExtensions": "[10.0.1, )", + "Microsoft.Extensions.Options.DataAnnotations": "[10.0.1, )", "PatternKit.Core": "[1.0.0, )", "PatternKit.Generators": "[1.0.0, )" } @@ -552,7 +556,7 @@ "patternkit.generators": { "type": "Project", "dependencies": { - "System.Collections.Immutable": "[9.0.9, )" + "System.Collections.Immutable": "[10.0.1, )" } } }, @@ -565,65 +569,65 @@ }, "JetBrains.Annotations": { "type": "Direct", - "requested": "[2025.2.2, )", - "resolved": "2025.2.2", - "contentHash": "0X56ZRizuHdrnPpgXjWV7f2tQO1FlQg5O1967OGKnI/4ZRNOK642J8L7brM1nYvrxTTU5TP1yRyXLRLaXLPQ8A==" + "requested": "[2025.2.4, )", + "resolved": "2025.2.4", + "contentHash": "TwbgxAkXxY+vNEhNVx/QXjJ4vqxmepOjsgRvvImQPbHkHMMb4W+ahL3laMsxXKtNT7iMy+E1B3xkqao2hf1n3A==" }, "Microsoft.Extensions.Configuration": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "w87wF/90/VI0ZQBhf4rbMEeyEy0vi2WKjFmACsNAKNaorY+ZlVz7ddyXkbADvaWouMKffNmR0yQOGcrvSSvKGg==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "zQV2WOSP+3z1EuK91ULxfGgo2Y75bTRnmJHp08+w/YXAyekZutX/qCd88/HOMNh35MDW9mJJJxPpMPS+1Rww8A==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" } }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "ORA4dICNz7cuwupPkjXpSuoiK6GMg0aygInBIQCCFEimwoHntRKdJqB59faxq2HHJuTPW3NsZm5EjN5P5Zh6nQ==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "qmoQkVZcbm4/gFpted3W3Y+1kTATZTcUhV3mRkbtpfBXlxWCHwh/2oMffVcCruaGOfJuEnyAsGyaSUouSdECOw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.9", - "Microsoft.Extensions.FileProviders.Abstractions": "9.0.9", - "Microsoft.Extensions.Logging.Abstractions": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1" } }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.14.1, )", - "resolved": "17.14.1", - "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "requested": "[18.0.1, )", + "resolved": "18.0.1", + "contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==", "dependencies": { - "Microsoft.CodeCoverage": "17.14.1", - "Microsoft.TestPlatform.TestHost": "17.14.1" + "Microsoft.CodeCoverage": "18.0.1", + "Microsoft.TestPlatform.TestHost": "18.0.1" } }, "System.Collections.Immutable": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "/kpkgDxH984e3J3z5v/DIFi+0TWbUJXS8HNKUYBy3YnXtK09JVGs3cw5aOV6fDSw5NxbWLWlGrYjRteu6cjX3w==" + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "kdTe61B8P7i2M1pODC3MLbZ/CfFGjpC6c6jzxjQoB5DHZNewayCRqgFUmx3JKB6vLQtozpMQEiw+R5fO32Jv4g==" }, "TinyBDD.Xunit": { "type": "Direct", - "requested": "[0.11.0, )", - "resolved": "0.11.0", - "contentHash": "gJu6gSe4sp24+ie+dQ+6bY9SrauQk1UcV02qQsucYcqajwM8B8TCUTwsFjt8ns1xtbOVPRZOlyoWEOBP6NR/Pw==", + "requested": "[0.12.1, )", + "resolved": "0.12.1", + "contentHash": "1V1RAF1OGY7m9kGzhhFpe4NzZO2bd8vSEoL9AlFhEWQ0GIeCCJ/a5Bq4Eqw00n9op/ZHUtb9Retk9XfQSkvKFw==", "dependencies": { - "TinyBDD": "0.11.0", + "TinyBDD": "0.12.1", "xunit.abstractions": "2.0.3", "xunit.extensibility.core": "2.9.3" } @@ -650,110 +654,113 @@ }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[3.1.4, )", - "resolved": "3.1.4", - "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + "requested": "[3.1.5, )", + "resolved": "3.1.5", + "contentHash": "tKi7dSTwP4m5m9eXPM2Ime4Kn7xNf4x4zT9sdLO/G4hZVnQCRiMTWoSZqI/pYTVeI27oPPqHBKYI/DjJ9GsYgA==" }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + "resolved": "18.0.1", + "contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA==" }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "p5RKAY9POvs3axwA/AQRuJeM8AHuE8h4qbP1NxQeGm0ep46aXz1oCLAp/oOYxX1GsjStgdhHrN3XXLLXr0+b3w==", + "resolved": "10.0.1", + "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "6SIp/6Bngk4jm2W36JekZbiIbFPdE/eMUtrJEqIqHGpd1zar3jvgnwxnpWQfzUiGrkyY8q8s6V82zkkEZozghA==", + "resolved": "10.0.1", + "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9" + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "/hymojfWbE9AlDOa0mczR44m00Jj+T3+HZO0ZnVTI032fVycI0ZbNOVFP6kqZMcXiLSYXzR2ilcwaRi6dzeGyA==" + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" }, "Microsoft.Extensions.Diagnostics.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "YHGmxccrVZ2Ar3eI+/NdbOHkd1/HzrHvmQ5yBsp0Gl7jTyBe6qcXNYjUt9v9JIO+Z14la44+YYEe63JSqs1fYg==", + "resolved": "10.0.1", + "contentHash": "QMoMrkNpnQym5mpfdxfxpRDuqLpsOuztguFvzH9p+Ex+do+uLFoi7UkAsBO4e9/tNR3eMFraFf2fOAi2cp3jjA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "System.Diagnostics.DiagnosticSource": "10.0.1" } }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "M1ZhL9QkBQ/k6l/Wjgcli5zrV86HzytQ+gQiNtk9vs9Ge1fb17KKZil9T6jd15p2x/BGfXpup7Hg55CC0kkfig==", + "resolved": "10.0.1", + "contentHash": "+b3DligYSZuoWltU5YdbMpIEUHNZPgPrzWfNiIuDkMdqOl93UxYB5KzS3lgpRfTXJhTNpo/CZ8w/sTkDTPDdxQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "FEgpSF+Z9StMvrsSViaybOBwR0f0ZZxDm8xV5cSOFiXN/t+ys+rwAlTd/6yG7Ld1gfppgvLcMasZry3GsI9lGA==", + "resolved": "10.0.1", + "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "System.Diagnostics.DiagnosticSource": "10.0.1" } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "loxGGHE1FC2AefwPHzrjPq7X92LQm64qnU/whKfo6oWaceewPUVYQJBJs3S3E2qlWwnCpeZ+dGCPTX+5dgVAuQ==", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "n4DCdnn2qs6V5U06Sx62FySEAZsJiJJgOzrPHDh9hPK7c2W8hEabC76F3Re3tGPjpiKa02RvB6FxZyxo8iICzg==", + "resolved": "10.0.1", + "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Configuration.Binder": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.DataAnnotations": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "Al+1FXnKKFygTXz0Zsa1+jYEPvsx5dKavlJxMXjRbrL6lmBhQZsVMhjuNB5lWvdRhdoxt5y/Q3v5kbLZLsXWdA==", + "resolved": "10.0.1", + "contentHash": "gwHO+zpVQGKK9KB3yen82Tff2zdLHHGIJzTut9L8RvoOO2RMSyYZrOmElvxu0lA4ZyaSxy8I0oNw1S/O/vkvFg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "z4pyMePOrl733ltTowbN565PxBw1oAr8IHmIXNDiDqd22nFpYltX9KhrNC/qBWAG1/Zx5MHX+cOYhWJQYCO/iw==" + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==", + "resolved": "18.0.1", + "contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==", "dependencies": { "System.Reflection.Metadata": "8.0.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "resolved": "18.0.1", + "contentHash": "uDJKAEjFTaa2wHdWlfo6ektyoh+WD4/Eesrwb4FpBFKsLGehhACVnwwTI4qD3FrIlIEPlxdXg3SyrYRIcO+RRQ==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Microsoft.TestPlatform.ObjectModel": "18.0.1", "Newtonsoft.Json": "13.0.3" } }, @@ -762,6 +769,11 @@ "resolved": "13.0.3", "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==" + }, "System.Reflection.Metadata": { "type": "Transitive", "resolved": "8.0.0", @@ -772,8 +784,8 @@ }, "TinyBDD": { "type": "Transitive", - "resolved": "0.11.0", - "contentHash": "q953tZ+XQP9BHEiLbxa55PxhqMZ7Knb/US+NLYC4+kQ+l4ODih+T1O0Fgl34XinFOI2xKOZgQcYyTnFB+yTBxQ==" + "resolved": "0.12.1", + "contentHash": "pf5G0SU/Gl65OAQoPbZC8tlAOvLM6/WowdmhTVJv8eov8ywgGaQbM7Z3mpF64P+u4x/0HGKYuqcNlimGqoQbTw==" }, "xunit.abstractions": { "type": "Transitive", @@ -813,12 +825,13 @@ "patternkit.examples": { "type": "Project", "dependencies": { - "JetBrains.Annotations": "[2025.2.2, )", - "Microsoft.Extensions.Configuration.Abstractions": "[9.0.9, )", - "Microsoft.Extensions.Configuration.Binder": "[9.0.9, )", - "Microsoft.Extensions.Options": "[9.0.9, )", - "Microsoft.Extensions.Options.ConfigurationExtensions": "[9.0.9, )", - "Microsoft.Extensions.Options.DataAnnotations": "[9.0.9, )", + "JetBrains.Annotations": "[2025.2.4, )", + "Microsoft.Extensions.Configuration.Abstractions": "[10.0.1, )", + "Microsoft.Extensions.Configuration.Binder": "[10.0.1, )", + "Microsoft.Extensions.DependencyInjection": "[10.0.1, )", + "Microsoft.Extensions.Options": "[10.0.1, )", + "Microsoft.Extensions.Options.ConfigurationExtensions": "[10.0.1, )", + "Microsoft.Extensions.Options.DataAnnotations": "[10.0.1, )", "PatternKit.Core": "[1.0.0, )", "PatternKit.Generators": "[1.0.0, )" } @@ -826,7 +839,7 @@ "patternkit.generators": { "type": "Project", "dependencies": { - "System.Collections.Immutable": "[9.0.9, )" + "System.Collections.Immutable": "[10.0.1, )" } } } diff --git a/test/PatternKit.Generators.Tests/FactoriesGeneratorTests.cs b/test/PatternKit.Generators.Tests/FactoriesGeneratorTests.cs new file mode 100644 index 00000000..5e42243b --- /dev/null +++ b/test/PatternKit.Generators.Tests/FactoriesGeneratorTests.cs @@ -0,0 +1,584 @@ +using System.Runtime.Loader; +using PatternKit.Generators.Factories; + +namespace PatternKit.Generators.Tests; + +public class FactoriesGeneratorTests +{ + [Fact] + public void FactoryMethod_Generates_Sync_Create_And_TryCreate() + { + const string user = """ + using PatternKit.Generators.Factories; + + namespace Demo; + + [FactoryMethod(typeof(string), CreateMethodName = "Make")] + public static partial class MimeFactory + { + [FactoryCase("json")] + public static string Json() => "application/json"; + + [FactoryDefault] + public static string Default() => "application/octet-stream"; + } + + public static class Runner + { + public static string Run() + { + var okJson = MimeFactory.TryCreate("json", out var json); + var okOther = MimeFactory.TryCreate("other", out var other); + return $"{MimeFactory.Make("json")}|{okJson}|{json}|{okOther}|{other}"; + } + } + """; + + var comp = RoslynTestHelpers.CreateCompilation( + user, + assemblyName: nameof(FactoryMethod_Generates_Sync_Create_And_TryCreate)); + + var gen = new FactoriesGenerator(); + _ = RoslynTestHelpers.Run(comp, gen, out var run, out var updated); + + Assert.All(run.Results, r => Assert.True(r.Diagnostics.IsEmpty)); + Assert.Contains("MimeFactory.FactoryMethod.g.cs", + run.Results.SelectMany(r => r.GeneratedSources).Select(g => g.HintName)); + + using var pe = new MemoryStream(); + using var pdb = new MemoryStream(); + var emit = updated.Emit(pe, pdb); + Assert.True(emit.Success, string.Join("\n", emit.Diagnostics)); + pe.Position = 0; + pdb.Position = 0; + var asm = AssemblyLoadContext.Default.LoadFromStream(pe, pdb); + var runResult = asm.GetType("Demo.Runner")!.GetMethod("Run")!.Invoke(null, null); + Assert.Equal("application/json|True|application/json|False|application/octet-stream", runResult); + } + + [Fact] + public async Task FactoryMethod_Async_Methods_Use_ValueTask() + { + const string user = """ + using System.Threading.Tasks; + using PatternKit.Generators.Factories; + + namespace Demo; + + [FactoryMethod(typeof(int))] + public static partial class NumberNames + { + [FactoryCase(1)] + public static ValueTask OneAsync() => ValueTask.FromResult("one"); + + [FactoryDefault] + public static Task DefaultAsync() => Task.FromResult("other"); + } + """; + + var comp = RoslynTestHelpers.CreateCompilation( + user, + assemblyName: nameof(FactoryMethod_Async_Methods_Use_ValueTask)); + + var gen = new FactoriesGenerator(); + _ = RoslynTestHelpers.Run(comp, gen, out var run, out var updated); + + Assert.All(run.Results, r => Assert.True(r.Diagnostics.IsEmpty)); + var hintNames = run.Results.SelectMany(r => r.GeneratedSources).Select(g => g.HintName).ToArray(); + Assert.Contains("NumberNames.FactoryMethod.g.cs", hintNames); + + using var pe = new MemoryStream(); + var emit = updated.Emit(pe); + Assert.True(emit.Success, string.Join("\n", emit.Diagnostics)); + + pe.Position = 0; + var asm = AssemblyLoadContext.Default.LoadFromStream(pe); + var type = asm.GetType("Demo.NumberNames")!; + var createAsync = type.GetMethod("CreateAsync")!; + var tryCreateAsync = type.GetMethod("TryCreateAsync")!; + + var task = (ValueTask)createAsync.Invoke(null, new object?[] { 1 })!; + Assert.Equal("one", await task); + + var tuple = await ((ValueTask<(bool Success, string Result)>)tryCreateAsync.Invoke(null, [2])!); + Assert.False(tuple.Success); + Assert.Equal("other", tuple.Result); + } + + [Fact] + public void FactoryClass_Generates_Create_And_TryCreate() + { + const string user = """ + using PatternKit.Generators.Factories; + + namespace Demo; + + [FactoryClass(typeof(string))] + public interface IMessage { } + + [FactoryClassKey("email")] + public class Email : IMessage { } + + [FactoryClassKey("sms")] + public class Sms : IMessage { } + """; + + var comp = RoslynTestHelpers.CreateCompilation( + user, + assemblyName: nameof(FactoryClass_Generates_Create_And_TryCreate)); + + var gen = new FactoriesGenerator(); + _ = RoslynTestHelpers.Run(comp, gen, out var run, out var updated); + + Assert.All(run.Results, r => Assert.True(r.Diagnostics.IsEmpty)); + Assert.Contains("MessageFactory.FactoryClass.g.cs", + run.Results.SelectMany(r => r.GeneratedSources).Select(g => g.HintName)); + + using var pe = new MemoryStream(); + using var pdb = new MemoryStream(); + var emit = updated.Emit(pe, pdb); + Assert.True(emit.Success, string.Join("\n", emit.Diagnostics)); + + pe.Position = 0; + pdb.Position = 0; + var asm = AssemblyLoadContext.Default.LoadFromStream(pe, pdb); + var factoryType = asm.GetType("Demo.MessageFactory")!; + dynamic factory = Activator.CreateInstance(factoryType)!; + + var email = factoryType.GetMethod("Create")!.Invoke(factory, new object?[] { "email" }); + Assert.Equal("Demo.Email", email!.GetType().FullName); + + var args = new object?[] { "sms", null }; + var success = (bool)factoryType.GetMethod("TryCreate")!.Invoke(factory, args)!; + Assert.True(success); + Assert.Equal("Demo.Sms", args[1]!.GetType().FullName); + } + + [Fact] + public void FactoryClass_Emits_Enum_Keys_And_Overloads() + { + const string user = """ + using PatternKit.Generators.Factories; + + namespace Demo; + + [FactoryClass(typeof(string), GenerateEnumKeys = true, FactoryTypeName = "NotificationFactory")] + public interface INotification { } + + [FactoryClassKey("email")] + public class Email : INotification { } + + [FactoryClassKey("sms")] + public class Sms : INotification { } + """; + + var comp = RoslynTestHelpers.CreateCompilation( + user, + assemblyName: nameof(FactoryClass_Emits_Enum_Keys_And_Overloads)); + + var gen = new FactoriesGenerator(); + _ = RoslynTestHelpers.Run(comp, gen, out var run, out var updated); + + Assert.All(run.Results, r => Assert.True(r.Diagnostics.IsEmpty)); + + using var pe = new MemoryStream(); + using var pdb = new MemoryStream(); + var emit = updated.Emit(pe, pdb); + Assert.True(emit.Success, string.Join("\n", emit.Diagnostics)); + + pe.Position = 0; + pdb.Position = 0; + var asm = AssemblyLoadContext.Default.LoadFromStream(pe, pdb); + var factoryType = asm.GetType("Demo.NotificationFactory")!; + var enumType = factoryType.GetNestedType("Keys")!; + var baseType = asm.GetType("Demo.INotification")!; + dynamic factory = Activator.CreateInstance(factoryType)!; + + var emailKey = Enum.Parse(enumType, "Email"); + var smsKey = Enum.Parse(enumType, "Sms"); + + var createEnum = factoryType.GetMethod("Create", new[] { enumType })!; + var email = createEnum.Invoke(factory, new[] { emailKey }); + Assert.Equal("Demo.Email", email!.GetType().FullName); + + var tryCreateEnum = factoryType.GetMethods() + .Single(m => m.Name == "TryCreate" && m.GetParameters()[0].ParameterType == enumType); + object?[] args = [smsKey, null]; + var success = (bool)tryCreateEnum.Invoke(factory, args)!; + Assert.True(success); + Assert.Equal("Demo.Sms", args[1]!.GetType().FullName); + } + + [Fact] + public void FactoryMethod_Requires_Static_Partial() + { + const string user = """ + using PatternKit.Generators.Factories; + + namespace Demo; + + [FactoryMethod(typeof(string))] + public class NotStatic + { + [FactoryCase("a")] + public static string A() => "a"; + } + """; + + var comp = RoslynTestHelpers.CreateCompilation( + user, + assemblyName: nameof(FactoryMethod_Requires_Static_Partial)); + + var gen = new FactoriesGenerator(); + _ = RoslynTestHelpers.Run(comp, gen, out var run, out _); + + var diags = run.Results.Single().Diagnostics; + Assert.Contains(diags, d => d.Id == "PKKF001"); + } + + [Fact] + public void FactoryMethod_Detects_Signature_Mismatch_And_Duplicate_Keys() + { + const string user = """ + using PatternKit.Generators.Factories; + + namespace Demo; + + [FactoryMethod(typeof(int))] + public static partial class BadFactory + { + [FactoryCase(1)] + public static string One() => "one"; + + [FactoryCase(2)] + public static int Two() => 2; + + [FactoryCase(1)] + public static string Duplicate() => "dup"; + } + """; + + var comp = RoslynTestHelpers.CreateCompilation( + user, + assemblyName: nameof(FactoryMethod_Detects_Signature_Mismatch_And_Duplicate_Keys)); + + var gen = new FactoriesGenerator(); + _ = RoslynTestHelpers.Run(comp, gen, out var run, out _); + + var diags = run.Results.Single().Diagnostics; + Assert.Contains(diags, d => d.Id == "PKKF002"); + Assert.Contains(diags, d => d.Id == "PKKF003"); + } + + [Fact] + public void FactoryMethod_Rejects_NonStatic_Methods() + { + const string user = """ + using PatternKit.Generators.Factories; + + namespace Demo; + + [FactoryMethod(typeof(int))] + public static partial class BadMethods + { + [FactoryCase(1)] + public string Instance() => "oops"; + } + """; + + var comp = RoslynTestHelpers.CreateCompilation( + user, + assemblyName: nameof(FactoryMethod_Rejects_NonStatic_Methods)); + + var gen = new FactoriesGenerator(); + _ = RoslynTestHelpers.Run(comp, gen, out var run, out _); + + var diags = run.Results.Single().Diagnostics; + Assert.Contains(diags, d => d.Id == "PKKF006"); + } + + [Fact] + public void FactoryMethod_Honors_String_Case_Sensitivity() + { + const string user = """ + using PatternKit.Generators.Factories; + + namespace Demo; + + [FactoryMethod(typeof(string), CaseInsensitiveStrings = false)] + public static partial class MimeFactory + { + [FactoryCase("json")] + public static string Json() => "application/json"; + } + + public static class Runner + { + public static string Run() + { + var foundInsensitive = MimeFactory.TryCreate("JSON", out var value); + return $"{foundInsensitive}|{value ?? ""}"; + } + } + """; + + var comp = RoslynTestHelpers.CreateCompilation( + user, + assemblyName: nameof(FactoryMethod_Honors_String_Case_Sensitivity)); + + var gen = new FactoriesGenerator(); + _ = RoslynTestHelpers.Run(comp, gen, out var run, out var updated); + + Assert.All(run.Results, r => Assert.True(r.Diagnostics.IsEmpty)); + + using var pe = new MemoryStream(); + var emit = updated.Emit(pe); + Assert.True(emit.Success, string.Join("\n", emit.Diagnostics)); + + pe.Position = 0; + var asm = AssemblyLoadContext.Default.LoadFromStream(pe); + var runResult = asm.GetType("Demo.Runner")!.GetMethod("Run")!.Invoke(null, null); + Assert.Equal("False|", runResult); + } + + [Fact] + public void FactoryClass_Requires_Interface_Or_Abstract_Base() + { + const string user = """ + using PatternKit.Generators.Factories; + + namespace Demo; + + [FactoryClass(typeof(int))] + public class ConcreteBase { } + """; + + var comp = RoslynTestHelpers.CreateCompilation( + user, + assemblyName: nameof(FactoryClass_Requires_Interface_Or_Abstract_Base)); + + var gen = new FactoriesGenerator(); + _ = RoslynTestHelpers.Run(comp, gen, out var run, out _); + + var diags = run.Results.Single().Diagnostics; + Assert.Contains(diags, d => d.Id == "PKCF001"); + } + + [Fact] + public void FactoryClass_Detects_Duplicate_Keys_And_Invalid_Types() + { + const string user = """ + using PatternKit.Generators.Factories; + + namespace Demo; + + [FactoryClass(typeof(int))] + public interface IThing { } + + [FactoryClassKey(1)] + public class One : IThing { } + + [FactoryClassKey(1)] + public class OneB : IThing { } + + [FactoryClassKey("wrong")] + public class WrongType : IThing { } + """; + + var comp = RoslynTestHelpers.CreateCompilation( + user, + assemblyName: nameof(FactoryClass_Detects_Duplicate_Keys_And_Invalid_Types)); + + var gen = new FactoriesGenerator(); + _ = RoslynTestHelpers.Run(comp, gen, out var run, out _); + + var diags = run.Results.Single().Diagnostics; + Assert.Contains(diags, d => d.Id == "PKCF004"); + Assert.Contains(diags, d => d.Id == "PKCF005"); + } + + [Fact] + public void FactoryClass_Flags_Missing_Ctor() + { + const string user = """ + using PatternKit.Generators.Factories; + + namespace Demo; + + [FactoryClass(typeof(string))] + public interface IService { } + + [FactoryClassKey("bad")] + public class MissingCtor : IService + { + private MissingCtor() { } + } + """; + + var comp = RoslynTestHelpers.CreateCompilation( + user, + assemblyName: nameof(FactoryClass_Flags_Missing_Ctor)); + + var gen = new FactoriesGenerator(); + _ = RoslynTestHelpers.Run(comp, gen, out var run, out _); + + var diags = run.Results.Single().Diagnostics; + Assert.Contains(diags, d => d.Id == "PKCF006"); + } + + [Fact] + public void FactoryClass_Respects_GenerateTryCreate_Flag() + { + const string user = """ + using PatternKit.Generators.Factories; + + namespace Demo; + + [FactoryClass(typeof(string), GenerateTryCreate = false)] + public interface ICommand { } + + [FactoryClassKey("ping")] + public class Ping : ICommand { } + """; + + var comp = RoslynTestHelpers.CreateCompilation( + user, + assemblyName: nameof(FactoryClass_Respects_GenerateTryCreate_Flag)); + + var gen = new FactoriesGenerator(); + _ = RoslynTestHelpers.Run(comp, gen, out var run, out var updated); + + Assert.All(run.Results, r => Assert.True(r.Diagnostics.IsEmpty)); + + using var pe = new MemoryStream(); + var emit = updated.Emit(pe); + Assert.True(emit.Success, string.Join("\n", emit.Diagnostics)); + + pe.Position = 0; + var asm = AssemblyLoadContext.Default.LoadFromStream(pe); + var factoryType = asm.GetType("Demo.CommandFactory")!; + var commandType = asm.GetType("Demo.ICommand")!; + Assert.NotNull(factoryType.GetMethod("Create", new[] { typeof(string) })); + Assert.Null(factoryType.GetMethod("TryCreate", new[] { typeof(string), commandType.MakeByRefType() })); + } + + [Fact] + public void FactoryClass_Uses_CreateAsync_When_Available() + { + const string user = """ + using System.Threading.Tasks; + using PatternKit.Generators.Factories; + + namespace Demo; + + [FactoryClass(typeof(string))] + public abstract class Service { } + + [FactoryClassKey("async")] + public class AsyncService : Service + { + public static ValueTask CreateAsync() => new ValueTask(new AsyncService()); + } + + [FactoryClassKey("sync")] + public class SyncService : Service { } + """; + + var comp = RoslynTestHelpers.CreateCompilation( + user, + assemblyName: nameof(FactoryClass_Uses_CreateAsync_When_Available)); + + var gen = new FactoriesGenerator(); + _ = RoslynTestHelpers.Run(comp, gen, out var run, out var updated); + + Assert.All(run.Results, r => Assert.True(r.Diagnostics.IsEmpty)); + + using var pe = new MemoryStream(); + var emit = updated.Emit(pe); + Assert.True(emit.Success, string.Join("\n", emit.Diagnostics)); + + pe.Position = 0; + var asm = AssemblyLoadContext.Default.LoadFromStream(pe); + var factoryType = asm.GetType("Demo.ServiceFactory")!; + dynamic factory = Activator.CreateInstance(factoryType)!; + + var sync = factoryType.GetMethod("Create")!; + var asyncResult = sync.Invoke(factory, new object?[] { "async" }); + Assert.Equal("Demo.AsyncService", asyncResult!.GetType().FullName); + + var createAsync = factoryType.GetMethod("CreateAsync", new[] { typeof(string) })!; + var vtObj = createAsync.Invoke(factory, new object?[] { "sync" })!; + var awaited = vtObj.GetAwaiter().GetResult(); + Assert.Equal("Demo.SyncService", ((object)awaited).GetType().FullName); + } + + [Fact] + public void FactoryGenerators_Work_In_DI_Orchestrator_Scenario() + { + const string user = """ + using System; + using System.Threading; + using System.Threading.Tasks; + using PatternKit.Generators.Factories; + + public interface IServiceCollection + { + IServiceCollection Add(Type serviceType, Type implType); + } + + public class Services : IServiceCollection + { + public IServiceCollection Add(Type serviceType, Type implType) => this; + } + + [FactoryMethod(typeof(string), CreateMethodName = "ConfigureModule")] + public static partial class ServiceModules + { + [FactoryCase("metrics")] + public static IServiceCollection AddMetrics(IServiceCollection services) => services.Add(typeof(IMetrics), typeof(ConsoleMetrics)); + + [FactoryCase("workers")] + public static IServiceCollection AddWorkers(IServiceCollection services) => services.Add(typeof(IWorker), typeof(Worker)); + + [FactoryDefault] + public static IServiceCollection AddDefaults(IServiceCollection services) => services.Add(typeof(IWorker), typeof(Worker)); + } + + [FactoryClass(typeof(string))] + public interface IOrchestratorStep + { + ValueTask ExecuteAsync(IServiceCollection services, CancellationToken cancellationToken = default); + } + + [FactoryClassKey("seed")] + public sealed class SeedStep : IOrchestratorStep + { + public ValueTask ExecuteAsync(IServiceCollection services, CancellationToken cancellationToken = default) => ValueTask.CompletedTask; + } + + [FactoryClassKey("start")] + public sealed class StartStep : IOrchestratorStep + { + public ValueTask ExecuteAsync(IServiceCollection services, CancellationToken cancellationToken = default) => ValueTask.CompletedTask; + } + + public interface IMetrics { } + public sealed class ConsoleMetrics : IMetrics { } + public interface IWorker { } + public sealed class Worker : IWorker { } + """; + + var comp = RoslynTestHelpers.CreateCompilation( + user, + assemblyName: nameof(FactoryGenerators_Work_In_DI_Orchestrator_Scenario)); + + var gen = new FactoriesGenerator(); + _ = RoslynTestHelpers.Run(comp, gen, out var run, out var updated); + + Assert.All(run.Results, r => Assert.True(r.Diagnostics.IsEmpty)); + + using var pe = new MemoryStream(); + var emit = updated.Emit(pe); + Assert.True(emit.Success, string.Join("\n", emit.Diagnostics)); + } +} diff --git a/test/PatternKit.Generators.Tests/PatternKit.Generators.Tests.csproj b/test/PatternKit.Generators.Tests/PatternKit.Generators.Tests.csproj index 89180702..75191797 100644 --- a/test/PatternKit.Generators.Tests/PatternKit.Generators.Tests.csproj +++ b/test/PatternKit.Generators.Tests/PatternKit.Generators.Tests.csproj @@ -9,12 +9,12 @@ - - - - + + + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/PatternKit.Generators.Tests/packages.lock.json b/test/PatternKit.Generators.Tests/packages.lock.json index 0bae9888..9fac9899 100644 --- a/test/PatternKit.Generators.Tests/packages.lock.json +++ b/test/PatternKit.Generators.Tests/packages.lock.json @@ -10,37 +10,35 @@ }, "Microsoft.CodeAnalysis.CSharp": { "type": "Direct", - "requested": "[4.14.0, )", - "resolved": "4.14.0", - "contentHash": "568a6wcTivauIhbeWcCwfWwIn7UV7MeHEBvFB2uzGIpM2OhJ4eM/FZ8KS0yhPoNxnSpjGzz7x7CIjTxhslojQA==", + "requested": "[5.0.0, )", + "resolved": "5.0.0", + "contentHash": "5DSyJ9bk+ATuDy7fp2Zt0mJStDVKbBoiz1DyfAwSa+k4H4IwykAUcV3URelw5b8/iVbfSaOwkwmPUZH6opZKCw==", "dependencies": { "Microsoft.CodeAnalysis.Analyzers": "3.11.0", - "Microsoft.CodeAnalysis.Common": "[4.14.0]", - "System.Collections.Immutable": "9.0.0", - "System.Reflection.Metadata": "9.0.0" + "Microsoft.CodeAnalysis.Common": "[5.0.0]" } }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.14.1, )", - "resolved": "17.14.1", - "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "requested": "[18.0.1, )", + "resolved": "18.0.1", + "contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==", "dependencies": { - "Microsoft.CodeCoverage": "17.14.1", - "Microsoft.TestPlatform.TestHost": "17.14.1" + "Microsoft.CodeCoverage": "18.0.1", + "Microsoft.TestPlatform.TestHost": "18.0.1" } }, "System.Collections.Immutable": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "/kpkgDxH984e3J3z5v/DIFi+0TWbUJXS8HNKUYBy3YnXtK09JVGs3cw5aOV6fDSw5NxbWLWlGrYjRteu6cjX3w==" + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "kdTe61B8P7i2M1pODC3MLbZ/CfFGjpC6c6jzxjQoB5DHZNewayCRqgFUmx3JKB6vLQtozpMQEiw+R5fO32Jv4g==" }, "TinyBDD": { "type": "Direct", - "requested": "[0.11.0, )", - "resolved": "0.11.0", - "contentHash": "q953tZ+XQP9BHEiLbxa55PxhqMZ7Knb/US+NLYC4+kQ+l4ODih+T1O0Fgl34XinFOI2xKOZgQcYyTnFB+yTBxQ==" + "requested": "[0.12.1, )", + "resolved": "0.12.1", + "contentHash": "pf5G0SU/Gl65OAQoPbZC8tlAOvLM6/WowdmhTVJv8eov8ywgGaQbM7Z3mpF64P+u4x/0HGKYuqcNlimGqoQbTw==" }, "xunit": { "type": "Direct", @@ -55,14 +53,14 @@ }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[3.1.4, )", - "resolved": "3.1.4", - "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + "requested": "[3.1.5, )", + "resolved": "3.1.5", + "contentHash": "tKi7dSTwP4m5m9eXPM2Ime4Kn7xNf4x4zT9sdLO/G4hZVnQCRiMTWoSZqI/pYTVeI27oPPqHBKYI/DjJ9GsYgA==" }, "JetBrains.Annotations": { "type": "Transitive", - "resolved": "2025.2.2", - "contentHash": "0X56ZRizuHdrnPpgXjWV7f2tQO1FlQg5O1967OGKnI/4ZRNOK642J8L7brM1nYvrxTTU5TP1yRyXLRLaXLPQ8A==" + "resolved": "2025.2.4", + "contentHash": "TwbgxAkXxY+vNEhNVx/QXjJ4vqxmepOjsgRvvImQPbHkHMMb4W+ahL3laMsxXKtNT7iMy+E1B3xkqao2hf1n3A==" }, "Microsoft.CodeAnalysis.Analyzers": { "type": "Transitive", @@ -71,89 +69,105 @@ }, "Microsoft.CodeAnalysis.Common": { "type": "Transitive", - "resolved": "4.14.0", - "contentHash": "PC3tuwZYnC+idaPuoC/AZpEdwrtX7qFpmnrfQkgobGIWiYmGi5MCRtl5mx6QrfMGQpK78X2lfIEoZDLg/qnuHg==", + "resolved": "5.0.0", + "contentHash": "ZXRAdvH6GiDeHRyd3q/km8Z44RoM6FBWHd+gen/la81mVnAdHTEsEkO5J0TCNXBymAcx5UYKt5TvgKBhaLJEow==", "dependencies": { - "Microsoft.CodeAnalysis.Analyzers": "3.11.0", - "System.Collections.Immutable": "9.0.0", - "System.Reflection.Metadata": "9.0.0" + "Microsoft.CodeAnalysis.Analyzers": "3.11.0" } }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + "resolved": "18.0.1", + "contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA==" + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "p5RKAY9POvs3axwA/AQRuJeM8AHuE8h4qbP1NxQeGm0ep46aXz1oCLAp/oOYxX1GsjStgdhHrN3XXLLXr0+b3w==", + "resolved": "10.0.1", + "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "6SIp/6Bngk4jm2W36JekZbiIbFPdE/eMUtrJEqIqHGpd1zar3jvgnwxnpWQfzUiGrkyY8q8s6V82zkkEZozghA==", + "resolved": "10.0.1", + "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "/hymojfWbE9AlDOa0mczR44m00Jj+T3+HZO0ZnVTI032fVycI0ZbNOVFP6kqZMcXiLSYXzR2ilcwaRi6dzeGyA==" + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "loxGGHE1FC2AefwPHzrjPq7X92LQm64qnU/whKfo6oWaceewPUVYQJBJs3S3E2qlWwnCpeZ+dGCPTX+5dgVAuQ==", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "n4DCdnn2qs6V5U06Sx62FySEAZsJiJJgOzrPHDh9hPK7c2W8hEabC76F3Re3tGPjpiKa02RvB6FxZyxo8iICzg==", + "resolved": "10.0.1", + "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Configuration.Binder": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.DataAnnotations": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "Al+1FXnKKFygTXz0Zsa1+jYEPvsx5dKavlJxMXjRbrL6lmBhQZsVMhjuNB5lWvdRhdoxt5y/Q3v5kbLZLsXWdA==", + "resolved": "10.0.1", + "contentHash": "gwHO+zpVQGKK9KB3yen82Tff2zdLHHGIJzTut9L8RvoOO2RMSyYZrOmElvxu0lA4ZyaSxy8I0oNw1S/O/vkvFg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "z4pyMePOrl733ltTowbN565PxBw1oAr8IHmIXNDiDqd22nFpYltX9KhrNC/qBWAG1/Zx5MHX+cOYhWJQYCO/iw==" + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==", + "resolved": "18.0.1", + "contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==", "dependencies": { "System.Reflection.Metadata": "8.0.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "resolved": "18.0.1", + "contentHash": "uDJKAEjFTaa2wHdWlfo6ektyoh+WD4/Eesrwb4FpBFKsLGehhACVnwwTI4qD3FrIlIEPlxdXg3SyrYRIcO+RRQ==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Microsoft.TestPlatform.ObjectModel": "18.0.1", "Newtonsoft.Json": "13.0.3" } }, @@ -164,8 +178,11 @@ }, "System.Reflection.Metadata": { "type": "Transitive", - "resolved": "9.0.0", - "contentHash": "ANiqLu3DxW9kol/hMmTWbt3414t9ftdIuiIU7j80okq2YzAueo120M442xk1kDJWtmZTqWQn7wHDvMRipVOEOQ==" + "resolved": "8.0.0", + "contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==", + "dependencies": { + "System.Collections.Immutable": "8.0.0" + } }, "xunit.abstractions": { "type": "Transitive", @@ -213,12 +230,13 @@ "patternkit.examples": { "type": "Project", "dependencies": { - "JetBrains.Annotations": "[2025.2.2, )", - "Microsoft.Extensions.Configuration.Abstractions": "[9.0.9, )", - "Microsoft.Extensions.Configuration.Binder": "[9.0.9, )", - "Microsoft.Extensions.Options": "[9.0.9, )", - "Microsoft.Extensions.Options.ConfigurationExtensions": "[9.0.9, )", - "Microsoft.Extensions.Options.DataAnnotations": "[9.0.9, )", + "JetBrains.Annotations": "[2025.2.4, )", + "Microsoft.Extensions.Configuration.Abstractions": "[10.0.1, )", + "Microsoft.Extensions.Configuration.Binder": "[10.0.1, )", + "Microsoft.Extensions.DependencyInjection": "[10.0.1, )", + "Microsoft.Extensions.Options": "[10.0.1, )", + "Microsoft.Extensions.Options.ConfigurationExtensions": "[10.0.1, )", + "Microsoft.Extensions.Options.DataAnnotations": "[10.0.1, )", "PatternKit.Core": "[1.0.0, )", "PatternKit.Generators": "[1.0.0, )" } @@ -226,7 +244,7 @@ "patternkit.generators": { "type": "Project", "dependencies": { - "System.Collections.Immutable": "[9.0.9, )" + "System.Collections.Immutable": "[10.0.1, )" } } }, @@ -239,37 +257,37 @@ }, "Microsoft.CodeAnalysis.CSharp": { "type": "Direct", - "requested": "[4.14.0, )", - "resolved": "4.14.0", - "contentHash": "568a6wcTivauIhbeWcCwfWwIn7UV7MeHEBvFB2uzGIpM2OhJ4eM/FZ8KS0yhPoNxnSpjGzz7x7CIjTxhslojQA==", + "requested": "[5.0.0, )", + "resolved": "5.0.0", + "contentHash": "5DSyJ9bk+ATuDy7fp2Zt0mJStDVKbBoiz1DyfAwSa+k4H4IwykAUcV3URelw5b8/iVbfSaOwkwmPUZH6opZKCw==", "dependencies": { "Microsoft.CodeAnalysis.Analyzers": "3.11.0", - "Microsoft.CodeAnalysis.Common": "[4.14.0]", + "Microsoft.CodeAnalysis.Common": "[5.0.0]", "System.Collections.Immutable": "9.0.0", "System.Reflection.Metadata": "9.0.0" } }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.14.1, )", - "resolved": "17.14.1", - "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "requested": "[18.0.1, )", + "resolved": "18.0.1", + "contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==", "dependencies": { - "Microsoft.CodeCoverage": "17.14.1", - "Microsoft.TestPlatform.TestHost": "17.14.1" + "Microsoft.CodeCoverage": "18.0.1", + "Microsoft.TestPlatform.TestHost": "18.0.1" } }, "System.Collections.Immutable": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "/kpkgDxH984e3J3z5v/DIFi+0TWbUJXS8HNKUYBy3YnXtK09JVGs3cw5aOV6fDSw5NxbWLWlGrYjRteu6cjX3w==" + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "kdTe61B8P7i2M1pODC3MLbZ/CfFGjpC6c6jzxjQoB5DHZNewayCRqgFUmx3JKB6vLQtozpMQEiw+R5fO32Jv4g==" }, "TinyBDD": { "type": "Direct", - "requested": "[0.11.0, )", - "resolved": "0.11.0", - "contentHash": "q953tZ+XQP9BHEiLbxa55PxhqMZ7Knb/US+NLYC4+kQ+l4ODih+T1O0Fgl34XinFOI2xKOZgQcYyTnFB+yTBxQ==" + "requested": "[0.12.1, )", + "resolved": "0.12.1", + "contentHash": "pf5G0SU/Gl65OAQoPbZC8tlAOvLM6/WowdmhTVJv8eov8ywgGaQbM7Z3mpF64P+u4x/0HGKYuqcNlimGqoQbTw==" }, "xunit": { "type": "Direct", @@ -284,14 +302,14 @@ }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[3.1.4, )", - "resolved": "3.1.4", - "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + "requested": "[3.1.5, )", + "resolved": "3.1.5", + "contentHash": "tKi7dSTwP4m5m9eXPM2Ime4Kn7xNf4x4zT9sdLO/G4hZVnQCRiMTWoSZqI/pYTVeI27oPPqHBKYI/DjJ9GsYgA==" }, "JetBrains.Annotations": { "type": "Transitive", - "resolved": "2025.2.2", - "contentHash": "0X56ZRizuHdrnPpgXjWV7f2tQO1FlQg5O1967OGKnI/4ZRNOK642J8L7brM1nYvrxTTU5TP1yRyXLRLaXLPQ8A==" + "resolved": "2025.2.4", + "contentHash": "TwbgxAkXxY+vNEhNVx/QXjJ4vqxmepOjsgRvvImQPbHkHMMb4W+ahL3laMsxXKtNT7iMy+E1B3xkqao2hf1n3A==" }, "Microsoft.CodeAnalysis.Analyzers": { "type": "Transitive", @@ -300,8 +318,8 @@ }, "Microsoft.CodeAnalysis.Common": { "type": "Transitive", - "resolved": "4.14.0", - "contentHash": "PC3tuwZYnC+idaPuoC/AZpEdwrtX7qFpmnrfQkgobGIWiYmGi5MCRtl5mx6QrfMGQpK78X2lfIEoZDLg/qnuHg==", + "resolved": "5.0.0", + "contentHash": "ZXRAdvH6GiDeHRyd3q/km8Z44RoM6FBWHd+gen/la81mVnAdHTEsEkO5J0TCNXBymAcx5UYKt5TvgKBhaLJEow==", "dependencies": { "Microsoft.CodeAnalysis.Analyzers": "3.11.0", "System.Collections.Immutable": "9.0.0", @@ -310,79 +328,97 @@ }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + "resolved": "18.0.1", + "contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA==" + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "p5RKAY9POvs3axwA/AQRuJeM8AHuE8h4qbP1NxQeGm0ep46aXz1oCLAp/oOYxX1GsjStgdhHrN3XXLLXr0+b3w==", + "resolved": "10.0.1", + "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "6SIp/6Bngk4jm2W36JekZbiIbFPdE/eMUtrJEqIqHGpd1zar3jvgnwxnpWQfzUiGrkyY8q8s6V82zkkEZozghA==", + "resolved": "10.0.1", + "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9" + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "/hymojfWbE9AlDOa0mczR44m00Jj+T3+HZO0ZnVTI032fVycI0ZbNOVFP6kqZMcXiLSYXzR2ilcwaRi6dzeGyA==" + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "loxGGHE1FC2AefwPHzrjPq7X92LQm64qnU/whKfo6oWaceewPUVYQJBJs3S3E2qlWwnCpeZ+dGCPTX+5dgVAuQ==", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "n4DCdnn2qs6V5U06Sx62FySEAZsJiJJgOzrPHDh9hPK7c2W8hEabC76F3Re3tGPjpiKa02RvB6FxZyxo8iICzg==", + "resolved": "10.0.1", + "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Configuration.Binder": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.DataAnnotations": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "Al+1FXnKKFygTXz0Zsa1+jYEPvsx5dKavlJxMXjRbrL6lmBhQZsVMhjuNB5lWvdRhdoxt5y/Q3v5kbLZLsXWdA==", + "resolved": "10.0.1", + "contentHash": "gwHO+zpVQGKK9KB3yen82Tff2zdLHHGIJzTut9L8RvoOO2RMSyYZrOmElvxu0lA4ZyaSxy8I0oNw1S/O/vkvFg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "z4pyMePOrl733ltTowbN565PxBw1oAr8IHmIXNDiDqd22nFpYltX9KhrNC/qBWAG1/Zx5MHX+cOYhWJQYCO/iw==" + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==", + "resolved": "18.0.1", + "contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==", "dependencies": { "System.Reflection.Metadata": "8.0.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "resolved": "18.0.1", + "contentHash": "uDJKAEjFTaa2wHdWlfo6ektyoh+WD4/Eesrwb4FpBFKsLGehhACVnwwTI4qD3FrIlIEPlxdXg3SyrYRIcO+RRQ==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Microsoft.TestPlatform.ObjectModel": "18.0.1", "Newtonsoft.Json": "13.0.3" } }, @@ -445,12 +481,13 @@ "patternkit.examples": { "type": "Project", "dependencies": { - "JetBrains.Annotations": "[2025.2.2, )", - "Microsoft.Extensions.Configuration.Abstractions": "[9.0.9, )", - "Microsoft.Extensions.Configuration.Binder": "[9.0.9, )", - "Microsoft.Extensions.Options": "[9.0.9, )", - "Microsoft.Extensions.Options.ConfigurationExtensions": "[9.0.9, )", - "Microsoft.Extensions.Options.DataAnnotations": "[9.0.9, )", + "JetBrains.Annotations": "[2025.2.4, )", + "Microsoft.Extensions.Configuration.Abstractions": "[10.0.1, )", + "Microsoft.Extensions.Configuration.Binder": "[10.0.1, )", + "Microsoft.Extensions.DependencyInjection": "[10.0.1, )", + "Microsoft.Extensions.Options": "[10.0.1, )", + "Microsoft.Extensions.Options.ConfigurationExtensions": "[10.0.1, )", + "Microsoft.Extensions.Options.DataAnnotations": "[10.0.1, )", "PatternKit.Core": "[1.0.0, )", "PatternKit.Generators": "[1.0.0, )" } @@ -458,7 +495,7 @@ "patternkit.generators": { "type": "Project", "dependencies": { - "System.Collections.Immutable": "[9.0.9, )" + "System.Collections.Immutable": "[10.0.1, )" } } }, @@ -471,37 +508,35 @@ }, "Microsoft.CodeAnalysis.CSharp": { "type": "Direct", - "requested": "[4.14.0, )", - "resolved": "4.14.0", - "contentHash": "568a6wcTivauIhbeWcCwfWwIn7UV7MeHEBvFB2uzGIpM2OhJ4eM/FZ8KS0yhPoNxnSpjGzz7x7CIjTxhslojQA==", + "requested": "[5.0.0, )", + "resolved": "5.0.0", + "contentHash": "5DSyJ9bk+ATuDy7fp2Zt0mJStDVKbBoiz1DyfAwSa+k4H4IwykAUcV3URelw5b8/iVbfSaOwkwmPUZH6opZKCw==", "dependencies": { "Microsoft.CodeAnalysis.Analyzers": "3.11.0", - "Microsoft.CodeAnalysis.Common": "[4.14.0]", - "System.Collections.Immutable": "9.0.0", - "System.Reflection.Metadata": "9.0.0" + "Microsoft.CodeAnalysis.Common": "[5.0.0]" } }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.14.1, )", - "resolved": "17.14.1", - "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "requested": "[18.0.1, )", + "resolved": "18.0.1", + "contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==", "dependencies": { - "Microsoft.CodeCoverage": "17.14.1", - "Microsoft.TestPlatform.TestHost": "17.14.1" + "Microsoft.CodeCoverage": "18.0.1", + "Microsoft.TestPlatform.TestHost": "18.0.1" } }, "System.Collections.Immutable": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "/kpkgDxH984e3J3z5v/DIFi+0TWbUJXS8HNKUYBy3YnXtK09JVGs3cw5aOV6fDSw5NxbWLWlGrYjRteu6cjX3w==" + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "kdTe61B8P7i2M1pODC3MLbZ/CfFGjpC6c6jzxjQoB5DHZNewayCRqgFUmx3JKB6vLQtozpMQEiw+R5fO32Jv4g==" }, "TinyBDD": { "type": "Direct", - "requested": "[0.11.0, )", - "resolved": "0.11.0", - "contentHash": "q953tZ+XQP9BHEiLbxa55PxhqMZ7Knb/US+NLYC4+kQ+l4ODih+T1O0Fgl34XinFOI2xKOZgQcYyTnFB+yTBxQ==" + "requested": "[0.12.1, )", + "resolved": "0.12.1", + "contentHash": "pf5G0SU/Gl65OAQoPbZC8tlAOvLM6/WowdmhTVJv8eov8ywgGaQbM7Z3mpF64P+u4x/0HGKYuqcNlimGqoQbTw==" }, "xunit": { "type": "Direct", @@ -516,14 +551,14 @@ }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[3.1.4, )", - "resolved": "3.1.4", - "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + "requested": "[3.1.5, )", + "resolved": "3.1.5", + "contentHash": "tKi7dSTwP4m5m9eXPM2Ime4Kn7xNf4x4zT9sdLO/G4hZVnQCRiMTWoSZqI/pYTVeI27oPPqHBKYI/DjJ9GsYgA==" }, "JetBrains.Annotations": { "type": "Transitive", - "resolved": "2025.2.2", - "contentHash": "0X56ZRizuHdrnPpgXjWV7f2tQO1FlQg5O1967OGKnI/4ZRNOK642J8L7brM1nYvrxTTU5TP1yRyXLRLaXLPQ8A==" + "resolved": "2025.2.4", + "contentHash": "TwbgxAkXxY+vNEhNVx/QXjJ4vqxmepOjsgRvvImQPbHkHMMb4W+ahL3laMsxXKtNT7iMy+E1B3xkqao2hf1n3A==" }, "Microsoft.CodeAnalysis.Analyzers": { "type": "Transitive", @@ -532,89 +567,105 @@ }, "Microsoft.CodeAnalysis.Common": { "type": "Transitive", - "resolved": "4.14.0", - "contentHash": "PC3tuwZYnC+idaPuoC/AZpEdwrtX7qFpmnrfQkgobGIWiYmGi5MCRtl5mx6QrfMGQpK78X2lfIEoZDLg/qnuHg==", + "resolved": "5.0.0", + "contentHash": "ZXRAdvH6GiDeHRyd3q/km8Z44RoM6FBWHd+gen/la81mVnAdHTEsEkO5J0TCNXBymAcx5UYKt5TvgKBhaLJEow==", "dependencies": { - "Microsoft.CodeAnalysis.Analyzers": "3.11.0", - "System.Collections.Immutable": "9.0.0", - "System.Reflection.Metadata": "9.0.0" + "Microsoft.CodeAnalysis.Analyzers": "3.11.0" } }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + "resolved": "18.0.1", + "contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA==" + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "p5RKAY9POvs3axwA/AQRuJeM8AHuE8h4qbP1NxQeGm0ep46aXz1oCLAp/oOYxX1GsjStgdhHrN3XXLLXr0+b3w==", + "resolved": "10.0.1", + "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "6SIp/6Bngk4jm2W36JekZbiIbFPdE/eMUtrJEqIqHGpd1zar3jvgnwxnpWQfzUiGrkyY8q8s6V82zkkEZozghA==", + "resolved": "10.0.1", + "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "/hymojfWbE9AlDOa0mczR44m00Jj+T3+HZO0ZnVTI032fVycI0ZbNOVFP6kqZMcXiLSYXzR2ilcwaRi6dzeGyA==" + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "loxGGHE1FC2AefwPHzrjPq7X92LQm64qnU/whKfo6oWaceewPUVYQJBJs3S3E2qlWwnCpeZ+dGCPTX+5dgVAuQ==", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "n4DCdnn2qs6V5U06Sx62FySEAZsJiJJgOzrPHDh9hPK7c2W8hEabC76F3Re3tGPjpiKa02RvB6FxZyxo8iICzg==", + "resolved": "10.0.1", + "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Configuration.Binder": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.DataAnnotations": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "Al+1FXnKKFygTXz0Zsa1+jYEPvsx5dKavlJxMXjRbrL6lmBhQZsVMhjuNB5lWvdRhdoxt5y/Q3v5kbLZLsXWdA==", + "resolved": "10.0.1", + "contentHash": "gwHO+zpVQGKK9KB3yen82Tff2zdLHHGIJzTut9L8RvoOO2RMSyYZrOmElvxu0lA4ZyaSxy8I0oNw1S/O/vkvFg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "z4pyMePOrl733ltTowbN565PxBw1oAr8IHmIXNDiDqd22nFpYltX9KhrNC/qBWAG1/Zx5MHX+cOYhWJQYCO/iw==" + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==", + "resolved": "18.0.1", + "contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==", "dependencies": { "System.Reflection.Metadata": "8.0.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "resolved": "18.0.1", + "contentHash": "uDJKAEjFTaa2wHdWlfo6ektyoh+WD4/Eesrwb4FpBFKsLGehhACVnwwTI4qD3FrIlIEPlxdXg3SyrYRIcO+RRQ==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Microsoft.TestPlatform.ObjectModel": "18.0.1", "Newtonsoft.Json": "13.0.3" } }, @@ -625,8 +676,11 @@ }, "System.Reflection.Metadata": { "type": "Transitive", - "resolved": "9.0.0", - "contentHash": "ANiqLu3DxW9kol/hMmTWbt3414t9ftdIuiIU7j80okq2YzAueo120M442xk1kDJWtmZTqWQn7wHDvMRipVOEOQ==" + "resolved": "8.0.0", + "contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==", + "dependencies": { + "System.Collections.Immutable": "8.0.0" + } }, "xunit.abstractions": { "type": "Transitive", @@ -674,12 +728,13 @@ "patternkit.examples": { "type": "Project", "dependencies": { - "JetBrains.Annotations": "[2025.2.2, )", - "Microsoft.Extensions.Configuration.Abstractions": "[9.0.9, )", - "Microsoft.Extensions.Configuration.Binder": "[9.0.9, )", - "Microsoft.Extensions.Options": "[9.0.9, )", - "Microsoft.Extensions.Options.ConfigurationExtensions": "[9.0.9, )", - "Microsoft.Extensions.Options.DataAnnotations": "[9.0.9, )", + "JetBrains.Annotations": "[2025.2.4, )", + "Microsoft.Extensions.Configuration.Abstractions": "[10.0.1, )", + "Microsoft.Extensions.Configuration.Binder": "[10.0.1, )", + "Microsoft.Extensions.DependencyInjection": "[10.0.1, )", + "Microsoft.Extensions.Options": "[10.0.1, )", + "Microsoft.Extensions.Options.ConfigurationExtensions": "[10.0.1, )", + "Microsoft.Extensions.Options.DataAnnotations": "[10.0.1, )", "PatternKit.Core": "[1.0.0, )", "PatternKit.Generators": "[1.0.0, )" } @@ -687,7 +742,7 @@ "patternkit.generators": { "type": "Project", "dependencies": { - "System.Collections.Immutable": "[9.0.9, )" + "System.Collections.Immutable": "[10.0.1, )" } } } diff --git a/test/PatternKit.Tests/PatternKit.Tests.csproj b/test/PatternKit.Tests/PatternKit.Tests.csproj index 3be4e673..84f59023 100644 --- a/test/PatternKit.Tests/PatternKit.Tests.csproj +++ b/test/PatternKit.Tests/PatternKit.Tests.csproj @@ -12,13 +12,13 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/PatternKit.Tests/packages.lock.json b/test/PatternKit.Tests/packages.lock.json index 6fedf87d..c225458c 100644 --- a/test/PatternKit.Tests/packages.lock.json +++ b/test/PatternKit.Tests/packages.lock.json @@ -10,33 +10,36 @@ }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.14.1, )", - "resolved": "17.14.1", - "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "requested": "[18.0.1, )", + "resolved": "18.0.1", + "contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==", "dependencies": { - "Microsoft.CodeCoverage": "17.14.1", - "Microsoft.TestPlatform.TestHost": "17.14.1" + "Microsoft.CodeCoverage": "18.0.1", + "Microsoft.TestPlatform.TestHost": "18.0.1" } }, "System.Collections.Immutable": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "/kpkgDxH984e3J3z5v/DIFi+0TWbUJXS8HNKUYBy3YnXtK09JVGs3cw5aOV6fDSw5NxbWLWlGrYjRteu6cjX3w==" + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "kdTe61B8P7i2M1pODC3MLbZ/CfFGjpC6c6jzxjQoB5DHZNewayCRqgFUmx3JKB6vLQtozpMQEiw+R5fO32Jv4g==" }, "System.Linq.Async": { "type": "Direct", - "requested": "[6.0.3, )", - "resolved": "6.0.3", - "contentHash": "hSHiq2m1ky7zUQgTp+/2h1K3lABIQ+GltRixoclHPg/Sc1vnfeS6g/Uy5moOVZKrZJdQiFPFZd6OobBp3tZcFg==" + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "A2Wci92Oyuodi8YLMQCJJ0vHqzgRFgEUG1K6tQNcoxHd3w05B1LvGzXvxQnGYPIL4Cr4hicHytpk2F2Jx8TZHg==", + "dependencies": { + "System.Interactive.Async": "7.0.0" + } }, "TinyBDD.Xunit": { "type": "Direct", - "requested": "[0.11.0, )", - "resolved": "0.11.0", - "contentHash": "gJu6gSe4sp24+ie+dQ+6bY9SrauQk1UcV02qQsucYcqajwM8B8TCUTwsFjt8ns1xtbOVPRZOlyoWEOBP6NR/Pw==", + "requested": "[0.12.1, )", + "resolved": "0.12.1", + "contentHash": "1V1RAF1OGY7m9kGzhhFpe4NzZO2bd8vSEoL9AlFhEWQ0GIeCCJ/a5Bq4Eqw00n9op/ZHUtb9Retk9XfQSkvKFw==", "dependencies": { - "TinyBDD": "0.11.0", + "TinyBDD": "0.12.1", "xunit.abstractions": "2.0.3", "xunit.extensibility.core": "2.9.3" } @@ -63,90 +66,108 @@ }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[3.1.4, )", - "resolved": "3.1.4", - "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + "requested": "[3.1.5, )", + "resolved": "3.1.5", + "contentHash": "tKi7dSTwP4m5m9eXPM2Ime4Kn7xNf4x4zT9sdLO/G4hZVnQCRiMTWoSZqI/pYTVeI27oPPqHBKYI/DjJ9GsYgA==" }, "JetBrains.Annotations": { "type": "Transitive", - "resolved": "2025.2.2", - "contentHash": "0X56ZRizuHdrnPpgXjWV7f2tQO1FlQg5O1967OGKnI/4ZRNOK642J8L7brM1nYvrxTTU5TP1yRyXLRLaXLPQ8A==" + "resolved": "2025.2.4", + "contentHash": "TwbgxAkXxY+vNEhNVx/QXjJ4vqxmepOjsgRvvImQPbHkHMMb4W+ahL3laMsxXKtNT7iMy+E1B3xkqao2hf1n3A==" }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + "resolved": "18.0.1", + "contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA==" + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "p5RKAY9POvs3axwA/AQRuJeM8AHuE8h4qbP1NxQeGm0ep46aXz1oCLAp/oOYxX1GsjStgdhHrN3XXLLXr0+b3w==", + "resolved": "10.0.1", + "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "6SIp/6Bngk4jm2W36JekZbiIbFPdE/eMUtrJEqIqHGpd1zar3jvgnwxnpWQfzUiGrkyY8q8s6V82zkkEZozghA==", + "resolved": "10.0.1", + "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9" + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "/hymojfWbE9AlDOa0mczR44m00Jj+T3+HZO0ZnVTI032fVycI0ZbNOVFP6kqZMcXiLSYXzR2ilcwaRi6dzeGyA==" + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "loxGGHE1FC2AefwPHzrjPq7X92LQm64qnU/whKfo6oWaceewPUVYQJBJs3S3E2qlWwnCpeZ+dGCPTX+5dgVAuQ==", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "n4DCdnn2qs6V5U06Sx62FySEAZsJiJJgOzrPHDh9hPK7c2W8hEabC76F3Re3tGPjpiKa02RvB6FxZyxo8iICzg==", + "resolved": "10.0.1", + "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Configuration.Binder": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.DataAnnotations": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "Al+1FXnKKFygTXz0Zsa1+jYEPvsx5dKavlJxMXjRbrL6lmBhQZsVMhjuNB5lWvdRhdoxt5y/Q3v5kbLZLsXWdA==", + "resolved": "10.0.1", + "contentHash": "gwHO+zpVQGKK9KB3yen82Tff2zdLHHGIJzTut9L8RvoOO2RMSyYZrOmElvxu0lA4ZyaSxy8I0oNw1S/O/vkvFg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "z4pyMePOrl733ltTowbN565PxBw1oAr8IHmIXNDiDqd22nFpYltX9KhrNC/qBWAG1/Zx5MHX+cOYhWJQYCO/iw==" + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==", + "resolved": "18.0.1", + "contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==", "dependencies": { "System.Reflection.Metadata": "8.0.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "resolved": "18.0.1", + "contentHash": "uDJKAEjFTaa2wHdWlfo6ektyoh+WD4/Eesrwb4FpBFKsLGehhACVnwwTI4qD3FrIlIEPlxdXg3SyrYRIcO+RRQ==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Microsoft.TestPlatform.ObjectModel": "18.0.1", "Newtonsoft.Json": "13.0.3" } }, @@ -155,6 +176,11 @@ "resolved": "13.0.3", "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, + "System.Interactive.Async": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Ckj+tg2BVOZ0oLp7FAbjfvRyA/BMkUhVxROLd+x22zncRR6KD7CdFzAYp+9Mo2cedxAMo2X9ZNyhZu68jdDITw==" + }, "System.Reflection.Metadata": { "type": "Transitive", "resolved": "8.0.0", @@ -165,8 +191,8 @@ }, "TinyBDD": { "type": "Transitive", - "resolved": "0.11.0", - "contentHash": "q953tZ+XQP9BHEiLbxa55PxhqMZ7Knb/US+NLYC4+kQ+l4ODih+T1O0Fgl34XinFOI2xKOZgQcYyTnFB+yTBxQ==" + "resolved": "0.12.1", + "contentHash": "pf5G0SU/Gl65OAQoPbZC8tlAOvLM6/WowdmhTVJv8eov8ywgGaQbM7Z3mpF64P+u4x/0HGKYuqcNlimGqoQbTw==" }, "xunit.abstractions": { "type": "Transitive", @@ -206,12 +232,13 @@ "patternkit.examples": { "type": "Project", "dependencies": { - "JetBrains.Annotations": "[2025.2.2, )", - "Microsoft.Extensions.Configuration.Abstractions": "[9.0.9, )", - "Microsoft.Extensions.Configuration.Binder": "[9.0.9, )", - "Microsoft.Extensions.Options": "[9.0.9, )", - "Microsoft.Extensions.Options.ConfigurationExtensions": "[9.0.9, )", - "Microsoft.Extensions.Options.DataAnnotations": "[9.0.9, )", + "JetBrains.Annotations": "[2025.2.4, )", + "Microsoft.Extensions.Configuration.Abstractions": "[10.0.1, )", + "Microsoft.Extensions.Configuration.Binder": "[10.0.1, )", + "Microsoft.Extensions.DependencyInjection": "[10.0.1, )", + "Microsoft.Extensions.Options": "[10.0.1, )", + "Microsoft.Extensions.Options.ConfigurationExtensions": "[10.0.1, )", + "Microsoft.Extensions.Options.DataAnnotations": "[10.0.1, )", "PatternKit.Core": "[1.0.0, )", "PatternKit.Generators": "[1.0.0, )" } @@ -219,7 +246,7 @@ "patternkit.generators": { "type": "Project", "dependencies": { - "System.Collections.Immutable": "[9.0.9, )" + "System.Collections.Immutable": "[10.0.1, )" } } }, @@ -232,33 +259,37 @@ }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.14.1, )", - "resolved": "17.14.1", - "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "requested": "[18.0.1, )", + "resolved": "18.0.1", + "contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==", "dependencies": { - "Microsoft.CodeCoverage": "17.14.1", - "Microsoft.TestPlatform.TestHost": "17.14.1" + "Microsoft.CodeCoverage": "18.0.1", + "Microsoft.TestPlatform.TestHost": "18.0.1" } }, "System.Collections.Immutable": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "/kpkgDxH984e3J3z5v/DIFi+0TWbUJXS8HNKUYBy3YnXtK09JVGs3cw5aOV6fDSw5NxbWLWlGrYjRteu6cjX3w==" + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "kdTe61B8P7i2M1pODC3MLbZ/CfFGjpC6c6jzxjQoB5DHZNewayCRqgFUmx3JKB6vLQtozpMQEiw+R5fO32Jv4g==" }, "System.Linq.Async": { "type": "Direct", - "requested": "[6.0.3, )", - "resolved": "6.0.3", - "contentHash": "hSHiq2m1ky7zUQgTp+/2h1K3lABIQ+GltRixoclHPg/Sc1vnfeS6g/Uy5moOVZKrZJdQiFPFZd6OobBp3tZcFg==" + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "A2Wci92Oyuodi8YLMQCJJ0vHqzgRFgEUG1K6tQNcoxHd3w05B1LvGzXvxQnGYPIL4Cr4hicHytpk2F2Jx8TZHg==", + "dependencies": { + "System.Interactive.Async": "7.0.0", + "System.Linq.AsyncEnumerable": "10.0.0" + } }, "TinyBDD.Xunit": { "type": "Direct", - "requested": "[0.11.0, )", - "resolved": "0.11.0", - "contentHash": "gJu6gSe4sp24+ie+dQ+6bY9SrauQk1UcV02qQsucYcqajwM8B8TCUTwsFjt8ns1xtbOVPRZOlyoWEOBP6NR/Pw==", + "requested": "[0.12.1, )", + "resolved": "0.12.1", + "contentHash": "1V1RAF1OGY7m9kGzhhFpe4NzZO2bd8vSEoL9AlFhEWQ0GIeCCJ/a5Bq4Eqw00n9op/ZHUtb9Retk9XfQSkvKFw==", "dependencies": { - "TinyBDD": "0.11.0", + "TinyBDD": "0.12.1", "xunit.abstractions": "2.0.3", "xunit.extensibility.core": "2.9.3" } @@ -285,90 +316,108 @@ }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[3.1.4, )", - "resolved": "3.1.4", - "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + "requested": "[3.1.5, )", + "resolved": "3.1.5", + "contentHash": "tKi7dSTwP4m5m9eXPM2Ime4Kn7xNf4x4zT9sdLO/G4hZVnQCRiMTWoSZqI/pYTVeI27oPPqHBKYI/DjJ9GsYgA==" }, "JetBrains.Annotations": { "type": "Transitive", - "resolved": "2025.2.2", - "contentHash": "0X56ZRizuHdrnPpgXjWV7f2tQO1FlQg5O1967OGKnI/4ZRNOK642J8L7brM1nYvrxTTU5TP1yRyXLRLaXLPQ8A==" + "resolved": "2025.2.4", + "contentHash": "TwbgxAkXxY+vNEhNVx/QXjJ4vqxmepOjsgRvvImQPbHkHMMb4W+ahL3laMsxXKtNT7iMy+E1B3xkqao2hf1n3A==" }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + "resolved": "18.0.1", + "contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA==" + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "p5RKAY9POvs3axwA/AQRuJeM8AHuE8h4qbP1NxQeGm0ep46aXz1oCLAp/oOYxX1GsjStgdhHrN3XXLLXr0+b3w==", + "resolved": "10.0.1", + "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "6SIp/6Bngk4jm2W36JekZbiIbFPdE/eMUtrJEqIqHGpd1zar3jvgnwxnpWQfzUiGrkyY8q8s6V82zkkEZozghA==", + "resolved": "10.0.1", + "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "/hymojfWbE9AlDOa0mczR44m00Jj+T3+HZO0ZnVTI032fVycI0ZbNOVFP6kqZMcXiLSYXzR2ilcwaRi6dzeGyA==" + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "loxGGHE1FC2AefwPHzrjPq7X92LQm64qnU/whKfo6oWaceewPUVYQJBJs3S3E2qlWwnCpeZ+dGCPTX+5dgVAuQ==", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "n4DCdnn2qs6V5U06Sx62FySEAZsJiJJgOzrPHDh9hPK7c2W8hEabC76F3Re3tGPjpiKa02RvB6FxZyxo8iICzg==", + "resolved": "10.0.1", + "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Configuration.Binder": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.DataAnnotations": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "Al+1FXnKKFygTXz0Zsa1+jYEPvsx5dKavlJxMXjRbrL6lmBhQZsVMhjuNB5lWvdRhdoxt5y/Q3v5kbLZLsXWdA==", + "resolved": "10.0.1", + "contentHash": "gwHO+zpVQGKK9KB3yen82Tff2zdLHHGIJzTut9L8RvoOO2RMSyYZrOmElvxu0lA4ZyaSxy8I0oNw1S/O/vkvFg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "z4pyMePOrl733ltTowbN565PxBw1oAr8IHmIXNDiDqd22nFpYltX9KhrNC/qBWAG1/Zx5MHX+cOYhWJQYCO/iw==" + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==", + "resolved": "18.0.1", + "contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==", "dependencies": { "System.Reflection.Metadata": "8.0.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "resolved": "18.0.1", + "contentHash": "uDJKAEjFTaa2wHdWlfo6ektyoh+WD4/Eesrwb4FpBFKsLGehhACVnwwTI4qD3FrIlIEPlxdXg3SyrYRIcO+RRQ==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Microsoft.TestPlatform.ObjectModel": "18.0.1", "Newtonsoft.Json": "13.0.3" } }, @@ -377,6 +426,19 @@ "resolved": "13.0.3", "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, + "System.Interactive.Async": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Ckj+tg2BVOZ0oLp7FAbjfvRyA/BMkUhVxROLd+x22zncRR6KD7CdFzAYp+9Mo2cedxAMo2X9ZNyhZu68jdDITw==", + "dependencies": { + "System.Linq.AsyncEnumerable": "10.0.0" + } + }, + "System.Linq.AsyncEnumerable": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "t1jnw7So3eeZVemma+82w+Y394oPX1bebkr3gY3IzyotP3kKC/JWwGRz2WssoUFODWWvkACT2hXliumgCHu9LQ==" + }, "System.Reflection.Metadata": { "type": "Transitive", "resolved": "8.0.0", @@ -387,8 +449,8 @@ }, "TinyBDD": { "type": "Transitive", - "resolved": "0.11.0", - "contentHash": "q953tZ+XQP9BHEiLbxa55PxhqMZ7Knb/US+NLYC4+kQ+l4ODih+T1O0Fgl34XinFOI2xKOZgQcYyTnFB+yTBxQ==" + "resolved": "0.12.1", + "contentHash": "pf5G0SU/Gl65OAQoPbZC8tlAOvLM6/WowdmhTVJv8eov8ywgGaQbM7Z3mpF64P+u4x/0HGKYuqcNlimGqoQbTw==" }, "xunit.abstractions": { "type": "Transitive", @@ -428,12 +490,13 @@ "patternkit.examples": { "type": "Project", "dependencies": { - "JetBrains.Annotations": "[2025.2.2, )", - "Microsoft.Extensions.Configuration.Abstractions": "[9.0.9, )", - "Microsoft.Extensions.Configuration.Binder": "[9.0.9, )", - "Microsoft.Extensions.Options": "[9.0.9, )", - "Microsoft.Extensions.Options.ConfigurationExtensions": "[9.0.9, )", - "Microsoft.Extensions.Options.DataAnnotations": "[9.0.9, )", + "JetBrains.Annotations": "[2025.2.4, )", + "Microsoft.Extensions.Configuration.Abstractions": "[10.0.1, )", + "Microsoft.Extensions.Configuration.Binder": "[10.0.1, )", + "Microsoft.Extensions.DependencyInjection": "[10.0.1, )", + "Microsoft.Extensions.Options": "[10.0.1, )", + "Microsoft.Extensions.Options.ConfigurationExtensions": "[10.0.1, )", + "Microsoft.Extensions.Options.DataAnnotations": "[10.0.1, )", "PatternKit.Core": "[1.0.0, )", "PatternKit.Generators": "[1.0.0, )" } @@ -441,7 +504,7 @@ "patternkit.generators": { "type": "Project", "dependencies": { - "System.Collections.Immutable": "[9.0.9, )" + "System.Collections.Immutable": "[10.0.1, )" } } }, @@ -454,33 +517,37 @@ }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.14.1, )", - "resolved": "17.14.1", - "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "requested": "[18.0.1, )", + "resolved": "18.0.1", + "contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==", "dependencies": { - "Microsoft.CodeCoverage": "17.14.1", - "Microsoft.TestPlatform.TestHost": "17.14.1" + "Microsoft.CodeCoverage": "18.0.1", + "Microsoft.TestPlatform.TestHost": "18.0.1" } }, "System.Collections.Immutable": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "/kpkgDxH984e3J3z5v/DIFi+0TWbUJXS8HNKUYBy3YnXtK09JVGs3cw5aOV6fDSw5NxbWLWlGrYjRteu6cjX3w==" + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "kdTe61B8P7i2M1pODC3MLbZ/CfFGjpC6c6jzxjQoB5DHZNewayCRqgFUmx3JKB6vLQtozpMQEiw+R5fO32Jv4g==" }, "System.Linq.Async": { "type": "Direct", - "requested": "[6.0.3, )", - "resolved": "6.0.3", - "contentHash": "hSHiq2m1ky7zUQgTp+/2h1K3lABIQ+GltRixoclHPg/Sc1vnfeS6g/Uy5moOVZKrZJdQiFPFZd6OobBp3tZcFg==" + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "A2Wci92Oyuodi8YLMQCJJ0vHqzgRFgEUG1K6tQNcoxHd3w05B1LvGzXvxQnGYPIL4Cr4hicHytpk2F2Jx8TZHg==", + "dependencies": { + "System.Interactive.Async": "7.0.0", + "System.Linq.AsyncEnumerable": "10.0.0" + } }, "TinyBDD.Xunit": { "type": "Direct", - "requested": "[0.11.0, )", - "resolved": "0.11.0", - "contentHash": "gJu6gSe4sp24+ie+dQ+6bY9SrauQk1UcV02qQsucYcqajwM8B8TCUTwsFjt8ns1xtbOVPRZOlyoWEOBP6NR/Pw==", + "requested": "[0.12.1, )", + "resolved": "0.12.1", + "contentHash": "1V1RAF1OGY7m9kGzhhFpe4NzZO2bd8vSEoL9AlFhEWQ0GIeCCJ/a5Bq4Eqw00n9op/ZHUtb9Retk9XfQSkvKFw==", "dependencies": { - "TinyBDD": "0.11.0", + "TinyBDD": "0.12.1", "xunit.abstractions": "2.0.3", "xunit.extensibility.core": "2.9.3" } @@ -507,90 +574,108 @@ }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[3.1.4, )", - "resolved": "3.1.4", - "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + "requested": "[3.1.5, )", + "resolved": "3.1.5", + "contentHash": "tKi7dSTwP4m5m9eXPM2Ime4Kn7xNf4x4zT9sdLO/G4hZVnQCRiMTWoSZqI/pYTVeI27oPPqHBKYI/DjJ9GsYgA==" }, "JetBrains.Annotations": { "type": "Transitive", - "resolved": "2025.2.2", - "contentHash": "0X56ZRizuHdrnPpgXjWV7f2tQO1FlQg5O1967OGKnI/4ZRNOK642J8L7brM1nYvrxTTU5TP1yRyXLRLaXLPQ8A==" + "resolved": "2025.2.4", + "contentHash": "TwbgxAkXxY+vNEhNVx/QXjJ4vqxmepOjsgRvvImQPbHkHMMb4W+ahL3laMsxXKtNT7iMy+E1B3xkqao2hf1n3A==" }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + "resolved": "18.0.1", + "contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA==" + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "p5RKAY9POvs3axwA/AQRuJeM8AHuE8h4qbP1NxQeGm0ep46aXz1oCLAp/oOYxX1GsjStgdhHrN3XXLLXr0+b3w==", + "resolved": "10.0.1", + "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "6SIp/6Bngk4jm2W36JekZbiIbFPdE/eMUtrJEqIqHGpd1zar3jvgnwxnpWQfzUiGrkyY8q8s6V82zkkEZozghA==", + "resolved": "10.0.1", + "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "/hymojfWbE9AlDOa0mczR44m00Jj+T3+HZO0ZnVTI032fVycI0ZbNOVFP6kqZMcXiLSYXzR2ilcwaRi6dzeGyA==" + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "loxGGHE1FC2AefwPHzrjPq7X92LQm64qnU/whKfo6oWaceewPUVYQJBJs3S3E2qlWwnCpeZ+dGCPTX+5dgVAuQ==", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "n4DCdnn2qs6V5U06Sx62FySEAZsJiJJgOzrPHDh9hPK7c2W8hEabC76F3Re3tGPjpiKa02RvB6FxZyxo8iICzg==", + "resolved": "10.0.1", + "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Configuration.Binder": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.DataAnnotations": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "Al+1FXnKKFygTXz0Zsa1+jYEPvsx5dKavlJxMXjRbrL6lmBhQZsVMhjuNB5lWvdRhdoxt5y/Q3v5kbLZLsXWdA==", + "resolved": "10.0.1", + "contentHash": "gwHO+zpVQGKK9KB3yen82Tff2zdLHHGIJzTut9L8RvoOO2RMSyYZrOmElvxu0lA4ZyaSxy8I0oNw1S/O/vkvFg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "z4pyMePOrl733ltTowbN565PxBw1oAr8IHmIXNDiDqd22nFpYltX9KhrNC/qBWAG1/Zx5MHX+cOYhWJQYCO/iw==" + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==", + "resolved": "18.0.1", + "contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==", "dependencies": { "System.Reflection.Metadata": "8.0.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "resolved": "18.0.1", + "contentHash": "uDJKAEjFTaa2wHdWlfo6ektyoh+WD4/Eesrwb4FpBFKsLGehhACVnwwTI4qD3FrIlIEPlxdXg3SyrYRIcO+RRQ==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Microsoft.TestPlatform.ObjectModel": "18.0.1", "Newtonsoft.Json": "13.0.3" } }, @@ -599,6 +684,19 @@ "resolved": "13.0.3", "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, + "System.Interactive.Async": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Ckj+tg2BVOZ0oLp7FAbjfvRyA/BMkUhVxROLd+x22zncRR6KD7CdFzAYp+9Mo2cedxAMo2X9ZNyhZu68jdDITw==", + "dependencies": { + "System.Linq.AsyncEnumerable": "10.0.0" + } + }, + "System.Linq.AsyncEnumerable": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "t1jnw7So3eeZVemma+82w+Y394oPX1bebkr3gY3IzyotP3kKC/JWwGRz2WssoUFODWWvkACT2hXliumgCHu9LQ==" + }, "System.Reflection.Metadata": { "type": "Transitive", "resolved": "8.0.0", @@ -609,8 +707,8 @@ }, "TinyBDD": { "type": "Transitive", - "resolved": "0.11.0", - "contentHash": "q953tZ+XQP9BHEiLbxa55PxhqMZ7Knb/US+NLYC4+kQ+l4ODih+T1O0Fgl34XinFOI2xKOZgQcYyTnFB+yTBxQ==" + "resolved": "0.12.1", + "contentHash": "pf5G0SU/Gl65OAQoPbZC8tlAOvLM6/WowdmhTVJv8eov8ywgGaQbM7Z3mpF64P+u4x/0HGKYuqcNlimGqoQbTw==" }, "xunit.abstractions": { "type": "Transitive", @@ -650,12 +748,13 @@ "patternkit.examples": { "type": "Project", "dependencies": { - "JetBrains.Annotations": "[2025.2.2, )", - "Microsoft.Extensions.Configuration.Abstractions": "[9.0.9, )", - "Microsoft.Extensions.Configuration.Binder": "[9.0.9, )", - "Microsoft.Extensions.Options": "[9.0.9, )", - "Microsoft.Extensions.Options.ConfigurationExtensions": "[9.0.9, )", - "Microsoft.Extensions.Options.DataAnnotations": "[9.0.9, )", + "JetBrains.Annotations": "[2025.2.4, )", + "Microsoft.Extensions.Configuration.Abstractions": "[10.0.1, )", + "Microsoft.Extensions.Configuration.Binder": "[10.0.1, )", + "Microsoft.Extensions.DependencyInjection": "[10.0.1, )", + "Microsoft.Extensions.Options": "[10.0.1, )", + "Microsoft.Extensions.Options.ConfigurationExtensions": "[10.0.1, )", + "Microsoft.Extensions.Options.DataAnnotations": "[10.0.1, )", "PatternKit.Core": "[1.0.0, )", "PatternKit.Generators": "[1.0.0, )" } @@ -663,7 +762,7 @@ "patternkit.generators": { "type": "Project", "dependencies": { - "System.Collections.Immutable": "[9.0.9, )" + "System.Collections.Immutable": "[10.0.1, )" } } }