-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
475 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
using System.ComponentModel; | ||
using Polly.Simmy.Utils; | ||
|
||
namespace Polly.Simmy.Fault; | ||
|
||
#pragma warning disable CA2225 // Operator overloads have named alternates | ||
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters | ||
|
||
/// <summary> | ||
/// A generator for creating faults (exceptions) using registered delegate functions. | ||
/// </summary> | ||
/// <remarks> | ||
/// An instance of this class can be assigned to the <see cref="FaultStrategyOptions.FaultGenerator"/> property. | ||
/// </remarks> | ||
public sealed class FaultGenerator | ||
{ | ||
private const int DefaultWeight = 100; | ||
|
||
private readonly GeneratorHelper<VoidResult> _helper; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="FaultGenerator"/> class. | ||
/// </summary> | ||
public FaultGenerator() | ||
=> _helper = new GeneratorHelper<VoidResult>(RandomUtil.Instance.Next); | ||
|
||
/// <summary> | ||
/// Registers an exception generator delegate. | ||
/// </summary> | ||
/// <param name="generator">The delegate that generates the exception.</param> | ||
/// <param name="weight">The weight assigned to this generator. Defaults to <c>100</c>.</param> | ||
/// <returns>The current instance of <see cref="FaultGenerator"/>.</returns> | ||
public FaultGenerator AddException(Func<Exception> generator, int weight = DefaultWeight) | ||
{ | ||
Guard.NotNull(generator); | ||
|
||
_helper.AddOutcome(_ => Outcome.FromException<VoidResult>(generator()), weight); | ||
|
||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Registers an exception generator delegate that accepts a <see cref="ResilienceContext"/>. | ||
/// </summary> | ||
/// <param name="generator">The delegate that generates the exception, accepting a <see cref="ResilienceContext"/>.</param> | ||
/// <param name="weight">The weight assigned to this generator. Defaults to <c>100</c>.</param> | ||
/// <returns>The current instance of <see cref="FaultGenerator"/>.</returns> | ||
public FaultGenerator AddException(Func<ResilienceContext, Exception> generator, int weight = DefaultWeight) | ||
{ | ||
Guard.NotNull(generator); | ||
|
||
_helper.AddOutcome(context => Outcome.FromException<VoidResult>(generator(context)), weight); | ||
|
||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Registers an exception generator for a specific exception type, using the default constructor of that exception. | ||
/// </summary> | ||
/// <typeparam name="TException">The type of the exception to generate.</typeparam> | ||
/// <param name="weight">The weight assigned to this generator. Defaults to <c>100</c>.</param> | ||
/// <returns>The current instance of <see cref="FaultGenerator"/>.</returns> | ||
public FaultGenerator AddException<TException>(int weight = DefaultWeight) | ||
where TException : Exception, new() | ||
{ | ||
_helper.AddOutcome(_ => Outcome.FromException<VoidResult>(new TException()), weight); | ||
|
||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Provides an implicit conversion from <see cref="FaultGenerator"/> to a delegate compatible with <see cref="FaultStrategyOptions.FaultGenerator"/>. | ||
/// </summary> | ||
/// <param name="generator">The instance of <see cref="FaultGenerator"/>.</param> | ||
[EditorBrowsable(EditorBrowsableState.Never)] | ||
public static implicit operator Func<FaultGeneratorArguments, ValueTask<Exception?>>(FaultGenerator generator) | ||
{ | ||
Guard.NotNull(generator); | ||
|
||
var generatorDelegate = generator._helper.CreateGenerator(); | ||
|
||
return args => new ValueTask<Exception?>(generatorDelegate(args.Context)!.Value.Exception); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
using System.ComponentModel; | ||
using Polly.Simmy.Utils; | ||
|
||
namespace Polly.Simmy.Outcomes; | ||
|
||
#pragma warning disable CA2225 // Operator overloads have named alternates | ||
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters | ||
|
||
/// <summary> | ||
/// Generator that produces faults such as exceptions or results. | ||
/// </summary> | ||
/// <typeparam name="TResult">The type of the result.</typeparam> | ||
/// <remarks> | ||
/// An instance of this class is assignable to <see cref="OutcomeStrategyOptions{TResult}.OutcomeGenerator"/>. | ||
/// </remarks> | ||
public sealed class OutcomeGenerator<TResult> | ||
{ | ||
private const int DefaultWeight = 100; | ||
private readonly GeneratorHelper<TResult> _helper; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="OutcomeGenerator{TResult}"/> class. | ||
/// </summary> | ||
public OutcomeGenerator() | ||
: this(RandomUtil.Instance.Next) | ||
{ | ||
} | ||
|
||
internal OutcomeGenerator(Func<int, int> weightGenerator) | ||
=> _helper = new GeneratorHelper<TResult>(weightGenerator); | ||
|
||
/// <summary> | ||
/// Registers an exception generator delegate. | ||
/// </summary> | ||
/// <param name="generator">The delegate that generates the exception.</param> | ||
/// <param name="weight">The weight assigned to this generator. Defaults to <c>100</c>.</param> | ||
/// <returns>The current instance of <see cref="OutcomeGenerator{TResult}"/>.</returns> | ||
public OutcomeGenerator<TResult> AddException(Func<Exception> generator, int weight = DefaultWeight) | ||
{ | ||
Guard.NotNull(generator); | ||
|
||
_helper.AddOutcome(_ => Outcome.FromException<TResult>(generator()), weight); | ||
|
||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Registers an exception generator delegate that accepts a <see cref="ResilienceContext"/>. | ||
/// </summary> | ||
/// <param name="generator">The delegate that generates the exception, accepting a <see cref="ResilienceContext"/>.</param> | ||
/// <param name="weight">The weight assigned to this generator. Defaults to <c>100</c>.</param> | ||
/// <returns>The current instance of <see cref="OutcomeGenerator{TResult}"/>.</returns> | ||
public OutcomeGenerator<TResult> AddException(Func<ResilienceContext, Exception> generator, int weight = DefaultWeight) | ||
{ | ||
Guard.NotNull(generator); | ||
|
||
_helper.AddOutcome(context => Outcome.FromException<TResult>(generator(context)), weight); | ||
|
||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Registers an exception generator for a specific exception type, using the default constructor of that exception. | ||
/// </summary> | ||
/// <typeparam name="TException">The type of the exception to generate.</typeparam> | ||
/// <param name="weight">The weight assigned to this generator. Defaults to <c>100</c>.</param> | ||
/// <returns>The current instance of <see cref="OutcomeGenerator{TResult}"/>.</returns> | ||
public OutcomeGenerator<TResult> AddException<TException>(int weight = DefaultWeight) | ||
where TException : Exception, new() | ||
{ | ||
_helper.AddOutcome(_ => Outcome.FromException<TResult>(new TException()), weight); | ||
|
||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Registers a result generator. | ||
/// </summary> | ||
/// <param name="generator">The delegate that generates the result.</param> | ||
/// <param name="weight">The weight assigned to this generator. Defaults to <c>100</c>.</param> | ||
/// <returns>The current instance of <see cref="OutcomeGenerator{TResult}"/>.</returns> | ||
public OutcomeGenerator<TResult> AddResult(Func<TResult> generator, int weight = DefaultWeight) | ||
{ | ||
Guard.NotNull(generator); | ||
|
||
_helper.AddOutcome(_ => Outcome.FromResult(generator()), weight); | ||
|
||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Registers a result generator. | ||
/// </summary> | ||
/// <param name="generator">The delegate that generates the result, accepting a <see cref="ResilienceContext"/>.</param> | ||
/// <param name="weight">The weight assigned to this generator. Defaults to <c>100</c>.</param> | ||
/// <returns>The current instance of <see cref="OutcomeGenerator{TResult}"/>.</returns> | ||
public OutcomeGenerator<TResult> AddResult(Func<ResilienceContext, TResult> generator, int weight = DefaultWeight) | ||
{ | ||
Guard.NotNull(generator); | ||
|
||
_helper.AddOutcome(context => Outcome.FromResult(generator(context)), weight); | ||
|
||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Implicit conversion to <see cref="OutcomeStrategyOptions{TResult}.OutcomeGenerator"/>. | ||
/// </summary> | ||
/// <param name="generator">The generator instance.</param> | ||
[EditorBrowsable(EditorBrowsableState.Never)] | ||
public static implicit operator Func<OutcomeGeneratorArguments, ValueTask<Outcome<TResult>?>>(OutcomeGenerator<TResult> generator) | ||
{ | ||
Guard.NotNull(generator); | ||
|
||
var generatorDelegate = generator._helper.CreateGenerator(); | ||
|
||
return args => new ValueTask<Outcome<TResult>?>(generatorDelegate(args.Context)); | ||
} | ||
} | ||
|
24 changes: 0 additions & 24 deletions
24
src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.TResult.cs
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,24 @@ | ||
namespace Polly.Simmy.Outcomes; | ||
using System.ComponentModel.DataAnnotations; | ||
|
||
/// <inheritdoc/> | ||
public class OutcomeStrategyOptions : OutcomeStrategyOptions<object> | ||
namespace Polly.Simmy.Outcomes; | ||
|
||
/// <summary> | ||
/// Represents the options for the Outcome chaos strategy. | ||
/// </summary> | ||
/// <typeparam name="TResult">The type of the outcome that was injected.</typeparam> | ||
public class OutcomeStrategyOptions<TResult> : MonkeyStrategyOptions | ||
{ | ||
/// <summary> | ||
/// Gets or sets the delegate that's invoked when the outcome is injected. | ||
/// </summary> | ||
/// <remarks> | ||
/// Defaults to <see langword="null"/>. | ||
/// </remarks> | ||
public Func<OnOutcomeInjectedArguments<TResult>, ValueTask>? OnOutcomeInjected { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the outcome generator to be injected for a given execution. | ||
/// </summary> | ||
[Required] | ||
public Func<OutcomeGeneratorArguments, ValueTask<Outcome<TResult>?>> OutcomeGenerator { get; set; } = default!; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
namespace Polly.Simmy.Utils; | ||
|
||
internal sealed class GeneratorHelper<TResult> | ||
{ | ||
private readonly Func<int, int> _weightGenerator; | ||
|
||
private readonly List<int> _weights = []; | ||
private readonly List<Func<ResilienceContext, Outcome<TResult>>> _factories = []; | ||
private int _totalWeight; | ||
|
||
public GeneratorHelper(Func<int, int> weightGenerator) => _weightGenerator = weightGenerator; | ||
|
||
public void AddOutcome(Func<ResilienceContext, Outcome<TResult>> generator, int weight) | ||
{ | ||
Guard.NotNull(generator); | ||
|
||
_totalWeight += weight; | ||
_factories.Add(generator); | ||
_weights.Add(weight); | ||
} | ||
|
||
internal Func<ResilienceContext, Outcome<TResult>?> CreateGenerator() | ||
{ | ||
if (_factories.Count == 0) | ||
{ | ||
return _ => null; | ||
} | ||
|
||
var totalWeight = _totalWeight; | ||
var factories = _factories.ToArray(); | ||
var weights = _weights.ToArray(); | ||
var generator = _weightGenerator; | ||
|
||
return context => | ||
{ | ||
var generatedWeight = generator(totalWeight); | ||
var weight = 0; | ||
for (var i = 0; i < factories.Length; i++) | ||
{ | ||
weight += weights[i]; | ||
if (generatedWeight < weight) | ||
{ | ||
return factories[i](context); | ||
} | ||
} | ||
return null; | ||
}; | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.