diff --git a/src/Agents/Extensions/Dedent.cs b/src/Agents/Extensions/Dedent.cs
new file mode 100644
index 0000000..920f671
--- /dev/null
+++ b/src/Agents/Extensions/Dedent.cs
@@ -0,0 +1,130 @@
+//
+#region License
+// MIT License
+//
+// Copyright (c) Daniel Cazzulino
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+#endregion
+
+#nullable enable
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace System
+{
+ ///
+ /// String extension methods for text processing.
+ ///
+ static partial class StringExtensions
+ {
+ ///
+ /// Remove leading whitespace from each line of a multi-line string that is common
+ /// to all non-empty lines. This is equivalent to Python's textwrap.dedent().
+ ///
+ /// The text to dedent.
+ /// The dedented text.
+ ///
+ ///
+ /// var text = """
+ /// Line 1
+ /// Line 2
+ /// Line 3
+ /// """;
+ /// var dedented = text.Dedent();
+ /// // Result:
+ /// // Line 1
+ /// // Line 2
+ /// // Line 3
+ ///
+ ///
+ public static string Dedent(this string text)
+ {
+ if (string.IsNullOrEmpty(text))
+ return text;
+
+ // Detect the line ending style used in the input
+ var lineEnding = Environment.NewLine;
+ if (text.Contains("\r\n"))
+ lineEnding = "\r\n";
+ else if (text.Contains('\r'))
+ lineEnding = "\r";
+ else if (text.Contains('\n'))
+ lineEnding = "\n";
+
+ // Split using regex to properly handle different line endings
+ var lines = NewLineExpr().Split(text);
+
+ // Remove leading and trailing empty lines
+ int start = 0;
+ int end = lines.Length - 1;
+
+ while (start < lines.Length && string.IsNullOrWhiteSpace(lines[start]))
+ start++;
+
+ while (end >= 0 && string.IsNullOrWhiteSpace(lines[end]))
+ end--;
+
+ if (start > end)
+ return string.Empty;
+
+ // Find the minimum indentation (ignoring empty lines)
+ int minIndent = int.MaxValue;
+ for (int i = start; i <= end; i++)
+ {
+ var line = lines[i];
+ if (!string.IsNullOrWhiteSpace(line))
+ {
+ int indent = 0;
+ while (indent < line.Length && char.IsWhiteSpace(line[indent]))
+ indent++;
+
+ minIndent = Math.Min(minIndent, indent);
+ }
+ }
+
+ if (minIndent == int.MaxValue || minIndent == 0)
+ minIndent = 0;
+
+ // Remove the common indentation from all lines
+ var result = new StringBuilder();
+ for (int i = start; i <= end; i++)
+ {
+ var line = lines[i];
+ if (string.IsNullOrWhiteSpace(line))
+ {
+ if (i < end) // Don't add newline for last empty line
+ result.Append(lineEnding);
+ }
+ else
+ {
+ var dedentedLine = line.Length > minIndent ? line[minIndent..] : string.Empty;
+ result.Append(dedentedLine);
+ if (i < end)
+ result.Append(lineEnding);
+ }
+ }
+
+ return result.ToString();
+ }
+
+ [GeneratedRegex(@"\r\n|\r|\n")]
+ private static partial Regex NewLineExpr();
+ }
+}
\ No newline at end of file
diff --git a/src/Agents/Extensions/Throw.cs b/src/Agents/Extensions/Throw.cs
new file mode 100644
index 0000000..eea3e12
--- /dev/null
+++ b/src/Agents/Extensions/Throw.cs
@@ -0,0 +1,992 @@
+//
+#region License
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// Adapted from https://github.com/dotnet/extensions/blob/main/src/Shared/Throw/Throw.cs
+#endregion
+
+#nullable enable
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+
+#pragma warning disable CA1716
+namespace System;
+#pragma warning restore CA1716
+
+///
+/// Defines static methods used to throw exceptions.
+///
+///
+/// The main purpose is to reduce code size, improve performance, and standardize exception
+/// messages.
+///
+[SuppressMessage("Minor Code Smell", "S4136:Method overloads should be grouped together", Justification = "Doesn't work with the region layout")]
+[SuppressMessage("Minor Code Smell", "S2333:Partial is gratuitous in this context", Justification = "Some projects add additional partial parts.")]
+[SuppressMessage("Design", "CA1716", Justification = "Not part of an API")]
+
+#if !SHARED_PROJECT
+[ExcludeFromCodeCoverage]
+#endif
+
+static partial class Throw
+{
+ #region For Object
+
+ ///
+ /// Throws an if the specified argument is .
+ ///
+ /// Argument type to be checked for .
+ /// Object to be checked for .
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [return: NotNull]
+ public static T IfNull([NotNull] T argument, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument is null)
+ {
+ ArgumentNullException(paramName);
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified argument is ,
+ /// or if the specified member is .
+ ///
+ /// Argument type to be checked for .
+ /// Member type to be checked for .
+ /// Argument to be checked for .
+ /// Object member to be checked for .
+ /// The name of the parameter being checked.
+ /// The name of the member.
+ /// The original value of .
+ ///
+ ///
+ /// Throws.IfNullOrMemberNull(myObject, myObject?.MyProperty)
+ ///
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [return: NotNull]
+ public static TMember IfNullOrMemberNull(
+ [NotNull] TParameter argument,
+ [NotNull] TMember member,
+ [CallerArgumentExpression(nameof(argument))] string paramName = "",
+ [CallerArgumentExpression(nameof(member))] string memberName = "")
+ {
+ if (argument is null)
+ {
+ ArgumentNullException(paramName);
+ }
+
+ if (member is null)
+ {
+ ArgumentException(paramName, $"Member {memberName} of {paramName} is null");
+ }
+
+ return member;
+ }
+
+ ///
+ /// Throws an if the specified member is .
+ ///
+ /// Argument type.
+ /// Member type to be checked for .
+ /// Argument to which member belongs.
+ /// Object member to be checked for .
+ /// The name of the parameter being checked.
+ /// The name of the member.
+ /// The original value of .
+ ///
+ ///
+ /// Throws.IfMemberNull(myObject, myObject.MyProperty)
+ ///
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [return: NotNull]
+ [SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Analyzer isn't seeing the reference to 'argument' in the attribute")]
+ public static TMember IfMemberNull(
+ TParameter argument,
+ [NotNull] TMember member,
+ [CallerArgumentExpression(nameof(argument))] string paramName = "",
+ [CallerArgumentExpression(nameof(member))] string memberName = "")
+ where TParameter : notnull
+ {
+ if (member is null)
+ {
+ ArgumentException(paramName, $"Member {memberName} of {paramName} is null");
+ }
+
+ return member;
+ }
+
+ #endregion
+
+ #region For String
+
+ ///
+ /// Throws either an or an
+ /// if the specified string is or whitespace respectively.
+ ///
+ /// String to be checked for or whitespace.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [return: NotNull]
+ public static string IfNullOrWhitespace([NotNull] string? argument, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+#if !NETCOREAPP3_1_OR_GREATER
+ if (argument == null)
+ {
+ ArgumentNullException(paramName);
+ }
+#endif
+
+ if (string.IsNullOrWhiteSpace(argument))
+ {
+ if (argument == null)
+ {
+ ArgumentNullException(paramName);
+ }
+ else
+ {
+ ArgumentException(paramName, "Argument is whitespace");
+ }
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the string is ,
+ /// or if it is empty.
+ ///
+ /// String to be checked for or empty.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [return: NotNull]
+ public static string IfNullOrEmpty([NotNull] string? argument, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+#if !NETCOREAPP3_1_OR_GREATER
+ if (argument == null)
+ {
+ ArgumentNullException(paramName);
+ }
+#endif
+
+ if (string.IsNullOrEmpty(argument))
+ {
+ if (argument == null)
+ {
+ ArgumentNullException(paramName);
+ }
+ else
+ {
+ ArgumentException(paramName, "Argument is an empty string");
+ }
+ }
+
+ return argument;
+ }
+
+ #endregion
+
+ #region For Buffer
+
+ ///
+ /// Throws an if the argument's buffer size is less than the required buffer size.
+ ///
+ /// The actual buffer size.
+ /// The required buffer size.
+ /// The name of the parameter to be checked.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void IfBufferTooSmall(int bufferSize, int requiredSize, string paramName = "")
+ {
+ if (bufferSize < requiredSize)
+ {
+ ArgumentException(paramName, $"Buffer too small, needed a size of {requiredSize} but got {bufferSize}");
+ }
+ }
+
+ #endregion
+
+ #region For Enums
+
+ ///
+ /// Throws an if the enum value is not valid.
+ ///
+ /// The argument to evaluate.
+ /// The name of the parameter being checked.
+ /// The type of the enumeration.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T IfOutOfRange(T argument, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ where T : struct, Enum
+ {
+#if NET5_0_OR_GREATER
+ if (!Enum.IsDefined(argument))
+#else
+ if (!Enum.IsDefined(typeof(T), argument))
+#endif
+ {
+ ArgumentOutOfRangeException(paramName, $"{argument} is an invalid value for enum type {typeof(T)}");
+ }
+
+ return argument;
+ }
+
+ #endregion
+
+ #region For Collections
+
+ ///
+ /// Throws an if the collection is ,
+ /// or if it is empty.
+ ///
+ /// The collection to evaluate.
+ /// The name of the parameter being checked.
+ /// The type of objects in the collection.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [return: NotNull]
+
+ // The method has actually 100% coverage, but due to a bug in the code coverage tool,
+ // a lower number is reported. Therefore, we temporarily exclude this method
+ // from the coverage measurements. Once the bug in the code coverage tool is fixed,
+ // the exclusion attribute can be removed.
+ [ExcludeFromCodeCoverage]
+ public static IEnumerable IfNullOrEmpty([NotNull] IEnumerable? argument, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument == null)
+ {
+ ArgumentNullException(paramName);
+ }
+ else
+ {
+ switch (argument)
+ {
+ case ICollection collection:
+ if (collection.Count == 0)
+ {
+ ArgumentException(paramName, "Collection is empty");
+ }
+
+ break;
+ case IReadOnlyCollection readOnlyCollection:
+ if (readOnlyCollection.Count == 0)
+ {
+ ArgumentException(paramName, "Collection is empty");
+ }
+
+ break;
+ default:
+ using (IEnumerator enumerator = argument.GetEnumerator())
+ {
+ if (!enumerator.MoveNext())
+ {
+ ArgumentException(paramName, "Collection is empty");
+ }
+ }
+
+ break;
+ }
+ }
+
+ return argument;
+ }
+
+ #endregion
+
+ #region Exceptions
+
+ ///
+ /// Throws an .
+ ///
+ /// The name of the parameter that caused the exception.
+#if !NET6_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ public static void ArgumentNullException(string paramName)
+ => throw new ArgumentNullException(paramName);
+
+ ///
+ /// Throws an .
+ ///
+ /// The name of the parameter that caused the exception.
+ /// A message that describes the error.
+#if !NET6_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ public static void ArgumentNullException(string paramName, string? message)
+ => throw new ArgumentNullException(paramName, message);
+
+ ///
+ /// Throws an .
+ ///
+ /// The name of the parameter that caused the exception.
+#if !NET6_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ public static void ArgumentOutOfRangeException(string paramName)
+ => throw new ArgumentOutOfRangeException(paramName);
+
+ ///
+ /// Throws an .
+ ///
+ /// The name of the parameter that caused the exception.
+ /// A message that describes the error.
+#if !NET6_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ public static void ArgumentOutOfRangeException(string paramName, string? message)
+ => throw new ArgumentOutOfRangeException(paramName, message);
+
+ ///
+ /// Throws an .
+ ///
+ /// The name of the parameter that caused the exception.
+ /// The value of the argument that caused this exception.
+ /// A message that describes the error.
+#if !NET6_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ public static void ArgumentOutOfRangeException(string paramName, object? actualValue, string? message)
+ => throw new ArgumentOutOfRangeException(paramName, actualValue, message);
+
+ ///
+ /// Throws an .
+ ///
+ /// The name of the parameter that caused the exception.
+ /// A message that describes the error.
+#if !NET6_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ public static void ArgumentException(string paramName, string? message)
+ => throw new ArgumentException(message, paramName);
+
+ ///
+ /// Throws an .
+ ///
+ /// The name of the parameter that caused the exception.
+ /// A message that describes the error.
+ /// The exception that is the cause of the current exception.
+ ///
+ /// If the is not a , the current exception is raised in a catch
+ /// block that handles the inner exception.
+ ///
+#if !NET6_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ public static void ArgumentException(string paramName, string? message, Exception? innerException)
+ => throw new ArgumentException(message, paramName, innerException);
+
+ ///
+ /// Throws an .
+ ///
+ /// A message that describes the error.
+#if !NET6_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ public static void InvalidOperationException(string message)
+ => throw new InvalidOperationException(message);
+
+ ///
+ /// Throws an .
+ ///
+ /// A message that describes the error.
+ /// The exception that is the cause of the current exception.
+#if !NET6_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ public static void InvalidOperationException(string message, Exception? innerException)
+ => throw new InvalidOperationException(message, innerException);
+
+ #endregion
+
+ #region For Integer
+
+ ///
+ /// Throws an if the specified number is less than min.
+ ///
+ /// Number to be expected being less than min.
+ /// The number that must be less than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int IfLessThan(int argument, int min, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument < min)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument less than minimum value {min}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is greater than max.
+ ///
+ /// Number to be expected being greater than max.
+ /// The number that must be greater than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int IfGreaterThan(int argument, int max, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument > max)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument greater than maximum value {max}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is less or equal than min.
+ ///
+ /// Number to be expected being less or equal than min.
+ /// The number that must be less or equal than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int IfLessThanOrEqual(int argument, int min, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument <= min)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument less or equal than minimum value {min}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is greater or equal than max.
+ ///
+ /// Number to be expected being greater or equal than max.
+ /// The number that must be greater or equal than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int IfGreaterThanOrEqual(int argument, int max, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument >= max)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument greater or equal than maximum value {max}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is not in the specified range.
+ ///
+ /// Number to be expected being greater or equal than max.
+ /// The lower bound of the allowed range of argument values.
+ /// The upper bound of the allowed range of argument values.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int IfOutOfRange(int argument, int min, int max, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument < min || argument > max)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument not in the range [{min}..{max}]");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is equal to 0.
+ ///
+ /// Number to be expected being not equal to zero.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int IfZero(int argument, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument == 0)
+ {
+ ArgumentOutOfRangeException(paramName, "Argument is zero");
+ }
+
+ return argument;
+ }
+
+ #endregion
+
+ #region For Unsigned Integer
+
+ ///
+ /// Throws an if the specified number is less than min.
+ ///
+ /// Number to be expected being less than min.
+ /// The number that must be less than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint IfLessThan(uint argument, uint min, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument < min)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument less than minimum value {min}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is greater than max.
+ ///
+ /// Number to be expected being greater than max.
+ /// The number that must be greater than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint IfGreaterThan(uint argument, uint max, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument > max)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument greater than maximum value {max}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is less or equal than min.
+ ///
+ /// Number to be expected being less or equal than min.
+ /// The number that must be less or equal than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint IfLessThanOrEqual(uint argument, uint min, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument <= min)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument less or equal than minimum value {min}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is greater or equal than max.
+ ///
+ /// Number to be expected being greater or equal than max.
+ /// The number that must be greater or equal than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint IfGreaterThanOrEqual(uint argument, uint max, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument >= max)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument greater or equal than maximum value {max}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is not in the specified range.
+ ///
+ /// Number to be expected being greater or equal than max.
+ /// The lower bound of the allowed range of argument values.
+ /// The upper bound of the allowed range of argument values.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint IfOutOfRange(uint argument, uint min, uint max, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument < min || argument > max)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument not in the range [{min}..{max}]");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is equal to 0.
+ ///
+ /// Number to be expected being not equal to zero.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint IfZero(uint argument, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument == 0U)
+ {
+ ArgumentOutOfRangeException(paramName, "Argument is zero");
+ }
+
+ return argument;
+ }
+
+ #endregion
+
+ #region For Long
+
+ ///
+ /// Throws an if the specified number is less than min.
+ ///
+ /// Number to be expected being less than min.
+ /// The number that must be less than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static long IfLessThan(long argument, long min, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument < min)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument less than minimum value {min}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is greater than max.
+ ///
+ /// Number to be expected being greater than max.
+ /// The number that must be greater than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static long IfGreaterThan(long argument, long max, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument > max)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument greater than maximum value {max}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is less or equal than min.
+ ///
+ /// Number to be expected being less or equal than min.
+ /// The number that must be less or equal than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static long IfLessThanOrEqual(long argument, long min, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument <= min)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument less or equal than minimum value {min}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is greater or equal than max.
+ ///
+ /// Number to be expected being greater or equal than max.
+ /// The number that must be greater or equal than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static long IfGreaterThanOrEqual(long argument, long max, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument >= max)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument greater or equal than maximum value {max}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is not in the specified range.
+ ///
+ /// Number to be expected being greater or equal than max.
+ /// The lower bound of the allowed range of argument values.
+ /// The upper bound of the allowed range of argument values.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static long IfOutOfRange(long argument, long min, long max, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument < min || argument > max)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument not in the range [{min}..{max}]");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is equal to 0.
+ ///
+ /// Number to be expected being not equal to zero.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static long IfZero(long argument, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument == 0L)
+ {
+ ArgumentOutOfRangeException(paramName, "Argument is zero");
+ }
+
+ return argument;
+ }
+
+ #endregion
+
+ #region For Unsigned Long
+
+ ///
+ /// Throws an if the specified number is less than min.
+ ///
+ /// Number to be expected being less than min.
+ /// The number that must be less than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong IfLessThan(ulong argument, ulong min, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument < min)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument less than minimum value {min}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is greater than max.
+ ///
+ /// Number to be expected being greater than max.
+ /// The number that must be greater than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong IfGreaterThan(ulong argument, ulong max, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument > max)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument greater than maximum value {max}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is less or equal than min.
+ ///
+ /// Number to be expected being less or equal than min.
+ /// The number that must be less or equal than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong IfLessThanOrEqual(ulong argument, ulong min, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument <= min)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument less or equal than minimum value {min}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is greater or equal than max.
+ ///
+ /// Number to be expected being greater or equal than max.
+ /// The number that must be greater or equal than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong IfGreaterThanOrEqual(ulong argument, ulong max, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument >= max)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument greater or equal than maximum value {max}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is not in the specified range.
+ ///
+ /// Number to be expected being greater or equal than max.
+ /// The lower bound of the allowed range of argument values.
+ /// The upper bound of the allowed range of argument values.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong IfOutOfRange(ulong argument, ulong min, ulong max, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument < min || argument > max)
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument not in the range [{min}..{max}]");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is equal to 0.
+ ///
+ /// Number to be expected being not equal to zero.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong IfZero(ulong argument, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ if (argument == 0UL)
+ {
+ ArgumentOutOfRangeException(paramName, "Argument is zero");
+ }
+
+ return argument;
+ }
+
+ #endregion
+
+ #region For Double
+
+ ///
+ /// Throws an if the specified number is less than min.
+ ///
+ /// Number to be expected being less than min.
+ /// The number that must be less than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double IfLessThan(double argument, double min, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ // strange conditional needed in order to handle NaN values correctly
+#pragma warning disable S1940 // Boolean checks should not be inverted
+ if (!(argument >= min))
+#pragma warning restore S1940 // Boolean checks should not be inverted
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument less than minimum value {min}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is greater than max.
+ ///
+ /// Number to be expected being greater than max.
+ /// The number that must be greater than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double IfGreaterThan(double argument, double max, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ // strange conditional needed in order to handle NaN values correctly
+#pragma warning disable S1940 // Boolean checks should not be inverted
+ if (!(argument <= max))
+#pragma warning restore S1940 // Boolean checks should not be inverted
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument greater than maximum value {max}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is less or equal than min.
+ ///
+ /// Number to be expected being less or equal than min.
+ /// The number that must be less or equal than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double IfLessThanOrEqual(double argument, double min, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ // strange conditional needed in order to handle NaN values correctly
+#pragma warning disable S1940 // Boolean checks should not be inverted
+ if (!(argument > min))
+#pragma warning restore S1940 // Boolean checks should not be inverted
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument less or equal than minimum value {min}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is greater or equal than max.
+ ///
+ /// Number to be expected being greater or equal than max.
+ /// The number that must be greater or equal than the argument.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double IfGreaterThanOrEqual(double argument, double max, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ // strange conditional needed in order to handle NaN values correctly
+#pragma warning disable S1940 // Boolean checks should not be inverted
+ if (!(argument < max))
+#pragma warning restore S1940 // Boolean checks should not be inverted
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument greater or equal than maximum value {max}");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is not in the specified range.
+ ///
+ /// Number to be expected being greater or equal than max.
+ /// The lower bound of the allowed range of argument values.
+ /// The upper bound of the allowed range of argument values.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double IfOutOfRange(double argument, double min, double max, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+ // strange conditional needed in order to handle NaN values correctly
+ if (!(min <= argument && argument <= max))
+ {
+ ArgumentOutOfRangeException(paramName, argument, $"Argument not in the range [{min}..{max}]");
+ }
+
+ return argument;
+ }
+
+ ///
+ /// Throws an if the specified number is equal to 0.
+ ///
+ /// Number to be expected being not equal to zero.
+ /// The name of the parameter being checked.
+ /// The original value of .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double IfZero(double argument, [CallerArgumentExpression(nameof(argument))] string paramName = "")
+ {
+#pragma warning disable S1244 // Floating point numbers should not be tested for equality
+ if (argument == 0.0)
+#pragma warning restore S1244 // Floating point numbers should not be tested for equality
+ {
+ ArgumentOutOfRangeException(paramName, "Argument is zero");
+ }
+
+ return argument;
+ }
+
+ #endregion
+}
diff --git a/src/Agents/Extensions/YamlConfigurationStreamParser.cs b/src/Agents/Extensions/YamlConfigurationStreamParser.cs
new file mode 100644
index 0000000..38d262a
--- /dev/null
+++ b/src/Agents/Extensions/YamlConfigurationStreamParser.cs
@@ -0,0 +1,132 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Microsoft.Extensions.Configuration;
+using YamlDotNet.RepresentationModel;
+
+namespace NetEscapades.Configuration.Yaml
+{
+ internal class YamlConfigurationStreamParser
+ {
+ private readonly IDictionary _data = new SortedDictionary(StringComparer.OrdinalIgnoreCase);
+ private readonly Stack _context = new Stack();
+ private string _currentPath;
+
+ public IDictionary Parse(Stream input)
+ {
+ _data.Clear();
+ _context.Clear();
+
+ // https://dotnetfiddle.net/rrR2Bb
+ var yaml = new YamlStream();
+ yaml.Load(new StreamReader(input, detectEncodingFromByteOrderMarks: true));
+
+ if (yaml.Documents.Any())
+ {
+ var mapping = (YamlMappingNode)yaml.Documents[0].RootNode;
+
+ // The document node is a mapping node
+ VisitYamlMappingNode(mapping);
+ }
+
+ return _data;
+ }
+
+ private void VisitYamlNodePair(KeyValuePair yamlNodePair)
+ {
+ var context = ((YamlScalarNode)yamlNodePair.Key).Value;
+ VisitYamlNode(context, yamlNodePair.Value);
+ }
+
+ private void VisitYamlNode(string context, YamlNode node)
+ {
+ if (node is YamlScalarNode scalarNode)
+ {
+ VisitYamlScalarNode(context, scalarNode);
+ }
+ if (node is YamlMappingNode mappingNode)
+ {
+ VisitYamlMappingNode(context, mappingNode);
+ }
+ if (node is YamlSequenceNode sequenceNode)
+ {
+ VisitYamlSequenceNode(context, sequenceNode);
+ }
+ }
+
+ private void VisitYamlScalarNode(string context, YamlScalarNode yamlValue)
+ {
+ //a node with a single 1-1 mapping
+ EnterContext(context);
+ var currentKey = _currentPath;
+
+ if (_data.ContainsKey(currentKey))
+ {
+ throw new FormatException(Resources.FormatError_KeyIsDuplicated(currentKey));
+ }
+
+ _data[currentKey] = IsNullValue(yamlValue) ? null : yamlValue.Value;
+ ExitContext();
+ }
+
+ private void VisitYamlMappingNode(YamlMappingNode node)
+ {
+ foreach (var yamlNodePair in node.Children)
+ {
+ VisitYamlNodePair(yamlNodePair);
+ }
+ }
+
+ private void VisitYamlMappingNode(string context, YamlMappingNode yamlValue)
+ {
+ //a node with an associated sub-document
+ EnterContext(context);
+
+ VisitYamlMappingNode(yamlValue);
+
+ ExitContext();
+ }
+
+ private void VisitYamlSequenceNode(string context, YamlSequenceNode yamlValue)
+ {
+ //a node with an associated list
+ EnterContext(context);
+
+ VisitYamlSequenceNode(yamlValue);
+
+ ExitContext();
+ }
+
+ private void VisitYamlSequenceNode(YamlSequenceNode node)
+ {
+ for (int i = 0; i < node.Children.Count; i++)
+ {
+ VisitYamlNode(i.ToString(), node.Children[i]);
+ }
+ }
+
+ private void EnterContext(string context)
+ {
+ _context.Push(context);
+ _currentPath = ConfigurationPath.Combine(_context.Reverse());
+ }
+
+ private void ExitContext()
+ {
+ _context.Pop();
+ _currentPath = ConfigurationPath.Combine(_context.Reverse());
+ }
+
+ private bool IsNullValue(YamlScalarNode yamlValue)
+ {
+ return yamlValue.Style == YamlDotNet.Core.ScalarStyle.Plain
+ && (
+ yamlValue.Value == "~"
+ || yamlValue.Value == "null"
+ || yamlValue.Value == "Null"
+ || yamlValue.Value == "NULL"
+ );
+ }
+ }
+}