Flow Control
Conditions that collect every failure before throwing, a reusable retry policy, and an exponential-backoff waiter with jitter.
Features
Condition: fluent condition aggregator; collects failure messages in order — repeats included — and can throwConditionFailedExceptionor convert to a process output.FailsWiththrowsInvalidOperationExceptionwhen noTrue/Falseprecedes it.ConditionFailedException: exception carrying all collected error messages asstring[].Retry: simple retry with configurable max attempts and fixed delay; supportsAction,Func<T>and the asynchronousExecuteAsyncoverloads, aWhenpredicate to restrict which exceptions are retried, andJitteredBackoffto space attempts with the same exponential-plus-jitter schedule asJitteredWaiter.MaxAttempts(n)runs the operation at mostntimes in total, and a configured instance is reusable.JitteredWaiter: exponential backoff with jitter, capped atmaxWaitMilliseconds(30 s by default);WaitAsyncaccepts aCancellationToken, exposesCanRetryand throwsMaxRetriesReachedExceptionwhen retries are exhausted.MaxRetriesReachedException: exception thrown whenJitteredWaiterexceeds its retry limit.
Class Diagram
classDiagram
namespace FlowControl {
class Condition {
-List~string~ _failedConditions
-bool? _expression
+static Condition Create
+string[] FailedConditions
+bool IsSatisfied
+Condition True(bool expression)
+Condition False(bool expression)
+Condition FailsWith(string error)
+void ThrowIfNotSatisfied()
+ProcessOutput ToProcessOutput()
}
class ConditionFailedException {
+string[] Errors
}
class Retry {
-int _delayMilliseconds
-int _maxAttempts
+static Retry New
+Retry MaxAttempts(int maxAttempts)
+Retry DelayMilliseconds(int delayMilliseconds)
+Retry JitteredBackoff(int maxWaitMilliseconds)
+Retry When(Func~Exception, bool~ predicate)
+Retry When~TException~()
+void Execute(Action action)
+T Execute~T~(Func~T~ func)
+Task ExecuteAsync(Func~CancellationToken, Task~ operation, CancellationToken)
+Task~T~ ExecuteAsync~T~(Func~CancellationToken, Task~T~~ operation, CancellationToken)
}
}
namespace FlowControl_Waiter {
class JitteredWaiter {
-const int FixedWaitDelay
+int MaxRetryCount
-int Count
+bool CanRetry
+Task WaitAsync(CancellationToken)
}
class MaxRetriesReachedException
}
Condition ..> ConditionFailedException : throws
JitteredWaiter ..> MaxRetriesReachedException : throwsUsage
Condition
Chain boolean assertions — all failures are collected before throwing:
using ArturRios.Util.FlowControl;
Condition.Create
.True(user is not null).FailsWith("User is required")
.True(emailValid).FailsWith("Invalid email format")
.False(string.IsNullOrEmpty(username)).FailsWith("Username cannot be empty")
.ThrowIfNotSatisfied();
Convert to a process output instead of throwing:
var result = Condition.Create
.True(age >= 18).FailsWith("Must be 18 or older")
.ToProcessOutput();
if (!result.Success)
Console.WriteLine(string.Join(", ", result.Errors));
Retry
Retry a void action:
using ArturRios.Util.FlowControl;
Retry.New
.MaxAttempts(3)
.DelayMilliseconds(200)
.Execute(() => SendEmail(recipient));
Retry a function that returns a value:
var data = Retry.New
.MaxAttempts(5)
.DelayMilliseconds(500)
.Execute(() => FetchDataFromApi());
JitteredWaiter
Use in a polling or retry loop with exponential backoff:
using ArturRios.Util.FlowControl.Waiter;
var waiter = new JitteredWaiter(maxRetryCount: 5);
while (waiter.CanRetry)
{
try
{
await TryOperationAsync();
break;
}
catch (TransientException)
{
await waiter.WaitAsync();
}
}