diff --git a/.netconfig b/.netconfig
index 59d2a2d..8887b0f 100644
--- a/.netconfig
+++ b/.netconfig
@@ -12,9 +12,9 @@
weak
[file "src/Directory.Build.props"]
url = https://github.com/devlooped/oss/tree/main/src/Directory.Build.props
- sha = 2fff747a9673b499c99f2da183cdd5263fdc9333
+ sha = 81d972fd0760c244d134dae7f4b17d6c43cb004a
- etag = 0fccddf04f282fe98122ab2610dc2972c205a521254559bf013655c6271b0017
+ etag = 1368697c1521e465a1dea88b93787b1c7def441c37d62afc903fb8d07179e4f6
weak
[file "src/Directory.Build.targets"]
url = https://github.com/devlooped/oss/tree/main/src/Directory.Build.targets
@@ -64,3 +64,8 @@
etag = fcb9759a96966df40dcd24906fd328ddec05953b7e747a6bb8d0d1e4c3865274
weak
+[file "src/Smith/Extensions/System/Throw.cs"]
+ url = https://github.com/devlooped/catbag/blob/main/System/Throw.cs
+ sha = 3012d56be7554c483e5c5d277144c063969cada9
+ etag = 43c81c6c6dcdf5baee40a9e3edc5e871e473e6c954c901b82bb87a3a48888ea0
+ weak
diff --git a/Smith.sln b/Smith.sln
new file mode 100644
index 0000000..342cfc6
--- /dev/null
+++ b/Smith.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.14.36221.1 d17.14
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Smith", "src\Smith\Smith.csproj", "{728856C5-A241-6AD6-5CDE-1991FE2F10D7}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {728856C5-A241-6AD6-5CDE-1991FE2F10D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {728856C5-A241-6AD6-5CDE-1991FE2F10D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {728856C5-A241-6AD6-5CDE-1991FE2F10D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {728856C5-A241-6AD6-5CDE-1991FE2F10D7}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Smith.slnx b/Smith.slnx
deleted file mode 100644
index ef93140..0000000
--- a/Smith.slnx
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index b0b9d94..b24450d 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -126,6 +126,8 @@
<_VersionLabel>$(_VersionLabel.Replace('/merge', ''))
<_VersionLabel>$(_VersionLabel.Replace('/', '-'))
+
+ <_VersionLabel>$(_VersionLabel.Replace('_', '-'))
$(_VersionLabel)
diff --git a/src/Directory.props b/src/Directory.props
new file mode 100644
index 0000000..a001624
--- /dev/null
+++ b/src/Directory.props
@@ -0,0 +1,8 @@
+
+
+
+ https://api.nuget.org/v3/index.json;https://pkg.kzu.app/index.json
+ https://github.com/devlooped/smith
+
+
+
\ No newline at end of file
diff --git a/src/Directory.targets b/src/Directory.targets
new file mode 100644
index 0000000..fc71061
--- /dev/null
+++ b/src/Directory.targets
@@ -0,0 +1,37 @@
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Smith.Extensions/Smith.Extensions.csproj b/src/Smith.Extensions/Smith.Extensions.csproj
deleted file mode 100644
index cd03483..0000000
--- a/src/Smith.Extensions/Smith.Extensions.csproj
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
- net10.0
-
-
-
-
-
-
-
diff --git a/src/Smith/AppInitializer.cs b/src/Smith/AppInitializer.cs
new file mode 100644
index 0000000..c9111e9
--- /dev/null
+++ b/src/Smith/AppInitializer.cs
@@ -0,0 +1,25 @@
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Weaving;
+
+class AppInitializer
+{
+#pragma warning disable CA2255 // The 'ModuleInitializer' attribute should not be used in libraries
+ [ModuleInitializer]
+#pragma warning restore CA2255 // The 'ModuleInitializer' attribute should not be used in libraries
+ public static void Init()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ Console.InputEncoding = Console.OutputEncoding = Encoding.UTF8;
+
+ // Load environment variables from .env files in current dir and above.
+ DotNetEnv.Env.TraversePath().Load();
+
+ // Load environment variables from user profile directory.
+ var userEnv = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".env");
+ if (File.Exists(userEnv))
+ DotNetEnv.Env.Load(userEnv);
+ }
+}
diff --git a/src/Smith/Env.cs b/src/Smith/Env.cs
new file mode 100644
index 0000000..4fc8ba8
--- /dev/null
+++ b/src/Smith/Env.cs
@@ -0,0 +1,30 @@
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+
+namespace Weaving;
+
+///
+/// Allows retrieving configuration settings for the app environment using the
+/// default configuration system provided by
+///
+public static class Env
+{
+ static readonly IConfigurationManager configuration =
+ Host.CreateApplicationBuilder(Environment.GetCommandLineArgs()).Configuration;
+
+ ///
+ /// Gets a (possibly null) configuration value for the given key.
+ ///
+ /// The key to retrieve, following .NET configuration system conventions.
+ public static string? Get(string key) => configuration[key];
+
+ ///
+ /// Gets a configuration value for the given key, returning a default value if the key is not found.
+ ///
+ /// The key to retrieve, following .NET configuration system conventions.
+ /// Default value to return if the key is not found.
+ /// Never returns null if is not .
+ [return: NotNullIfNotNull(nameof(defaultValue))]
+ public static string? Get(string key, string defaultValue) => configuration[key] ?? defaultValue;
+}
diff --git a/src/Smith/Extensions/System/Throw.cs b/src/Smith/Extensions/System/Throw.cs
new file mode 100644
index 0000000..eea3e12
--- /dev/null
+++ b/src/Smith/Extensions/System/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/Smith/HostExtensions.cs b/src/Smith/HostExtensions.cs
new file mode 100644
index 0000000..6cc547a
--- /dev/null
+++ b/src/Smith/HostExtensions.cs
@@ -0,0 +1,45 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+namespace Weaving;
+
+///
+/// Extensions for configuring the host application builder and accessing services.
+///
+public static class HostExtensions
+{
+ extension(TBuilder builder) where TBuilder : IHostApplicationBuilder
+ {
+ ///
+ /// Configures the host configuration.
+ ///
+ public TBuilder ConfigureAppConfiguration(Action configure)
+ {
+ configure?.Invoke(builder.Configuration);
+ return builder;
+ }
+
+ ///
+ /// Configures services in the host.
+ ///
+ public TBuilder ConfigureServices(Action configure)
+ {
+ configure?.Invoke(builder.Services);
+ return builder;
+ }
+ }
+
+ extension(IHost app)
+ {
+ ///
+ /// Gest the host app configuration;
+ ///
+ public IConfiguration Configuration => app.Services.GetRequiredService();
+
+ ///
+ /// Gets the default AI client for the app.
+ ///
+ public IChatClient ChatClient => app.Services.GetRequiredService();
+ }
+}
diff --git a/src/Smith/Smith.csproj b/src/Smith/Smith.csproj
new file mode 100644
index 0000000..9de1521
--- /dev/null
+++ b/src/Smith/Smith.csproj
@@ -0,0 +1,24 @@
+
+
+
+ net10.0
+ enable
+ Preview
+ Smith
+ Smith
+ Run AI-powered C# files using Microsoft.Extensions.AI and Devlooped.Extensions.AI
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Smith/Smith.msbuildproj b/src/Smith/Smith.msbuildproj
deleted file mode 100644
index 40e3b46..0000000
--- a/src/Smith/Smith.msbuildproj
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
- net10.0
- Smith
- An opinionated meta-package for doing AI agents using Microsoft.Extensions.AI and MCP
- $(MSBuildThisFileDirectory)bin
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Smith/Smith.props b/src/Smith/Smith.props
index 633c5f0..5336b3b 100644
--- a/src/Smith/Smith.props
+++ b/src/Smith/Smith.props
@@ -1,9 +1,8 @@
-
+
true
enable
true
- TA001;$(NoWarn)
@@ -22,17 +21,15 @@
+
+
+
-
-
-
-
-
-
+
diff --git a/src/Smith/Visibility.cs b/src/Smith/Visibility.cs
new file mode 100644
index 0000000..6a49ba1
--- /dev/null
+++ b/src/Smith/Visibility.cs
@@ -0,0 +1,4 @@
+namespace System
+{
+ public partial class Throw { }
+}
\ No newline at end of file
diff --git a/src/Smith/cs/AddUserSecrets.cs b/src/Smith/cs/AddUserSecrets.cs
deleted file mode 100644
index 9f0fe0b..0000000
--- a/src/Smith/cs/AddUserSecrets.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace Microsoft.Extensions.Configuration;
-
-///
-/// Provides configuration extensions for adding user secrets configuration source.
-///
-public static class UserSecretsConfigurationExtensions
-{
- ///
- ///
- /// Adds the user secrets configuration source with the project-specified user secrets ID.
- ///
- ///
- /// The configuration builder.
- /// The configuration builder.
- public static IConfigurationBuilder AddUserSecrets(this IConfigurationBuilder configuration)
- {
- if (!string.IsNullOrEmpty(ThisAssembly.Project.UserSecretsId))
- return configuration.AddUserSecrets(ThisAssembly.Project.UserSecretsId, reloadOnChange: false);
-
- return configuration;
- }
-}
\ No newline at end of file
diff --git a/src/Smith/readme.md b/src/Smith/readme.md
index 2bf6101..18e2456 100644
--- a/src/Smith/readme.md
+++ b/src/Smith/readme.md
@@ -1,3 +1,50 @@
-
+
+Run AI-powered C# files using Microsoft.Extensions.AI and Devlooped.Extensions.AI
+
+```csharp
+#:package Smith@0.*
+
+// Sample X.AI client usage with .NET
+var messages = new Chat()
+{
+ { "system", "You are a highly intelligent AI assistant." },
+ { "user", "What is 101*3?" },
+};
+
+IChatClient grok = new GrokClient(Throw.IfNullOrEmpty(Env.Get("XAI_API_KEY")))
+ .GetChatClient("grok-3-mini")
+ .AsIChatClient();
+
+var options = new GrokChatOptions
+{
+ ReasoningEffort = ReasoningEffort.High, // or ReasoningEffort.Low
+ Search = GrokSearch.Auto, // or GrokSearch.On/GrokSearch.Off
+};
+
+var response = await grok.GetResponseAsync(messages, options);
+
+AnsiConsole.MarkupLine($":robot: {response.Text}");
+```
+
+> [!NOTE]
+> The most useful namespaces and dependencies for developing Microsoft.Extensions.AI-powered
+> applications are automatically referenced and imported when using this package.
+
+## Configuration / Environment Variables
+
+The `Env` class provides access to the following variables/configuration automatically:
+
+* `.env` files: in local and parent directories
+* `~/.env` file: in the user's home directory (`%userprofile%\.env` on Windows)
+* All default configuration sources from [App Builder](https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host?tabs=appbuilder#host-builder-settings):
+ * Environment variables prefixed with DOTNET_.
+ * Command-line arguments.
+ * appsettings.json.
+ * appsettings.{Environment}.json.
+ * Secret Manager when the app runs in the Development environment.
+ * Environment variables.
+ * Command-line arguments.
+
+
\ No newline at end of file